def _export(cls, _format): """ Export current file in out format :flavor: pdf or html :returns: return code """ f = _format if _format == 'pdf' else 'html' emacs = os.path.expandvars(os.path.expanduser( \ settings.get(u'org_export_emacs', u'/usr/bin/emacs'))) if os.path.exists(emacs): cmd = [emacs, u'-nw', u'--batch', u'--visit=%s' \ % (vim.eval(u'expand("%:p")'), ), \ u'--funcall=org-export-as-%s' % f] # source init script as well init_script = cls._get_init_script() if init_script: cmd.extend(['--script', init_script]) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, \ stderr=subprocess.PIPE) p.wait() if p.returncode != 0 or settings.get(u'org_export_verbose') == 1: echom('\n'.join(p.communicate())) return p.returncode else: echoe(u'Unable to find emacs binary %s' % emacs)
def create(self): from orgmode import ORGMODE, echom cmd = self._mode if cmd == MODE_ALL: cmd = u'' if not self._remap: cmd += u'nore' try: create_mapping = True if isinstance(self._action, Plug): # create plug self._action.create() if int( vim.eval((u'hasmapto("%s")' % (self._action, )).encode(u'utf-8'))): create_mapping = False if isinstance(self._action, Command): # create command self._action.create() if create_mapping: vim.command((u':%smap %s %s %s' % (cmd, u' '.join( self._options), self._key, self._action)).encode(u'utf-8')) except Exception, e: if ORGMODE.debug: echom(u'Failed to register key binding %s %s' % (self._key, self._action))
def follow(cls, action='openLink', visual=''): """ Follow hyperlink. If called on a regular string UTL determines the outcome. Normally a file with that name will be opened. :action: "copy" if the link should be copied to clipboard, otherwise the link will be opened :visual: "visual" if Universal Text Linking should be triggered in visual mode :returns: URI or None """ if not int(vim.eval('exists(":Utl")')): echom( 'Universal Text Linking plugin not installed, unable to proceed.' ) return action = 'copyLink' if action and action.startswith( 'copy') else 'openLink' visual = 'visual' if visual and visual.startswith('visual') else '' link = Hyperlinks._get_link() if link and link['uri'] != None: # call UTL with the URI vim.command('Utl %s %s %s' % (action, visual, link['uri'])) return link['uri'] else: # call UTL and let it decide what to do vim.command('Utl %s %s' % (action, visual))
def follow(cls, action=u'openLink', visual=u''): u""" Follow hyperlink. If called on a regular string UTL determines the outcome. Normally a file with that name will be opened. :action: "copy" if the link should be copied to clipboard, otherwise the link will be opened :visual: "visual" if Universal Text Linking should be triggered in visual mode :returns: URI or None """ if not int(vim.eval(u'exists(":Utl")')): echom(u'Universal Text Linking plugin not installed, unable to proceed.') return action = u'copyLink' if action and action.startswith(u'copy') else u'openLink' visual = u'visual' if visual and visual.startswith(u'visual') else u'' link = Hyperlinks._get_link() if link and link[u'uri'] is not None: # call UTL with the URI vim.command((u'Utl %s %s %s' % (action, visual, link[u'uri'])).encode(u'utf-8')) return link[u'uri'] else: # call UTL and let it decide what to do vim.command((u'Utl %s %s' % (action, visual)).encode(u'utf-8'))
def __init__(self): u""" Initialize plugin """ object.__init__(self) # menu entries this plugin should create self.menu = ORGMODE.orgmenu + Submenu(u'Dates and Scheduling') # key bindings for this plugin # key bindings are also registered through the menu so only additional # bindings should be put in this variable self.keybindings = [] # commands for this plugin self.commands = [] # set speeddating format that is compatible with orgmode try: if int(vim.eval(u'exists(":SpeedDatingFormat")'.encode(u'utf-8'))): vim.command( u':1SpeedDatingFormat %Y-%m-%d %a'.encode(u'utf-8')) vim.command( u':1SpeedDatingFormat %Y-%m-%d %a %H:%M'.encode(u'utf-8')) else: echom(u'Speeddating plugin not installed. Please install it.') except: echom(u'Speeddating plugin not installed. Please install it.')
def topdf(cls): u""" Export the current buffer as pdf using emacs orgmode. """ ret = cls._export(u'pdf') if ret != 0: echoe(u'PDF export failed.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
def tohtml(cls): u""" Export the current buffer as html using emacs orgmode. """ ret = cls._export(u'html') if ret != 0: echoe(u'HTML export failed.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'html'))
def toggle_todo_state(cls, direction=Direction.FORWARD, interactive=False, next_set=False): u""" Toggle state of TODO item :returns: The changed heading """ d = ORGMODE.get_document(allow_dirty=True) # get heading heading = d.find_current_heading() if not heading: vim.eval(u'feedkeys("^", "n")') return todo_states = d.get_todo_states(strip_access_key=False) # get todo states if not todo_states: echom(u'No todo keywords configured.') return current_state = heading.todo # get new state interactively if interactive: # determine position of the interactive prompt prompt_pos = settings.get(u'org_todo_prompt_position', u'botright') if not prompt_pos in [u'botright', u'topleft']: prompt_pos = u'botright' # pass todo states to new window ORGTODOSTATES[d.bufnr] = todo_states settings.set(u'org_current_state_%d' % d.bufnr, \ current_state if current_state is not None else u'', overwrite=True) todo_buffer_exists = bool(int(vim.eval((u'bufexists("org:todo/%d")' % (d.bufnr, )).encode(u'utf-8')))) if todo_buffer_exists: # if the buffer already exists, reuse it vim.command((u'%s sbuffer org:todo/%d' % (prompt_pos, d.bufnr, )).encode(u'utf-8')) else: # create a new window vim.command((u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr)).encode(u'utf-8')) else: new_state = Todo._get_next_state(current_state, todo_states, direction=direction, interactive=interactive, next_set=next_set) cls.set_todo_state(new_state) # plug plug = u'OrgTodoForward' if direction == Direction.BACKWARD: plug = u'OrgTodoBackward' return plug
def toggle_todo_state(cls, direction=DIRECTION_FORWARD, interactive=False, next_set=False): u""" Toggle state of TODO item :returns: The changed heading """ d = ORGMODE.get_document(allow_dirty=True) lineno, colno = vim.current.window.cursor # get heading heading = d.find_current_heading() if not heading: vim.eval(u'feedkeys("^", "n")') return todo_states = d.get_todo_states(strip_access_key=False) # get todo states if not todo_states: echom(u'No todo keywords configured.') return # current_state current_state = heading.todo # get new state new_state = Todo._get_next_state(current_state, todo_states, \ direction=direction, interactive=interactive, next_set=next_set) # move cursor along with the inserted state only when current position # is in the heading; otherwite do nothing if heading.start_vim == lineno: if current_state is None and new_state is None: offset = 0 elif current_state is None: offset = len(new_state) elif new_state is None: offset = -len(current_state) else: offset = len(current_state) - len(new_state) vim.current.window.cursor = (lineno, colno + offset) # set new headline heading.todo = new_state # plug plug = u'OrgTodoForward' if direction == DIRECTION_BACKWARD: plug = u'OrgTodoBackward' d.write_heading(heading) return plug
def toggle_todo_state(cls, direction=DIRECTION_FORWARD): """ Toggle state of TODO item :returns: The changed heading """ lineno, colno = vim.current.window.cursor # get heading heading = Document.current_heading() if not heading: vim.eval('feedkeys("^", "n")') return # get todo states todo_states, done_states = Todo._get_states() all_states = todo_states + done_states if len(all_states) < 2: echom('No todo keywords configured.') return # current_state and rest of heading current_state, rest = Todo._split_heading(heading.text, all_states) # get new state new_state = Todo._get_next_state(current_state, all_states, direction) # set new headline if not new_state: new_heading = ' '.join(('*' * heading.level, rest)) else: new_heading = ' '.join(('*' * heading.level, new_state, rest)) vim.current.buffer[heading.start] = new_heading # move cursor along with the inserted state only when current position # is in the heading; otherwite do nothing if heading.start_vim == lineno: if current_state is None: offset = len(new_state) elif new_state is None: offset = -len(current_state) else: offset = len(current_state) - len(new_state) vim.current.window.cursor = (lineno, colno + offset) # plug plug = 'OrgToggleTodoForward' if direction == DIRECTION_BACKWARD: plug = 'OrgToggleTodoBackward' return plug
def init_org_todo(cls): u""" Initialize org todo selection window. """ bufnr = int(vim.current.buffer.name.split('/')[-1]) all_states = ORGTODOSTATES.get(bufnr, None) # because timeoutlen can only be set globally it needs to be stored and restored later vim.command(u'let g:org_sav_timeoutlen=&timeoutlen'.encode(u'utf-8')) vim.command(u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif'.encode(u'utf-8')) vim.command(u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif'.encode(u'utf-8')) # make window a scratch window and set the statusline differently vim.command(u'setlocal tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight'.encode(u'utf-8')) vim.command((u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval((u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr).encode(u'utf-8'))).encode(u'utf-8')) vim.command((u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % (vim.eval(u'bufnr("%")'.encode(u'utf-8')), )).encode(u'utf-8')) vim.command(u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "py ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>'.encode(u'utf-8')) if all_states is None: vim.command(u'bw'.encode(u'utf-8')) echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name) for l in xrange(0, len(all_states)): res = u'' did_done = False for j in xrange(0, 2): if j < len(all_states[l]): for i in all_states[l][j]: if type(i) != unicode: continue v, k = split_access_key(i) if k: res += (u'\t' if res else u'') + u'[%s] %s' % (k, v) # map access keys to callback that updates current heading # map selection keys vim.command((u'nnoremap <silent> <buffer> %s :bw<Bar>py ORGMODE.plugins[u"Todo"].set_todo_state("%s".decode(u"utf-8"))<CR>' % (k, v)).encode(u'utf-8') ) elif v: res += (u'\t' if res else u'') + v if res: if l == 0: vim.current.buffer[0] = res.encode(u'utf-8') else: vim.current.buffer.append(res.encode(u'utf-8')) # finally make buffer non modifiable vim.command(u'setfiletype orgtodo'.encode(u'utf-8')) vim.command(u'setlocal nomodifiable'.encode(u'utf-8')) # remove temporary todo states for the current buffer del ORGTODOSTATES[bufnr]
def toggle_todo_state(cls, direction=DIRECTION_FORWARD, interactive=False, next_set=False): u""" Toggle state of TODO item :returns: The changed heading """ d = ORGMODE.get_document(allow_dirty=True) # get heading heading = d.find_current_heading() if not heading: vim.eval(u'feedkeys("^", "n")') return todo_states = d.get_todo_states(strip_access_key=False) # get todo states if not todo_states: echom(u'No todo keywords configured.') return current_state = heading.todo # get new state interactively if interactive: # pass todo states to new window ORGTODOSTATES[d.bufnr] = todo_states if bool(int(vim.eval(( u'bufexists("org:todo/%d")' % (d.bufnr, ) ).encode(u'utf-8')))): # if the buffer already exists, reuse it vim.command((u'sbuffer org:todo/%d' % (d.bufnr, )).encode(u'utf-8')) else: # create a new window vim.command((u'keepalt %dsp org:todo/%d' % (len(todo_states), d.bufnr)).encode(u'utf-8')) else: new_state = Todo._get_next_state(current_state, todo_states, \ direction=direction, interactive=interactive, next_set=next_set) cls.set_todo_state(new_state) # plug plug = u'OrgTodoForward' if direction == DIRECTION_BACKWARD: plug = u'OrgTodoBackward' return plug
def insert_timestamp(cls, active=True): """ Insert a timestamp (today) at the cursor position. TODO: show fancy calendar to pick the date from. """ today = date.today() msg = ''.join(['Insert Date: ', today.strftime('%Y-%m-%d %a'), ' | Change date']) modifier = get_user_input(msg) echom(modifier) newdate = cls._modify_time(today, modifier) # format if isinstance(newdate, datetime): newdate = newdate.strftime('%Y-%m-%d %a %H:%M') else: newdate = newdate.strftime('%Y-%m-%d %a') timestamp = '<%s>' % newdate if active else '[%s]' % newdate insert_at_cursor(timestamp)
def __init__(self): u""" Initialize plugin """ object.__init__(self) # menu entries this plugin should create self.menu = ORGMODE.orgmenu + Submenu(u"Dates and Scheduling") # key bindings for this plugin # key bindings are also registered through the menu so only additional # bindings should be put in this variable self.keybindings = [] # commands for this plugin self.commands = [] # set speeddating format that is compatible with orgmode try: if int(vim.eval(u'exists(":SpeedDatingFormat")'.encode(u"utf-8"))) == 2: vim.command(u":1SpeedDatingFormat %Y-%m-%d %a".encode(u"utf-8")) vim.command(u":1SpeedDatingFormat %Y-%m-%d %a %H:%M".encode(u"utf-8")) else: echom(u"Speeddating plugin not installed. Please install it.") except: echom(u"Speeddating plugin not installed. Please install it.")
def insert_timestamp(cls, active=True): """ Insert a timestamp (today) at the cursor position. TODO: show fancy calendar to pick the date from. """ today = date.today() msg = ''.join( ['Insert Date: ', today.strftime('%Y-%m-%d %a'), ' | Change date']) modifier = get_user_input(msg) echom(modifier) newdate = cls._modify_time(today, modifier) # format if isinstance(newdate, datetime): newdate = newdate.strftime('%Y-%m-%d %a %H:%M') else: newdate = newdate.strftime('%Y-%m-%d %a') timestamp = '<%s>' % newdate if active else '[%s]' % newdate insert_at_cursor(timestamp)
def create(self): from orgmode import ORGMODE, echom cmd = self._mode if cmd == MODE_ALL: cmd = u'' if not self._remap: cmd += u'nore' try: create_mapping = True if isinstance(self._action, Plug): # create plug self._action.create() if int(vim.eval((u'hasmapto("%s")' % (self._action, )).encode(u'utf-8'))): create_mapping = False if isinstance(self._action, Command): # create command self._action.create() if create_mapping: vim.command((u':%smap %s %s %s' % (cmd, u' '.join(self._options), self._key, self._action)).encode(u'utf-8')) except Exception, e: if ORGMODE.debug: echom(u'Failed to register key binding %s %s' % (self._key, self._action))
def toggle_todo_state(cls, direction=Direction.FORWARD, interactive=False, next_set=False): u""" Toggle state of TODO item :returns: The changed heading """ d = ORGMODE.get_document(allow_dirty=True) # get heading heading = d.find_current_heading() if not heading: vim.eval(u'feedkeys("^", "n")') return todo_states = d.get_todo_states(strip_access_key=False) # get todo states if not todo_states: echom(u'No todo keywords configured.') return current_state = heading.todo # get new state interactively if interactive: # determine position of the interactive prompt prompt_pos = settings.get(u'org_todo_prompt_position', u'botright') if not prompt_pos in [u'botright', u'topleft']: prompt_pos = u'botright' # pass todo states to new window ORGTODOSTATES[d.bufnr] = todo_states settings.set(u'org_current_state_%d' % d.bufnr, \ current_state if current_state is not None else u'', overwrite=True) todo_buffer_exists = bool( int( vim.eval((u'bufexists("org:todo/%d")' % (d.bufnr, )).encode(u'utf-8')))) if todo_buffer_exists: # if the buffer already exists, reuse it vim.command((u'%s sbuffer org:todo/%d' % ( prompt_pos, d.bufnr, )).encode(u'utf-8')) else: # create a new window vim.command( (u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr)).encode(u'utf-8')) else: new_state = Todo._get_next_state(current_state, todo_states, direction=direction, interactive=interactive, next_set=next_set) cls.set_todo_state(new_state) # plug plug = u'OrgTodoForward' if direction == Direction.BACKWARD: plug = u'OrgTodoBackward' return plug
class Date(object): """ Handles all date and timestamp related tasks. TODO: extend functionality (calendar, repetitions, ranges). See http://orgmode.org/guide/Dates-and-Times.html#Dates-and-Times """ date_regex = r"\d\d\d\d-\d\d-\d\d" datetime_regex = r"[A-Z]\w\w \d\d\d\d-\d\d-\d\d \d\d:\d\d>" month_mapping = { 'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'mai': 5, 'jun': 6, 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12 } # set speeddating format that is compatible with orgmode try: if int(vim.eval('exists(":SpeedDatingFormat")')): vim.command(':1SpeedDatingFormat %Y-%m-%d %a') vim.command(':1SpeedDatingFormat %Y-%m-%d %a %H:%M') else: echom('Speeddating plugin not installed. Please install it.') except: echom('Speeddating plugin not installed. Please install it.') def __init__(self): """ Initialize plugin """ object.__init__(self) # menu entries this plugin should create self.menu = ORGMODE.orgmenu + Submenu('Dates and Scheduling') # key bindings for this plugin # key bindings are also registered through the menu so only additional # bindings should be put in this variable self.keybindings = [] # commands for this plugin self.commands = [] @classmethod def _modify_time(cls, startdate, modifier): """Modify the given startdate according to modifier. Return the new time. See http://orgmode.org/manual/The-date_002ftime-prompt.html#The-date_002ftime-prompt """ if modifier is None: return startdate # check real date date_regex = r"(\d\d\d\d)-(\d\d)-(\d\d)" match = re.search(date_regex, modifier) if match: year, month, day = match.groups() t = date(int(year), int(month), int(day)) return t # check abbreviated date, seperated with '-' date_regex = "(\d{1,2})-(\d+)-(\d+)" match = re.search(date_regex, modifier) if match: year, month, day = match.groups() t = date(2000 + int(year), int(month), int(day)) return t # check abbreviated date, seperated with '/' # month/day/year date_regex = "(\d{1,2})/(\d+)/(\d+)" match = re.search(date_regex, modifier) if match: month, day, year = match.groups() t = date(2000 + int(year), int(month), int(day)) return t # check abbreviated date, seperated with '/' # month/day date_regex = "(\d{1,2})/(\d{1,2})" match = re.search(date_regex, modifier) if match: month, day = match.groups() newdate = date(startdate.year, int(month), int(day)) # date should be always in the future if newdate < startdate: newdate = date(startdate.year + 1, int(month), int(day)) return newdate # check full date, seperated with 'space' # month day year # 'sep 12 9' --> 2009 9 12 date_regex = "(\w\w\w) (\d{1,2}) (\d{1,2})" match = re.search(date_regex, modifier) if match: gr = match.groups() day = int(gr[1]) month = int(cls.month_mapping[gr[0]]) year = 2000 + int(gr[2]) return date(year, int(month), int(day)) # check days as integers date_regex = "^(\d{1,2})$" match = re.search(date_regex, modifier) if match: newday, = match.groups() newday = int(newday) if newday > startdate.day: newdate = date(startdate.year, startdate.month, newday) else: # TODO: DIRTY, fix this # this does NOT cover all edge cases newdate = startdate + timedelta(days=28) newdate = date(newdate.year, newdate.month, newday) return newdate # check for full days: Mon, Tue, Wed, Thu, Fri, Sat, Sun modifier_lc = modifier.lower() match = re.search('mon|tue|wed|thu|fri|sat|sun', modifier_lc) if match: weekday_mapping = { 'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5, 'sun': 6 } diff = (weekday_mapping[modifier_lc] - startdate.weekday()) % 7 # use next weeks weekday if current weekday is the same as modifier if diff == 0: diff = 7 return startdate + timedelta(days=diff) # check for month day modifier_lc = modifier.lower() match = re.search( '(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2})', modifier_lc) if match: month = cls.month_mapping[match.groups()[0]] day = int(match.groups()[1]) newdate = date(startdate.year, int(month), int(day)) # date should be always in the future if newdate < startdate: newdate = date(startdate.year + 1, int(month), int(day)) return newdate # check for time: HH:MM # '12:45' --> datetime(2006,06,13, 12,45)) match = re.search('(\d{1,2}):(\d\d)', modifier) if match: return datetime(startdate.year, startdate.month, startdate.day, int(match.groups()[0]), int(match.groups()[1])) # check for days modifier match = re.search('\+(\d*)d', modifier) if match: days = int(match.groups()[0]) return startdate + timedelta(days=days) # check for week modifier match = re.search('\+(\d+)w', modifier) if match: weeks = int(match.groups()[0]) return startdate + timedelta(weeks=weeks) # check for week modifier match = re.search('\+(\d+)m', modifier) if match: months = int(match.groups()[0]) return date(startdate.year, startdate.month + months, startdate.day) # check for year modifier match = re.search('\+(\d*)y', modifier) if match: years = int(match.groups()[0]) return date(startdate.year + years, startdate.month, startdate.day) return startdate @classmethod def insert_timestamp(cls, active=True): """ Insert a timestamp (today) at the cursor position. TODO: show fancy calendar to pick the date from. """ today = date.today() msg = ''.join( ['Insert Date: ', today.strftime('%Y-%m-%d %a'), ' | Change date']) modifier = get_user_input(msg) echom(modifier) newdate = cls._modify_time(today, modifier) # format if isinstance(newdate, datetime): newdate = newdate.strftime('%Y-%m-%d %a %H:%M') else: newdate = newdate.strftime('%Y-%m-%d %a') timestamp = '<%s>' % newdate if active else '[%s]' % newdate insert_at_cursor(timestamp) def register(self): """ Registration of the plugin. Key bindings and other initialization should be done here. """ settings.set('org_leader', ',') leader = settings.get('org_leader', ',') self.keybindings.append( Keybinding( '%stn' % leader, Plug('OrgDateInsertTimestampActive', ':py ORGMODE.plugins["Date"].insert_timestamp()<CR>'))) self.menu + ActionEntry('Timestamp', self.keybindings[-1]) self.keybindings.append( Keybinding( '%sti' % leader, Plug( 'OrgDateInsertTimestampInactive', ':py ORGMODE.plugins["Date"].insert_timestamp(False)<CR>')) ) self.menu + ActionEntry('Timestamp (inactive)', self.keybindings[-1])
def _get_next_state(cls, current_state, all_states, direction=Direction.FORWARD, interactive=False, next_set=False): u""" :current_state: the current todo state :all_states: a list containing all todo states within sublists. The todo states may contain access keys :direction: direction of state or keyword set change (forward/backward) :interactive: if interactive and more than one todo sequence is specified, open a selection window :next_set: advance to the next keyword set in defined direction :return: return the next state as string, or NONE if the next state is no state. """ if not all_states: return def find_current_todo_state(c, a, stop=0): u""" :c: current todo state :a: list of todo states :stop: internal parameter for parsing only two levels of lists :return: first position of todo state in list in the form (IDX_TOPLEVEL, IDX_SECOND_LEVEL (0|1), IDX_OF_ITEM) """ for i in xrange(0, len(a)): if type(a[i]) in (tuple, list) and stop < 2: r = find_current_todo_state(c, a[i], stop=stop + 1) if r: r.insert(0, i) return r # ensure that only on the second level of sublists todo states # are found if type(a[i]) == unicode and stop == 2: _i = split_access_key(a[i])[0] if c == _i: return [i] ci = find_current_todo_state(current_state, all_states) if not ci: if next_set and direction == Direction.BACKWARD: echom(u'Already at the first keyword set') return current_state return split_access_key(all_states[0][0][0] if all_states[0][0] else all_states[0][1][0])[0] \ if direction == Direction.FORWARD else \ split_access_key(all_states[0][1][-1] if all_states[0][1] else all_states[0][0][-1])[0] elif next_set: if direction == Direction.FORWARD and ci[0] + 1 < len(all_states[ci[0]]): echom(u'Keyword set: %s | %s' % (u', '.join(all_states[ci[0] + 1][0]), u', '.join(all_states[ci[0] + 1][1]))) return split_access_key(all_states[ci[0] + 1][0][0] \ if all_states[ci[0] + 1][0] else all_states[ci[0] + 1][1][0])[0] elif current_state is not None and direction == Direction.BACKWARD and ci[0] - 1 >= 0: echom(u'Keyword set: %s | %s' % (u', '.join(all_states[ci[0] - 1][0]), u', '.join(all_states[ci[0] - 1][1]))) return split_access_key(all_states[ci[0] - 1][0][0] \ if all_states[ci[0] - 1][0] else all_states[ci[0] - 1][1][0])[0] else: echom(u'Already at the %s keyword set' % (u'first' if direction == Direction.BACKWARD else u'last')) return current_state else: next_pos = ci[2] + 1 if direction == Direction.FORWARD else ci[2] - 1 if direction == Direction.FORWARD: if next_pos < len(all_states[ci[0]][ci[1]]): # select next state within done or todo states return split_access_key(all_states[ci[0]][ci[1]][next_pos])[0] elif not ci[1] and next_pos - len(all_states[ci[0]][ci[1]]) < len(all_states[ci[0]][ci[1] + 1]): # finished todo states, jump to done states return split_access_key(all_states[ci[0]][ci[1] + 1][next_pos - len(all_states[ci[0]][ci[1]])])[0] else: if next_pos >= 0: # select previous state within done or todo states return split_access_key(all_states[ci[0]][ci[1]][next_pos])[0] elif ci[1] and len(all_states[ci[0]][ci[1] - 1]) + next_pos < len(all_states[ci[0]][ci[1] - 1]): # finished done states, jump to todo states return split_access_key(all_states[ci[0]][ci[1] - 1][len(all_states[ci[0]][ci[1] - 1]) + next_pos])[0]
def _get_next_state(cls, current_state, all_states, direction=DIRECTION_FORWARD, interactive=False, next_set=False): u""" :current_state: the current todo state :all_states: a list containing all todo states within sublists. The todo states may contain access keys :direction: direction of state or keyword set change (forward/backward) :interactive: if interactive and more than one todo sequence is specified, open a selection window :next_set: advance to the next keyword set in defined direction :return: return the next state as string, or NONE if the next state is no state. """ if not all_states: return def find_current_todo_state(c, a, stop=0): u""" :c: current todo state :a: list of todo states :stop: internal parameter for parsing only two levels of lists :return: first position of todo state in list in the form (IDX_TOPLEVEL, IDX_SECOND_LEVEL (0|1), IDX_OF_ITEM) """ for i in xrange(0, len(a)): if type(a[i]) in (tuple, list) and stop < 2: r = find_current_todo_state(c, a[i], stop=stop + 1) if r: r.insert(0, i) return r # ensure that only on the second level of sublists todo states # are found if type(a[i]) == unicode and stop == 2: _i = split_access_key(a[i])[0] if c == _i: return [i] ci = find_current_todo_state(current_state, all_states) if not ci: if next_set and direction == DIRECTION_BACKWARD: echom(u'Already at the first keyword set') return current_state return split_access_key(all_states[0][0][0] if all_states[0][0] else all_states[0][1][0])[0] \ if direction == DIRECTION_FORWARD else \ split_access_key(all_states[0][1][-1] if all_states[0][1] else all_states[0][0][-1])[0] elif next_set: if direction == DIRECTION_FORWARD and ci[0] + 1 < len(all_states[ci[0]]): echom(u'Keyword set: %s | %s' % (u', '.join(all_states[ci[0] + 1][0]), u', '.join(all_states[ci[0] + 1][1]))) return split_access_key(all_states[ci[0] + 1][0][0] \ if all_states[ci[0] + 1][0] else all_states[ci[0] + 1][1][0])[0] elif current_state is not None and direction == DIRECTION_BACKWARD and ci[0] - 1 >= 0: echom(u'Keyword set: %s | %s' % (u', '.join(all_states[ci[0] - 1][0]), u', '.join(all_states[ci[0] - 1][1]))) return split_access_key(all_states[ci[0] - 1][0][0] \ if all_states[ci[0] - 1][0] else all_states[ci[0] - 1][1][0])[0] else: echom(u'Already at the %s keyword set' % (u'first' if direction == DIRECTION_BACKWARD else u'last')) return current_state else: next_pos = ci[2] + 1 if direction == DIRECTION_FORWARD else ci[2] - 1 if direction == DIRECTION_FORWARD: if next_pos < len(all_states[ci[0]][ci[1]]): # select next state within done or todo states return split_access_key(all_states[ci[0]][ci[1]][next_pos])[0] elif not ci[1] and next_pos - len(all_states[ci[0]][ci[1]]) < len(all_states[ci[0]][ci[1] + 1]): # finished todo states, jump to done states return split_access_key(all_states[ci[0]][ci[1] + 1][next_pos - len(all_states[ci[0]][ci[1]])])[0] else: if next_pos >= 0: # select previous state within done or todo states return split_access_key(all_states[ci[0]][ci[1]][next_pos])[0] elif ci[1] and len(all_states[ci[0]][ci[1] - 1]) + next_pos < len(all_states[ci[0]][ci[1] - 1]): # finished done states, jump to todo states return split_access_key(all_states[ci[0]][ci[1] - 1][len(all_states[ci[0]][ci[1] - 1]) + next_pos])[0]