def create(self): from orgmode._vim 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 tolatex(cls): u"""Export the current buffer as latex using emacs orgmode.""" ret = cls._export(u'org-latex-export-to-latex') if ret != 0: echoe(u'latex export failed.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'tex'))
def tobeamer(cls): u"""Export the current buffer as beamer pdf using emacs orgmode.""" ret = cls._export(u'org-beamer-export-to-pdf') if ret != 0: echoe(u'PDF export failed.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
def _export(cls, format_): """Export current file to format. Args: format_: pdf or html Returns: return code """ emacsbin = os.path.expandvars( os.path.expanduser( settings.get(u'org_export_emacs', u'/usr/bin/emacs'))) if not os.path.exists(emacsbin): echoe(u'Unable to find emacs binary %s' % emacsbin) # build the export command cmd = [ emacsbin, u'-nw', u'--batch', u'--visit=%s' % vim.eval(u'expand("%:p")'), u'--funcall=%s' % format_ ] # source init script as well init_script = cls._get_init_script() if init_script: cmd.extend(['--script', init_script]) # export 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
def split_access_key(t, sub=None): u""" Split access key Args: t (str): Todo state sub: A value that will be returned instead of access key if there was not access key Returns: tuple: Todo state and access key separated (TODO, ACCESS_KEY) Example: >>> split_access_key('TODO(t)') >>> ('TODO', '(t)') >>> split_access_key('WANT', sub='(hi)') >>> ('WANT', '(hi)') """ if type(t) != unicode: echom("String must be unicode") return (None, None) idx = t.find(u'(') v, k = (t, sub) if idx != -1 and t[idx + 1:-1]: v, k = (t[:idx], t[idx + 1:-1]) return (v, k)
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 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_encode(u'Utl %s %s %s' % (action, visual, link[u'uri']))) return link[u'uri'] else: # call UTL and let it decide what to do vim.command(u_encode(u'Utl %s %s' % (action, visual)))
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 _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 tohtml(cls): u"""Export the current buffer as html using emacs orgmode.""" ret = cls._export(u'org-html-export-to-html') if ret != 0: echoe(u'HTML export failed.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'html'))
def tomarkdown(cls): u"""Export the current buffer as markdown using emacs orgmode.""" ret = cls._export(u'org-md-export-to-markdown') if ret != 0: echoe('Markdown export failed. Make sure org-md-export-to-markdown is loaded in emacs, see the manual for details.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'md'))
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_encode((u'Utl %s %s %s' % (action, visual, link[u'uri'])))) return link[u'uri'] else: # call UTL and let it decide what to do vim.command(u_encode((u'Utl %s %s' % (action, visual))))
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 _export(cls, format_): """Export current file to format_. :format_: pdf or html :returns: return code """ emacsbin = os.path.expandvars(os.path.expanduser( settings.get(u'org_export_emacs', u'/usr/bin/emacs'))) if not os.path.exists(emacsbin): echoe(u'Unable to find emacs binary %s' % emacsbin) # build the export command cmd = [ emacsbin, u'-nw', u'--batch', u'--visit=%s' % vim.eval(u'expand("%:p")'), u'--funcall=%s' % format_ ] # source init script as well init_script = cls._get_init_script() if init_script: cmd.extend(['--script', init_script]) # export 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
def tolatex(cls): u"""Export the current buffer as latex using emacs orgmode.""" ret = cls._export(u'latex') if ret != 0: echoe(u'latex export failed.') else: echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'tex'))
def init_org_todo(cls): u""" Initialize org todo selection window. """ bufnr = int(re.findall('\d+$', vim.current.buffer.name)[0]) all_states = ORGTODOSTATES.get(bufnr, None) vim_commands = [ u'let g:org_sav_timeoutlen=&timeoutlen', u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif', u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif', u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight', u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval( u_encode( u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)), u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % vim.eval(u_encode(u'bufnr("%")')), u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL, ] # because timeoutlen can only be set globally it needs to be stored and # restored later # make window a scratch window and set the statusline differently for cmd in vim_commands: vim.command(u_encode(cmd)) if all_states is None: vim.command(u_encode(u'bw')) echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name) for idx, state in enumerate(all_states): pairs = [split_access_key(x, sub=u' ') for x in it.chain(*state)] line = u'\t'.join(u''.join((u'[%s] ' % x[1], x[0])) for x in pairs) vim.current.buffer.append(u_encode(line)) for todo, key in pairs: # FIXME if double key is used for access modified this doesn't work vim.command( u_encode( u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state("%s")<CR>' % (key, VIM_PY_CALL, u_decode(todo)))) # position the cursor of the current todo item vim.command(u_encode(u'normal! G')) current_state = settings.unset(u'org_current_state_%d' % bufnr) if current_state is not None and current_state != '': for i, buf in enumerate(vim.current.buffer): idx = buf.find(current_state) if idx != -1: vim.current.window.cursor = (i + 1, idx) break else: vim.current.window.cursor = (2, 4) # finally make buffer non modifiable vim.command(u_encode(u'setfiletype orgtodo')) vim.command(u_encode(u'setlocal nomodifiable')) # 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: # determine position of the interactive prompt prompt_pos = settings.get(u'org_todo_prompt_position', u'botright') if prompt_pos not 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_encode( u'bufexists("org:todo/%d")' % (d.bufnr, ))))) if todo_buffer_exists: # if the buffer already exists, reuse it vim.command(u_encode( u'%s sbuffer org:todo/%d' % (prompt_pos, d.bufnr, ))) else: # create a new window vim.command(u_encode( u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr))) else: new_state = Todo._get_next_state( current_state, todo_states, direction=direction, 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) # 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 prompt_pos not 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 init_org_todo(cls): u""" Initialize org todo selection window. """ bufnr = int(vim.current.buffer.name.split('/')[-1]) all_states = ORGTODOSTATES.get(bufnr, None) vim_commands = [ u'let g:org_sav_timeoutlen=&timeoutlen', u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif', u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif', u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight', u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval(u_encode(u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)), u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % vim.eval(u_encode(u'bufnr("%")')), u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL, ] # because timeoutlen can only be set globally it needs to be stored and # restored later # make window a scratch window and set the statusline differently for cmd in vim_commands: vim.command(u_encode(cmd)) if all_states is None: vim.command(u_encode(u'bw')) echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name) for idx, state in enumerate(all_states): pairs = [split_access_key(x, sub=u' ') for x in it.chain(*state)] line = u'\t'.join(u''.join((u'[%s] ' % x[1], x[0])) for x in pairs) vim.current.buffer.append(u_encode(line)) for todo, key in pairs: # FIXME if double key is used for access modified this doesn't work vim.command(u_encode(u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state("%s")<CR>' % (key, VIM_PY_CALL, u_decode(todo)))) # position the cursor of the current todo item vim.command(u_encode(u'normal! G')) current_state = settings.unset(u'org_current_state_%d' % bufnr) if current_state is not None and current_state != '': for i, buf in enumerate(vim.current.buffer): idx = buf.find(current_state) if idx != -1: vim.current.window.cursor = (i + 1, idx) break else: vim.current.window.cursor = (2, 4) # finally make buffer non modifiable vim.command(u_encode(u'setfiletype orgtodo')) vim.command(u_encode(u'setlocal nomodifiable')) # remove temporary todo states for the current buffer del ORGTODOSTATES[bufnr]
def _get_next_state(cls, current_state, all_states, direction=Direction.FORWARD, next_set=False): u""" Get the next todo state Args: current_state (str): The current todo state all_states (list): A list containing all todo states within sublists. The todo states may contain access keys direction: Direction of state or keyword set change (forward or backward) next_set: Advance to the next keyword set in defined direction. Returns: str or None: next todo state, or None if there is no next state. Note: all_states should have the form of: [(['TODO(t)'], ['DONE(d)']), (['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)'], ['FIXED(f)']), ([], ['CANCELED(c)'])] """ cleaned_todos, flattened_todos = cls._process_all_states(all_states) # backward direction should really be -1 not 2 next_dir = -1 if direction == Direction.BACKWARD else 1 # work only with top level index if next_set: top_set = next((todo_set[0] for todo_set in enumerate(cleaned_todos) if current_state in todo_set[1]), -1) ind = (top_set + next_dir) % len(cleaned_todos) if ind != len(cleaned_todos) - 1: echom("Using set: %s" % str(all_states[ind])) else: echom("Keyword removed.") return cleaned_todos[ind][0] # No next set, cycle around everything else: ind = next((todo_iter[0] for todo_iter in enumerate(flattened_todos) if todo_iter[1] == current_state), -1) return flattened_todos[(ind + next_dir) % len(flattened_todos)]
def _get_next_state( cls, current_state, all_states, direction=Direction.FORWARD, next_set=False): u""" Get the next todo state Args: current_state (str): The current todo state all_states (list): A list containing all todo states within sublists. The todo states may contain access keys direction: Direction of state or keyword set change (forward or backward) next_set: Advance to the next keyword set in defined direction. Returns: str or None: next todo state, or None if there is no next state. Note: all_states should have the form of: [(['TODO(t)'], ['DONE(d)']), (['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)'], ['FIXED(f)']), ([], ['CANCELED(c)'])] """ cleaned_todos, flattened_todos = cls._process_all_states(all_states) # backward direction should really be -1 not 2 next_dir = -1 if direction == Direction.BACKWARD else 1 # work only with top level index if next_set: top_set = next(( todo_set[0] for todo_set in enumerate(cleaned_todos) if current_state in todo_set[1]), -1) ind = (top_set + next_dir) % len(cleaned_todos) if ind != len(cleaned_todos) - 1: echom("Using set: %s" % str(all_states[ind])) else: echom("Keyword removed.") return cleaned_todos[ind][0] # No next set, cycle around everything else: ind = next(( todo_iter[0] for todo_iter in enumerate(flattened_todos) if todo_iter[1] == current_state), -1) return flattened_todos[(ind + next_dir) % len(flattened_todos)]
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 create(self): from orgmode._vim 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_encode(u'hasmapto("%s")' % (self._action, )))): create_mapping = False if isinstance(self._action, Command): # create command self._action.create() if create_mapping: vim.command(u_encode(u':%smap %s %s %s' % (cmd, u' '.join(self._options), self._key, self._action))) except Exception as e: if ORGMODE.debug: echom(u'Failed to register key binding %s %s' % (self._key, self._action))
def add_planning_date_line(cls, planning_tag): u""" Insert a planning datestamp in a line after the current line. TODO: Show fancy calendar to pick the date from. TODO: Update an existing date, if present. """ today = date.today() msg = u''.join([ u'Inserting ', unicode(today.strftime(u'%Y-%m-%d %a'), u'utf-8'), u' | Modify date']) modifier = get_user_input(msg) # abort if the user canceled the input promt if modifier is None: return echom('The mod was' + modifier + str(len(modifier))) newdate = cls._modify_time(today, modifier) # format if isinstance(newdate, datetime): newdate = newdate.strftime( u'%Y-%m-%d %a %H:%M'.encode(u'utf-8')).decode(u'utf-8') else: newdate = newdate.strftime( u'%Y-%m-%d %a'.encode(u'utf-8')).decode(u'utf-8') # Find the heading level for indentation curr_line = vim.current.line match = re.match(r'^(\*+)', curr_line) level = 1 if match: level += len(match.group()) timestamp = u' ' * level + u'%s: <%s>' % (planning_tag, newdate) # Edit the current buffer to insert the planning line curr_row, _ = vim.current.window.cursor curr_row -= 1 # Change vim 1-based indexing to 0-based indexing buff = vim.current.buffer pre_lines = buff[:curr_row+1] # Lines up to and including the current line if len(buff) > curr_row + 1: if planning_tag in buff[curr_row+1]: post_lines = buff[curr_row+2:] plan_line = re.sub( r'%s:[^>]*>' % planning_tag, timestamp.strip(), buff[curr_row+1]) elif 'SCHEDULED' in buff[curr_row+1] or 'DEADLINE' in buff[curr_row+1]: post_lines = buff[curr_row+2:] plan_line = buff[curr_row+1] + " " + timestamp.strip() else: post_lines = buff[curr_row+1:] plan_line = timestamp else: post_lines = [] plan_line = timestamp buff[:] = pre_lines + [plan_line] + post_lines
def new_checkbox(cls, below=None, plain=None): ''' if below is: True -> create new list below current line False/None -> create new list above current line if plain is: True -> create a plainlist item False/None -> create an empty checkbox ''' d = ORGMODE.get_document() h = d.current_heading() if h is None: return # init checkboxes for current heading h.init_checkboxes() c = h.current_checkbox() nc = Checkbox() nc._heading = h # default checkbox level # make it align with the 4-space tabbing level = 4 start = vim.current.window.cursor[0] - 1 # if no checkbox is found, insert at current line with indent level=1 if c is None: h.checkboxes.append(nc) else: l = c.get_parent_list() idx = c.get_index_in_parent_list() if l is not None and idx is not None: l.insert(idx + (1 if below else 0), nc) # workaround for broken associations, Issue #165 nc._parent = c.parent if below: if c.next_sibling: c.next_sibling._previous_sibling = nc nc._next_sibling = c.next_sibling c._next_sibling = nc nc._previous_sibling = c else: if c.previous_sibling: c.previous_sibling._next_sibling = nc nc._next_sibling = c nc._previous_sibling = c.previous_sibling c._previous_sibling = nc t = c.type # increase key for ordered lists if t[-1] in OrderListType: try: num = int(t[:-1]) + (1 if below else -1) if num < 0: # don't decrease to numbers below zero echom(u"Can't decrement further than '0'") return t = '%d%s' % (num, t[-1]) except ValueError: try: char = ord(t[:-1]) + (1 if below else -1) if below: if char == 91: # stop incrementing at Z (90) echom(u"Can't increment further than 'Z'") return elif char == 123: # increment from z (122) to A char = 65 else: if char == 96: # stop decrementing at a (97) echom(u"Can't decrement further than 'a'") return elif char == 64: # decrement from A (65) to z char = 122 t = u'%s%s' % (chr(char), t[-1]) except ValueError: pass nc.type = t level = c.level if below: start = c.end_of_last_child else: start = c.start if plain: # only create plainlist item when requested nc.status = None nc.level = level if below: start += 1 # vim's buffer behave just opposite to Python's list when inserting a # new item. The new entry is appended in vim put prepended in Python! vim.current.buffer.append("") # workaround for neovim vim.current.buffer[start:start] = [unicode(nc)] del vim.current.buffer[-1] # restore from workaround for neovim # update checkboxes status cls.update_checkboxes_status() # do not start insert upon adding new checkbox, Issue #211 if int(settings.get(u'org_prefer_insert_mode', u'1')): vim.command( u_encode(u'exe "normal %dgg"|startinsert!' % (start + 1, ))) else: vim.command(u_encode(u'exe "normal %dgg$"' % (start + 1, )))
def _get_next_state(cls, current_state, all_states, direction=Direction.FORWARD, interactive=False, next_set=False): u""" WTF is going on here!!! FIXME: reimplement this in a clean way :) :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 range(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 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_encode(u'let g:org_sav_timeoutlen=&timeoutlen')) vim.command( u_encode( u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif' )) vim.command( u_encode( u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif' )) # make window a scratch window and set the statusline differently vim.command( u_encode( u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight' )) vim.command( u_encode((u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval( u_encode((u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)))))) vim.command( u_encode((u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % (vim.eval(u_encode(u'bufnr("%")')), )))) vim.command( u_encode( u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL)) if all_states is None: vim.command(u_encode(u'bw')) echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name) for l in range(0, len(all_states)): res = u'' for j in range(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_encode(( u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state(u_decode("%s")))<CR>' % (k, VIM_PY_CALL, v)))) elif v: res += (u'\t' if res else u'') + v if res: if l == 0: # WORKAROUND: the cursor can not be positioned properly on # the first line. Another line is just inserted and it # works great vim.current.buffer[0] = u_encode(u'') vim.current.buffer.append(u_encode(res)) # position the cursor of the current todo item vim.command(u_encode(u'normal! G')) current_state = settings.unset(u'org_current_state_%d' % bufnr) found = False if current_state is not None and current_state != '': for i in range(0, len(vim.current.buffer)): idx = vim.current.buffer[i].find(current_state) if idx != -1: vim.current.window.cursor = (i + 1, idx) found = True break if not found: vim.current.window.cursor = (2, 4) # finally make buffer non modifiable vim.command(u_encode(u'setfiletype orgtodo')) vim.command(u_encode(u'setlocal nomodifiable')) # remove temporary todo states for the current buffer del ORGTODOSTATES[bufnr]
def new_checkbox(cls, below=None): d = ORGMODE.get_document() h = d.current_heading() if h is None: return # init checkboxes for current heading h.init_checkboxes() c = h.current_checkbox() nc = Checkbox() nc._heading = h # default checkbox level level = h.level + 1 start = vim.current.window.cursor[0] - 1 # if no checkbox is found, insert at current line with indent level=1 if c is None: h.checkboxes.append(nc) else: l = c.get_parent_list() idx = c.get_index_in_parent_list() if l is not None and idx is not None: l.insert(idx + (1 if below else 0), nc) # workaround for broken associations, Issue #165 nc._parent = c.parent if below: if c.next_sibling: c.next_sibling._previous_sibling = nc nc._next_sibling = c.next_sibling c._next_sibling = nc nc._previous_sibling = c else: if c.previous_sibling: c.previous_sibling._next_sibling = nc nc._next_sibling = c nc._previous_sibling = c.previous_sibling c._previous_sibling = nc t = c.type # increase key for ordered lists if t[-1] in OrderListType: try: num = int(t[:-1]) + (1 if below else -1) if num < 0: # don't decrease to numbers below zero echom(u"Can't decrement further than '0'") return t = '%d%s' % (num, t[-1]) except ValueError: try: char = ord(t[:-1]) + (1 if below else -1) if below: if char == 91: # stop incrementing at Z (90) echom(u"Can't increment further than 'Z'") return elif char == 123: # increment from z (122) to A char = 65 else: if char == 96: # stop decrementing at a (97) echom(u"Can't decrement further than 'a'") return elif char == 64: # decrement from A (65) to z char = 122 t = u'%s%s' % (chr(char), t[-1]) except ValueError: pass nc.type = t if not c.status: nc.status = None level = c.level if below: start = c.end_of_last_child else: start = c.start nc.level = level if below: start += 1 # vim's buffer behave just opposite to Python's list when inserting a # new item. The new entry is appended in vim put prepended in Python! vim.current.buffer[start:start] = [unicode(nc)] # update checkboxes status cls.update_checkboxes_status() vim.command((u'exe "normal %dgg"|startinsert!' % (start + 1, )).encode(u'utf-8'))
def new_checkbox(cls, below=None, plain=None): ''' if below is: True -> create new list below current line False/None -> create new list above current line if plain is: True -> create a plainlist item False/None -> create an empty checkbox ''' d = ORGMODE.get_document() h = d.current_heading() if h is None: return # init checkboxes for current heading h.init_checkboxes() c = h.current_checkbox() nc = Checkbox() nc._heading = h # default checkbox level level = h.level + 1 start = vim.current.window.cursor[0] - 1 # if no checkbox is found, insert at current line with indent level=1 if c is None: h.checkboxes.append(nc) else: l = c.get_parent_list() idx = c.get_index_in_parent_list() if l is not None and idx is not None: l.insert(idx + (1 if below else 0), nc) # workaround for broken associations, Issue #165 nc._parent = c.parent if below: if c.next_sibling: c.next_sibling._previous_sibling = nc nc._next_sibling = c.next_sibling c._next_sibling = nc nc._previous_sibling = c else: if c.previous_sibling: c.previous_sibling._next_sibling = nc nc._next_sibling = c nc._previous_sibling = c.previous_sibling c._previous_sibling = nc t = c.type # increase key for ordered lists if t[-1] in OrderListType: try: num = int(t[:-1]) + (1 if below else -1) if num < 0: # don't decrease to numbers below zero echom(u"Can't decrement further than '0'") return t = '%d%s' % (num, t[-1]) except ValueError: try: char = ord(t[:-1]) + (1 if below else -1) if below: if char == 91: # stop incrementing at Z (90) echom(u"Can't increment further than 'Z'") return elif char == 123: # increment from z (122) to A char = 65 else: if char == 96: # stop decrementing at a (97) echom(u"Can't decrement further than 'a'") return elif char == 64: # decrement from A (65) to z char = 122 t = u'%s%s' % (chr(char), t[-1]) except ValueError: pass nc.type = t level = c.level if below: start = c.end_of_last_child else: start = c.start if plain: # only create plainlist item when requested nc.status = None nc.level = level if below: start += 1 # vim's buffer behave just opposite to Python's list when inserting a # new item. The new entry is appended in vim put prepended in Python! vim.current.buffer.append("") # workaround for neovim vim.current.buffer[start:start] = [unicode(nc)] del vim.current.buffer[-1] # restore from workaround for neovim # update checkboxes status cls.update_checkboxes_status() # do not start insert upon adding new checkbox, Issue #211 if int(settings.get(u'org_prefer_insert_mode', u'1')): vim.command(u_encode((u'exe "normal %dgg"|startinsert!' % (start + 1, )))) else: vim.command(u_encode((u'exe "normal %dgg$"' % (start + 1, ))))
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_encode(u'let g:org_sav_timeoutlen=&timeoutlen')) vim.command(u_encode(u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif')) vim.command(u_encode(u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif')) # make window a scratch window and set the statusline differently vim.command(u_encode(u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight')) vim.command(u_encode((u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval(u_encode((u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)))))) vim.command(u_encode((u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % (vim.eval(u_encode(u'bufnr("%")')), )))) vim.command(u_encode(u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL)) if all_states is None: vim.command(u_encode(u'bw')) echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name) for l in range(0, len(all_states)): res = u'' for j in range(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_encode((u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state(u_decode("%s")))<CR>' % (k, VIM_PY_CALL, v)))) elif v: res += (u'\t' if res else u'') + v if res: if l == 0: # WORKAROUND: the cursor can not be positioned properly on # the first line. Another line is just inserted and it # works great vim.current.buffer[0] = u_encode(u'') vim.current.buffer.append(u_encode(res)) # position the cursor of the current todo item vim.command(u_encode(u'normal! G')) current_state = settings.unset(u'org_current_state_%d' % bufnr) found = False if current_state is not None and current_state != '': for i in range(0, len(vim.current.buffer)): idx = vim.current.buffer[i].find(current_state) if idx != -1: vim.current.window.cursor = (i + 1, idx) found = True break if not found: vim.current.window.cursor = (2, 4) # finally make buffer non modifiable vim.command(u_encode(u'setfiletype orgtodo')) vim.command(u_encode(u'setlocal nomodifiable')) # remove temporary todo states for the current buffer del ORGTODOSTATES[bufnr]
def _get_next_state( cls, current_state, all_states, direction=Direction.FORWARD, interactive=False, next_set=False): u""" WTF is going on here!!! FIXME: reimplement this in a clean way :) :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 range(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]