def setUp(self): global counter counter += 1 vim.CMDHISTORY = [] vim.CMDRESULTS = {} vim.EVALHISTORY = [] vim.EVALRESULTS = { # no org_todo_keywords for b u'exists("b:org_todo_keywords")'.encode(u'utf-8'): '0'.encode(u'utf-8'), # global values for org_todo_keywords u'exists("g:org_todo_keywords")'.encode(u'utf-8'): '1'.encode(u'utf-8'), u'g:org_todo_keywords'.encode(u'utf-8'): [ u'TODO'.encode(u'utf-8'), u'DONE'.encode(u'utf-8'), u'|'.encode(u'utf-8') ], u'exists("g:org_debug")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("b:org_debug")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("*repeat#set()")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("b:org_leader")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("g:org_leader")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'b:changedtick'.encode(u'utf-8'): (u'%d' % counter).encode(u'utf-8'), u"v:count".encode(u'utf-8'): u'0'.encode(u'utf-8') } if not u'TagsProperties' in ORGMODE.plugins: ORGMODE.register_plugin(u'TagsProperties') self.showhide = ORGMODE.plugins[u'TagsProperties'] vim.current.buffer[:] = [ i.encode(u'utf-8') for i in u""" * Überschrift 1 Text 1 Bla bla ** Überschrift 1.1 Text 2 Bla Bla bla ** Überschrift 1.2 Text 3 **** Überschrift 1.2.1.falsch Bla Bla bla bla *** Überschrift 1.2.1 * Überschrift 2 * Überschrift 3 asdf sdf """.split(u'\n') ]
def setUp(self): global counter counter += 1 vim.CMDHISTORY = [] vim.CMDRESULTS = {} vim.EVALHISTORY = [] vim.EVALRESULTS = { # no org_todo_keywords for b u'exists("b:org_todo_keywords")'.encode(u"utf-8"): "0".encode(u"utf-8"), # global values for org_todo_keywords u'exists("g:org_todo_keywords")'.encode(u"utf-8"): "1".encode(u"utf-8"), u"g:org_todo_keywords".encode(u"utf-8"): [ u"TODO".encode(u"utf-8"), u"DONE".encode(u"utf-8"), u"|".encode(u"utf-8"), ], u'exists("g:org_improve_split_heading")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u'exists("b:org_improve_split_heading")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u'exists("g:org_debug")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u'exists("b:org_debug")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u'exists("*repeat#set()")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u"b:changedtick".encode(u"utf-8"): (u"%d" % counter).encode(u"utf-8"), u"&ts".encode(u"utf-8"): u"8".encode(u"utf-8"), u'exists("g:org_tag_column")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u'exists("b:org_tag_column")'.encode(u"utf-8"): u"0".encode(u"utf-8"), u"v:count".encode(u"utf-8"): u"0".encode(u"utf-8"), } if not u"EditStructure" in ORGMODE.plugins: ORGMODE.register_plugin(u"EditStructure") self.editstructure = ORGMODE.plugins[u"EditStructure"] vim.current.buffer[:] = [ i.encode(u"utf-8") for i in u""" * Überschrift 1 Text 1 Bla bla ** Überschrift 1.1 Text 2 Bla Bla bla ** Überschrift 1.2 Text 3 **** Überschrift 1.2.1.falsch Bla Bla bla bla *** Überschrift 1.2.1 * Überschrift 2 * Überschrift 3 asdf sdf """.split( u"\n" ) ]
def set_todo_state(cls, state): u""" Set todo state for buffer. :bufnr: Number of buffer the todo state should be updated for :state: The new todo state """ lineno, colno = vim.current.window.cursor d = ORGMODE.get_document(allow_dirty=True) heading = d.find_current_heading() if not heading: return current_state = heading.todo # 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 state is None: offset = 0 elif current_state is None: offset = len(state) elif state is None: offset = -len(current_state) else: offset = len(current_state) - len(state) vim.current.window.cursor = (lineno, colno + offset) # set new headline heading.todo = state d.write_heading(heading)
def global_toggle_folding(cls, reverse=False): """ Toggle folding globally :reverse: If False open folding by one level otherwise close it by one. """ d = ORGMODE.get_document() if reverse: foldlevel = int(vim.eval(u'&foldlevel'.encode(u'utf-8'))) if foldlevel == 0: # open all folds because the user tries to close folds beyound 0 vim.eval(u'feedkeys("zR", "n")'.encode(u'utf-8')) else: # vim can reduce the foldlevel on its own vim.eval(u'feedkeys("zm", "n")'.encode(u'utf-8')) else: found = False for h in d.headings: res = cls._fold_depth(h) if res: found = res[1] if found: break if not found: # no fold found and the user tries to advance the fold level # beyond maximum so close everything vim.eval(u'feedkeys("zM", "n")'.encode(u'utf-8')) else: # fold found, vim can increase the foldlevel on its own vim.eval(u'feedkeys("zr", "n")'.encode(u'utf-8')) return d
def _move_heading(cls, direction=DIRECTION_FORWARD, including_children=True): u""" Move heading up or down :returns: heading or None """ d = ORGMODE.get_document() heading = d.current_heading() if not heading or \ direction == DIRECTION_FORWARD and not heading.next_sibling or \ direction == DIRECTION_BACKWARD and not heading.previous_sibling: return None cursor_offset_within_the_heading_vim = vim.current.window.cursor[0] - (heading._orig_start + 1) if not including_children: heading.previous_sibling.children.extend(heading.children) del heading.children heading_insert_position = 1 if direction == DIRECTION_FORWARD else -1 l = heading.get_parent_list() idx = heading.get_index_in_parent_list() if l is not None and idx is not None: l.insert(idx + heading_insert_position, heading) else: raise HeadingDomError('Current heading is not properly linked in DOM') d.write() vim.current.window.cursor = (heading.start_vim + cursor_offset_within_the_heading_vim, vim.current.window.cursor[1]) return True
def parent_next_sibling(cls, mode): u""" Focus the parent's next sibling :returns: parent's next sibling heading or None """ heading = ORGMODE.get_document().current_heading() if not heading: if mode == u'visual': vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No heading found') return if not heading.parent or not heading.parent.next_sibling: if mode == u'visual': vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No parent heading found') return ns = heading.parent.next_sibling if mode == u'visual': cls._change_visual_selection(heading, ns, direction=DIRECTION_FORWARD, parent=False) elif mode == u'operator': vim.current.window.cursor = (ns.start_vim, 0) else: vim.current.window.cursor = (ns.start_vim, ns.level + 1) return ns
def parent_next_sibling(cls, mode): u""" Focus the parent's next sibling :returns: parent's next sibling heading or None """ heading = ORGMODE.get_document().current_heading() if not heading: if mode == u"visual": vim.command(u"normal! gv".encode(u"utf-8")) else: echo(u"No heading found") return if not heading.parent or not heading.parent.next_sibling: if mode == u"visual": vim.command(u"normal! gv".encode(u"utf-8")) else: echo(u"No parent heading found") return ns = heading.parent.next_sibling if mode == u"visual": cls._change_visual_selection(heading, ns, direction=Direction.FORWARD, parent=False) elif mode == u"operator": vim.current.window.cursor = (ns.start_vim, 0) else: vim.current.window.cursor = (ns.start_vim, ns.level + 1) return ns
def a_heading(cls, selection=u'inner', skip_children=False): u""" a heading text object """ heading = ORGMODE.get_document().current_heading() if heading: if selection != u'inner': heading = heading if not heading.parent else heading.parent line_start, col_start = [ int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3] ] line_end, col_end = [ int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3] ] start = line_start end = line_end if heading.start_vim < line_start: start = heading.start_vim if heading.end_vim > line_end and not skip_children: end = heading.end_vim elif heading.end_of_last_child_vim > line_end and skip_children: end = heading.end_of_last_child_vim swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u'' vim.command((u'normal! %dgg%s%dgg$%s' % \ (start, vim.eval(u'visualmode()'.encode(u'utf-8')), end, swap_cursor)).encode(u'utf-8')) if selection == u'inner': return u'OrgAInnerHeadingVisual' if not skip_children else u'OrgAInnerTreeVisual' else: return u'OrgAOuterHeadingVisual' if not skip_children else u'OrgAOuterTreeVisual' else: vim.command(u'normal! gv'.encode(u'utf-8'))
def _move_heading(cls, direction=Direction.FORWARD, including_children=True): u""" Move heading up or down :returns: heading or None """ d = ORGMODE.get_document() heading = d.current_heading() if (not heading) or \ (direction == Direction.FORWARD and not heading.next_sibling) or \ (direction == Direction.BACKWARD and not heading.previous_sibling): return None cursor_offset_within_the_heading_vim = vim.current.window.cursor[0] - (heading._orig_start + 1) if not including_children: heading.previous_sibling.children.extend(heading.children) del heading.children heading_insert_position = 0 if direction == Direction.FORWARD else -1 l = heading.get_parent_list() idx = heading.get_index_in_parent_list() del l[idx] if l is not None and idx is not None: l.insert(idx + heading_insert_position, heading) else: raise HeadingDomError(u'Current heading is not properly linked in DOM') d.write() vim.current.window.cursor = (heading.start_vim + cursor_offset_within_the_heading_vim, vim.current.window.cursor[1]) return True
def parent(cls, mode): u""" Focus parent heading :returns: parent heading or None """ heading = ORGMODE.get_document().current_heading() if not heading: if mode == u'visual': vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No heading found') return if not heading.parent: if mode == u'visual': vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No parent heading found') return p = heading.parent if mode == u'visual': cls._change_visual_selection(heading, p, direction=DIRECTION_BACKWARD, parent=True) else: vim.current.window.cursor = (p.start_vim, p.level + 1) return p
def set_tags(cls): u""" Set tags for current heading """ d = ORGMODE.get_document() heading = d.current_heading() if not heading: return # retrieve tags res = None if heading.tags: res = vim.eval(u'input("Tags: ", ":%s:", "customlist,Org_complete_tags")' % u':'.join(heading.tags)) else: res = vim.eval(u'input("Tags: ", "", "customlist,Org_complete_tags")') if res is None: # user pressed <Esc> abort any further processing return # remove empty tags heading.tags = filter(lambda x: x.strip() != u'', res.decode(u'utf-8').strip().strip(u':').split(u':')) d.write() return u'OrgSetTags'
def parent(cls, mode): u""" Focus parent heading :returns: parent heading or None """ heading = ORGMODE.get_document().current_heading() if not heading: if mode == u'visual': vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No heading found') return if not heading.parent: if mode == u'visual': vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No parent heading found') return if mode == u'visual': cls._change_visual_selection(heading, heading.parent, direction=DIRECTION_BACKWARD, parent=True) else: vim.current.window.cursor = (heading.parent.start_vim, heading.parent.level + 1) return heading.parent
def _get_agendadocuments(self): u""" Return the org documents of the agenda files; return None if no agenda documents are defined. TODO: maybe turn this into an decorator? """ # load org files of agenda agenda_files = settings.get(u'org_agenda_files', u',') if not agenda_files or agenda_files == ',': echoe((u"No org_agenda_files defined. Use :let " u"g:org_agenda_files=['~/org/index.org'] to add " u"files to the agenda view.")) return agenda_files = [os.path.realpath(os.path.expanduser(f)) for f in agenda_files] for agenda_file in agenda_files: vim.command((u'badd %s' % agenda_file).encode(u'utf-8')) # determine the buffer nr of the agenda files agenda_nums = [get_bufnumber(fn) for fn in agenda_files] # collect all documents of the agenda files and create the agenda return [ORGMODE.get_document(i) for i in agenda_nums if i is not None]
def _get_agendadocuments(self): u""" Return the org documents of the agenda files; return None if no agenda documents are defined. TODO: maybe turn this into an decorator? """ # load org files of agenda agenda_files = settings.get(u'org_agenda_files', u',') if not agenda_files or agenda_files == ',': echoe((u"No org_agenda_files defined. Use :let " u"g:org_agenda_files=['~/org/index.org'] to add " u"files to the agenda view.")) return agenda_files = [os.path.expanduser(f) for f in agenda_files] for agenda_file in agenda_files: vim.command((u'badd %s' % agenda_file).encode(u'utf-8')) # determine the buffer nr of the agenda files agenda_nums = [get_bufnumber(fn) for fn in agenda_files] # collect all documents of the agenda files and create the agenda return [ORGMODE.get_document(i) for i in agenda_nums if i is not None]
def jump_to_first_character(cls): heading = ORGMODE.get_document().current_heading() if not heading: vim.eval(u'feedkeys("^", "n")'.encode(u'utf-8')) return vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1)
def parent(cls, mode): u""" Focus parent heading :returns: parent heading or None """ heading = ORGMODE.get_document().current_heading() if not heading: if mode == u"visual": vim.command(u"normal! gv".encode(u"utf-8")) else: echo(u"No heading found") return if not heading.parent: if mode == u"visual": vim.command(u"normal! gv".encode(u"utf-8")) else: echo(u"No parent heading found") return p = heading.parent if mode == u"visual": cls._change_visual_selection(heading, p, direction=Direction.BACKWARD, parent=True) else: vim.current.window.cursor = (p.start_vim, p.level + 1) return p
def edit_at_first_character(cls): heading = ORGMODE.get_document().current_heading() if not heading: vim.eval(u'feedkeys("I", "n")'.encode(u'utf-8')) return vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1) vim.command(u'startinsert'.encode(u'utf-8'))
def setUp(self): global counter counter += 1 vim.CMDHISTORY = [] vim.CMDRESULTS = {} vim.EVALHISTORY = [] vim.EVALRESULTS = { # no org_todo_keywords for b u'exists("b:org_todo_keywords")'.encode(u'utf-8'): '0'.encode(u'utf-8'), # global values for org_todo_keywords u'exists("g:org_todo_keywords")'.encode(u'utf-8'): '1'.encode(u'utf-8'), u'g:org_todo_keywords'.encode(u'utf-8'): [u'TODO'.encode(u'utf-8'), u'DONE'.encode(u'utf-8'), u'|'.encode(u'utf-8')], u'exists("g:org_improve_split_heading")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("b:org_improve_split_heading")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("g:org_debug")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("b:org_debug")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("*repeat#set()")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'b:changedtick'.encode(u'utf-8'): (u'%d' % counter).encode(u'utf-8'), u'&ts'.encode(u'utf-8'): u'8'.encode(u'utf-8'), u'exists("g:org_tag_column")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("b:org_tag_column")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u"v:count".encode(u'utf-8'): u'0'.encode(u'utf-8')} if not u'EditStructure' in ORGMODE.plugins: ORGMODE.register_plugin(u'EditStructure') self.editstructure = ORGMODE.plugins[u'EditStructure'] vim.current.buffer[:] = [ i.encode(u'utf-8') for i in u""" * Überschrift 1 Text 1 Bla bla ** Überschrift 1.1 Text 2 Bla Bla bla ** Überschrift 1.2 Text 3 **** Überschrift 1.2.1.falsch Bla Bla bla bla *** Überschrift 1.2.1 * Überschrift 2 * Überschrift 3 asdf sdf """.split(u'\n')]
def edit_at_first_character(cls): heading = ORGMODE.get_document().current_heading() if not heading or heading.start_vim != vim.current.window.cursor[0]: vim.eval(u'feedkeys("I", "n")'.encode(u'utf-8')) return vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1) vim.command(u'startinsert'.encode(u'utf-8'))
def realign_all_tags(cls): u""" Updates tags when user finishes editing a heading """ d = ORGMODE.get_document() for heading in d.all_headings(): heading.set_dirty_heading() d.write()
def _move_heading(cls, direction=Direction.FORWARD, including_children=True): u""" Move heading up or down :returns: heading or None """ d = ORGMODE.get_document() current_heading = d.current_heading() if not current_heading or \ (direction == Direction.FORWARD and not current_heading.next_sibling) or \ (direction == Direction.BACKWARD and not current_heading.previous_sibling): return None cursor_offset = vim.current.window.cursor[0] - ( current_heading._orig_start + 1) l = current_heading.get_parent_list() if l is None: raise HeadingDomError( u'Current heading is not properly linked in DOM') if not including_children: if current_heading.previous_sibling: npl = current_heading.previous_sibling.children for child in current_heading.children: npl.append(child, taint=False) elif current_heading.parent: # if the current heading doesn't have a previous sibling it # must be the first heading np = current_heading.parent for child in current_heading.children: cls._append_heading(child, np) else: # if the current heading doesn't have a parent, its children # must be added as top level headings to the document npl = l for child in current_heading.children[::-1]: npl.insert(0, child, taint=False) current_heading.children.remove_slice( 0, len(current_heading.children), taint=False) idx = current_heading.get_index_in_parent_list() if idx is None: raise HeadingDomError( u'Current heading is not properly linked in DOM') offset = 1 if direction == Direction.FORWARD else -1 del l[idx] l.insert(idx + offset, current_heading) d.write() vim.current.window.cursor = (current_heading.start_vim + cursor_offset, \ vim.current.window.cursor[1]) return True
def _change_heading_level(cls, level, including_children=True, on_heading=False): u""" Change level of heading realtively with or without including children. """ d = ORGMODE.get_document() h = d.current_heading() if not h or on_heading and h.start_vim != vim.current.window.cursor[0]: # TODO figure out the actually pressed keybinding and feed these # keys instead of making keys up like this if level > 0: if including_children: vim.eval((u'feedkeys(">]]", "n")').encode(u'utf-8')) elif on_heading: vim.eval(u'feedkeys(">>", "n")'.encode(u'utf-8')) else: vim.eval(u'feedkeys(">}", "n")'.encode(u'utf-8')) else: if including_children: vim.eval(u'feedkeys("<]]", "n")'.encode(u'utf-8')) elif on_heading: vim.eval(u'feedkeys("<<", "n")'.encode(u'utf-8')) else: vim.eval(u'feedkeys("<}", "n")'.encode(u'utf-8')) # return True because otherwise apply_count will not work return True # don't allow demotion below level 1 if h.level == 1 and level < 1: return False # reduce level of demotion to a minimum heading level of 1 if (h.level + level) < 1: level = 1 def indent(heading, ic): if not heading: return heading.level += level if ic: for child in heading.children: indent(child, ic) # save cursor position c = vim.current.window.cursor[:] # indent the promoted/demoted heading indent_end_vim = h.end_of_last_child_vim if including_children else h.end_vim indent(h, including_children) d.write() if indent_end_vim != h.start_vim: vim.command((u'normal %dggV%dgg=' % (h.start_vim, indent_end_vim)).encode(u'utf-8')) # restore cursor position vim.current.window.cursor = (c[0], c[1] + level) return True
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 test_demote_last_heading(self): vim.current.buffer[:] = """ * Überschrift 2 * Überschrift 3""".split('\n') vim.current.window.cursor = (3, 0) h = ORGMODE.get_document().current_heading() self.assertNotEqual(self.editstructure.demote_heading(), None) self.assertEqual(h.end, 2) self.assertFalse(vim.CMDHISTORY) self.assertEqual(vim.current.buffer[2], '** Überschrift 3') self.assertEqual(vim.current.window.cursor, (3, 1))
def realign_tags(cls): u""" Updates tags when user finished editing a heading """ d = ORGMODE.get_document(allow_dirty=True) heading = d.find_current_heading() if not heading: return if vim.current.window.cursor[0] == heading.start_vim: heading.set_dirty_heading() d.write_heading(heading, including_children=False)
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 _move_heading(cls, direction=Direction.FORWARD, including_children=True): u""" Move heading up or down :returns: heading or None """ d = ORGMODE.get_document() current_heading = d.current_heading() if ( not current_heading or (direction == Direction.FORWARD and not current_heading.next_sibling) or (direction == Direction.BACKWARD and not current_heading.previous_sibling) ): return None cursor_offset = vim.current.window.cursor[0] - (current_heading._orig_start + 1) l = current_heading.get_parent_list() if l is None: raise HeadingDomError(u"Current heading is not properly linked in DOM") if not including_children: if current_heading.previous_sibling: npl = current_heading.previous_sibling.children for child in current_heading.children: npl.append(child, taint=False) elif current_heading.parent: # if the current heading doesn't have a previous sibling it # must be the first heading np = current_heading.parent for child in current_heading.children: cls._append_heading(child, np) else: # if the current heading doesn't have a parent, its children # must be added as top level headings to the document npl = l for child in current_heading.children[::-1]: npl.insert(0, child, taint=False) current_heading.children.remove_slice(0, len(current_heading.children), taint=False) idx = current_heading.get_index_in_parent_list() if idx is None: raise HeadingDomError(u"Current heading is not properly linked in DOM") offset = 1 if direction == Direction.FORWARD else -1 del l[idx] l.insert(idx + offset, current_heading) d.write() vim.current.window.cursor = (current_heading.start_vim + cursor_offset, vim.current.window.cursor[1]) return True
def setUp(self): global counter counter += 1 vim.CMDHISTORY = [] vim.CMDRESULTS = {} vim.EVALHISTORY = [] vim.EVALRESULTS = { u'exists("g:org_debug")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("g:org_debug")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("*repeat#set()")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'b:changedtick'.encode(u'utf-8'): (u'%d' % counter).encode(u'utf-8'), u'&ts'.encode(u'utf-8'): u'8'.encode(u'utf-8'), u'exists("g:org_tag_column")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u'exists("b:org_tag_column")'.encode(u'utf-8'): u'0'.encode(u'utf-8'), u"v:count".encode(u'utf-8'): u'0'.encode(u'utf-8')} if not u'EditStructure' in ORGMODE.plugins: ORGMODE.register_plugin(u'EditStructure') self.editstructure = ORGMODE.plugins[u'EditStructure'] vim.current.buffer = [ i.encode(u'utf-8') for i in u""" * Überschrift 1 Text 1 Bla bla ** Überschrift 1.1 Text 2 Bla Bla bla ** Überschrift 1.2 Text 3 **** Überschrift 1.2.1.falsch Bla Bla bla bla *** Überschrift 1.2.1 * Überschrift 2 * Überschrift 3 asdf sdf """.split(u'\n')]
def setUp(self): vim.CMDHISTORY = [] vim.CMDRESULTS = {} vim.EVALHISTORY = [] vim.EVALRESULTS = { 'exists("g:org_debug")': 0, 'exists("b:org_debug")': 0, 'exists("*repeat#set()")': 0, 'exists("b:org_leader")': 0, 'exists("g:org_leader")': 0, 'exists("g:org_tags_column")': 0, 'exists("b:org_tags_column")': 0, 'exists("b:org_tags_completion_ignorecase")': 0, 'exists("g:org_tags_completion_ignorecase")': 0, "v:count": 0} if not 'TagsProperties' in ORGMODE.plugins: ORGMODE.register_plugin('TagsProperties') self.showhide = ORGMODE.plugins['TagsProperties'] vim.current.buffer = """ * Überschrift 1 Text 1 Bla bla ** Überschrift 1.1 Text 2 Bla Bla bla ** Überschrift 1.2 Text 3 **** Überschrift 1.2.1.falsch Bla Bla bla bla *** Überschrift 1.2.1 * Überschrift 2 * Überschrift 3 asdf sdf """.split('\n')
def test_demote_last_heading(self): vim.current.buffer[:] = [ i.encode(u'utf-8') for i in u""" * Überschrift 2 * Überschrift 3""".split('\n') ] vim.current.window.cursor = (3, 0) h = ORGMODE.get_document().current_heading() self.assertNotEqual(self.editstructure.demote_heading(), None) self.assertEqual(h.end, 2) self.assertFalse(vim.CMDHISTORY) self.assertEqual(vim.current.buffer[2], u'** Überschrift 3'.encode(u'utf-8')) self.assertEqual(vim.current.window.cursor, (3, 1))
def test_demote_last_heading(self): vim.current.buffer[:] = [ i.encode(u"utf-8") for i in u""" * Überschrift 2 * Überschrift 3""".split( "\n" ) ] vim.current.window.cursor = (3, 0) h = ORGMODE.get_document().current_heading() self.assertNotEqual(self.editstructure.demote_heading(), None) self.assertEqual(h.end, 2) self.assertFalse(vim.CMDHISTORY) self.assertEqual(vim.current.buffer[2], u"** Überschrift 3".encode(u"utf-8")) self.assertEqual(vim.current.window.cursor, (3, 1))
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 complete_tags(cls): u""" build a list of tags and store it in variable b:org_tag_completion """ d = ORGMODE.get_document() heading = d.current_heading() if not heading: return leading_portion = vim.eval(u'a:ArgLead').decode(u'utf-8') cursor = int(vim.eval(u'a:CursorPos')) # extract currently completed tag idx_orig = leading_portion.rfind(u':', 0, cursor) if idx_orig == -1: idx = 0 else: idx = idx_orig current_tag = leading_portion[idx: cursor].lstrip(u':') head = leading_portion[:idx + 1] if idx_orig == -1: head = u'' tail = leading_portion[cursor:] # extract all tags of the current file all_tags = set() for h in d.all_headings(): for t in h.tags: all_tags.add(t) ignorecase = bool(int(settings.get(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase'))))) possible_tags = [] current_tags = heading.tags for t in all_tags: if ignorecase: if t.lower().startswith(current_tag.lower()): possible_tags.append(t) elif t.startswith(current_tag): possible_tags.append(t) vim.command((u'let b:org_complete_tags = [%s]' % u', '.join([u'"%s%s:%s"' % (head, i, tail) for i in possible_tags])).encode(u'utf-8'))
def a_heading(cls, selection=u'inner', skip_children=False): u""" a heading text object """ heading = ORGMODE.get_document().current_heading() if heading: if selection != u'inner': heading = heading if not heading.parent else heading.parent line_start, col_start = [ int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3] ] line_end, col_end = [ int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3] ] start = line_start end = line_end if heading.start_vim < line_start: start = heading.start_vim if heading.end_vim > line_end and not skip_children: end = heading.end_vim elif heading.end_of_last_child_vim > line_end and skip_children: end = heading.end_of_last_child_vim swap_cursor = u'o' if vim.current.window.cursor[ 0] == line_start else u'' vim.command((u'normal! %dgg%s%dgg$%s' % \ (start, vim.eval(u'visualmode()'.encode(u'utf-8')), end, swap_cursor)).encode(u'utf-8')) if selection == u'inner': return u'OrgAInnerHeadingVisual' if not skip_children else u'OrgAInnerTreeVisual' else: return u'OrgAOuterHeadingVisual' if not skip_children else u'OrgAOuterTreeVisual' else: vim.command(u'normal! gv'.encode(u'utf-8'))
def set_todo_state(cls, state): u""" Set todo state for buffer. :bufnr: Number of buffer the todo state should be updated for :state: The new todo state """ lineno, colno = vim.current.window.cursor d = ORGMODE.get_document(allow_dirty=True) heading = d.find_current_heading() if not heading: return current_state = heading.todo # set new headline heading.todo = state d.write_heading(heading) # move cursor along with the inserted state only when current position # is in the heading; otherwite do nothing if heading.start_vim == lineno and colno > heading.level: if current_state is not None and \ colno <= heading.level + len(current_state): # the cursor is actually on the todo keyword # move it back to the beginning of the keyword in that case vim.current.window.cursor = (lineno, heading.level + 1) else: # the cursor is somewhere in the text, move it along if current_state is None and state is None: offset = 0 elif current_state is None and state is not None: offset = len(state) + 1 elif current_state is not None and state is None: offset = -len(current_state) - 1 else: offset = len(state) - len(current_state) vim.current.window.cursor = (lineno, colno + offset)
def list_timeline(cls): """ List a timeline of the current buffer to get an overview of the current file. """ raw_agenda = ORGMODE.agenda_manager.get_timestamped_items( [ORGMODE.get_document()]) # create buffer at bottom cmd = [u'setlocal filetype=orgagenda'] cls._switch_to(u'AGENDA', cmd) cls.line2doc = {} # format text of agenda final_agenda = [] for i, h in enumerate(raw_agenda): tmp = u"%s %s" % (h.todo, h.title) final_agenda.append(tmp) cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start) # show agenda vim.current.buffer[:] = [ i.encode(u'utf-8') for i in final_agenda ] vim.command(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'.encode(u'utf-8'))
def i_heading(cls, mode=u'visual', selection=u'inner', skip_children=False): u""" inner heading text object """ heading = ORGMODE.get_document().current_heading() if heading: if selection != u'inner': heading = heading if not heading.parent else heading.parent line_start, col_start = [ int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3] ] line_end, col_end = [ int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3] ] if mode != u'visual': line_start = vim.current.window.cursor[0] line_end = line_start start = line_start end = line_end move_one_character_back = u'' if mode == u'visual' else u'h' if heading.start_vim < line_start: start = heading.start_vim if heading.end_vim > line_end and not skip_children: end = heading.end_vim elif heading.end_of_last_child_vim > line_end and skip_children: end = heading.end_of_last_child_vim if mode != u'visual' and not vim.current.buffer[end - 1]: end -= 1 move_one_character_back = u'' swap_cursor = u'o' if vim.current.window.cursor[ 0] == line_start else u'' if selection == u'inner' and vim.current.window.cursor[ 0] != line_start: h = ORGMODE.get_document().current_heading() if h: heading = h visualmode = vim.eval(u'visualmode()').decode( u'utf-8') if mode == u'visual' else u'v' if line_start == start and line_start != heading.start_vim: if col_start in (0, 1): vim.command((u'normal! %dgg0%s%dgg$%s%s' % \ (start, visualmode, end, move_one_character_back, swap_cursor)).encode(u'utf-8')) else: vim.command((u'normal! %dgg0%dl%s%dgg$%s%s' % \ (start, col_start - 1, visualmode, end, move_one_character_back, swap_cursor)).encode(u'utf-8')) else: vim.command((u'normal! %dgg0%dl%s%dgg$%s%s' % \ (start, heading.level + 1, visualmode, end, move_one_character_back, swap_cursor)).encode(u'utf-8')) if selection == u'inner': if mode == u'visual': return u'OrgInnerHeadingVisual' if not skip_children else u'OrgInnerTreeVisual' else: return u'OrgInnerHeadingOperator' if not skip_children else u'OrgInnerTreeOperator' else: if mode == u'visual': return u'OrgOuterHeadingVisual' if not skip_children else u'OrgOuterTreeVisual' else: return u'OrgOuterHeadingOperator' if not skip_children else u'OrgOuterTreeOperator' elif mode == u'visual': vim.command(u'normal! gv'.encode(u'utf-8'))
def _focus_heading(cls, mode, direction=DIRECTION_FORWARD, skip_children=False): u""" Focus next or previous heading in the given direction :direction: True for next heading, False for previous heading :returns: next heading or None """ d = ORGMODE.get_document() current_heading = d.current_heading() heading = current_heading focus_heading = None # FIXME this is just a piece of really ugly and unmaintainable code. It # should be rewritten if not heading: if direction == DIRECTION_FORWARD and d.headings \ and vim.current.window.cursor[0] < d.headings[0].start_vim: # the cursor is in the meta information are, therefore focus # first heading focus_heading = d.headings[0] if not (heading or focus_heading): if mode == u'visual': # restore visual selection when no heading was found vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No heading found') return elif direction == DIRECTION_BACKWARD: if vim.current.window.cursor[0] != heading.start_vim: # the cursor is in the body of the current heading, therefore # the current heading will be focused if mode == u'visual': line_start, col_start = [ int(i) for i in vim.eval(u'getpos("\'<")'.encode( u'utf-8'))[1:3] ] line_end, col_end = [ int(i) for i in vim.eval(u'getpos("\'>")'.encode( u'utf-8'))[1:3] ] if line_start >= heading.start_vim and line_end > heading.start_vim: focus_heading = heading else: focus_heading = heading # so far no heading has been found that the next focus should be on if not focus_heading: if not skip_children and direction == DIRECTION_FORWARD and heading.children: focus_heading = heading.children[0] elif direction == DIRECTION_FORWARD and heading.next_sibling: focus_heading = heading.next_sibling elif direction == DIRECTION_BACKWARD and heading.previous_sibling: focus_heading = heading.previous_sibling if not skip_children: while focus_heading.children: focus_heading = focus_heading.children[-1] else: if direction == DIRECTION_FORWARD: focus_heading = current_heading.next_heading else: focus_heading = current_heading.previous_heading noheadingfound = False if not focus_heading: if mode in (u'visual', u'operator'): # the cursor seems to be on the last or first heading of this # document and performes another next/previous operation focus_heading = heading noheadingfound = True else: if direction == DIRECTION_FORWARD: echo(u'Already focussing last heading') else: echo(u'Already focussing first heading') return if mode == u'visual': cls._change_visual_selection(current_heading, focus_heading, direction=direction, noheadingfound=noheadingfound) elif mode == u'operator': if direction == DIRECTION_FORWARD and vim.current.window.cursor[ 0] >= focus_heading.start_vim: vim.current.window.cursor = ( focus_heading.end_vim, len(vim.current.buffer[focus_heading.end].decode(u'utf-8')) ) else: vim.current.window.cursor = (focus_heading.start_vim, 0) else: vim.current.window.cursor = (focus_heading.start_vim, focus_heading.level + 1) if noheadingfound: return return focus_heading
def new_heading(cls, below=None, insert_mode=False, end_of_last_child=False): """ :below: True, insert heading below current heading, False, insert heading above current heading, None, special behavior for insert mode, use the current text as heading :insert_mode: True, if action is performed in insert mode :end_of_last_child: True, insert heading at the end of last child, otherwise the newly created heading will "take over" the current heading's children """ d = ORGMODE.get_document() h = d.current_heading() cursor = vim.current.window.cursor[:] if not h: # the user is in meta data region pos = cursor[0] - 1 heading = Heading(body=d.meta_information[pos:]) d.headings.insert(0, heading) del d.meta_information[pos:] d.write() vim.current.window.cursor = (pos + 1, heading.level + 1) return heading = Heading(level=h.level) # it's weird but this is the behavior of original orgmode if below is None: below = cursor[1] != 0 or end_of_last_child heading_insert_position = 0 if below: heading_insert_position = 1 if not end_of_last_child: # append heading at the end of current heading but also take # over the children of current heading heading.children = h.children[:] del h.children # insert newly created heading l = h.get_parent_list() idx = h.get_index_in_parent_list() if l is not None and idx is not None: l.insert(idx + heading_insert_position, heading) else: raise HeadingDomError('Current heading is not properly linked in DOM') d.write() # if cursor is currently on a heading, insert parts of it into the # newly created heading # TODO implement me #if insert_mode and not end_of_last_child and cursor[0] == h.start_vim: # if cursor[1] > h.level: # tmp1 = vim.current.buffer[cursor[0] - 1][:cursor[1]] # tmp2 = vim.current.buffer[cursor[0] - 1][cursor[1]:] # vim.current.buffer[cursor[0] - 1] = tmp1 # else: # tmp2 = u'' # if below: # vim.current.buffer[cursor[0]:cursor[0]] = [(u'%s %s' % (u'*' * level, tmp2.lstrip())).encode(u'utf-8')] # vim.current.window.cursor = (cursor[0] + 1, level + 1) # else: # # this can only happen at column 0 # vim.current.buffer[cursor[0] - 1:cursor[0] - 1] = [(u'%s ' % (u'*' * level, )).encode(u'utf-8')] # vim.current.window.cursor = (cursor[0], level + 1) if insert_mode: vim.command((u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )).encode(u'utf-8')) else: vim.current.window.cursor = (cursor[0], cursor[1] + heading.level + 1) # return newly created heading return heading
def _change_heading_level(cls, level, including_children=True, on_heading=False, insert_mode=False): u""" Change level of heading realtively with or without including children. :level: the number of levels to promote/demote heading :including_children: True if should should be included in promoting/demoting :on_heading: True if promoting/demoting should only happen when the cursor is on the heading :insert_mode: True if vim is in insert mode """ d = ORGMODE.get_document() current_heading = d.current_heading() if not current_heading or on_heading and current_heading.start_vim != vim.current.window.cursor[ 0]: # TODO figure out the actually pressed keybinding and feed these # keys instead of making keys up like this if level > 0: if insert_mode: vim.eval(u'feedkeys("\<C-t>", "n")'.encode(u'utf-8')) elif including_children: vim.eval(u'feedkeys(">]]", "n")'.encode(u'utf-8')) elif on_heading: vim.eval(u'feedkeys(">>", "n")'.encode(u'utf-8')) else: vim.eval(u'feedkeys(">}", "n")'.encode(u'utf-8')) else: if insert_mode: vim.eval(u'feedkeys("\<C-d>", "n")'.encode(u'utf-8')) elif including_children: vim.eval(u'feedkeys("<]]", "n")'.encode(u'utf-8')) elif on_heading: vim.eval(u'feedkeys("<<", "n")'.encode(u'utf-8')) else: vim.eval(u'feedkeys("<}", "n")'.encode(u'utf-8')) # return True because otherwise apply_count will not work return True # don't allow demotion below level 1 if current_heading.level == 1 and level < 1: return False # reduce level of demotion to a minimum heading level of 1 if (current_heading.level + level) < 1: level = 1 def indent(heading, ic): if not heading: return heading.level += level if ic: for child in heading.children: indent(child, ic) # save cursor position c = vim.current.window.cursor[:] # indent the promoted/demoted heading indent_end_vim = current_heading.end_of_last_child_vim if including_children else current_heading.end_vim indent(current_heading, including_children) # when changing the level of a heading, its position in the DOM # needs to be updated. It's likely that the heading gets a new # parent and new children when demoted or promoted # find new parent p = current_heading.parent pl = current_heading.get_parent_list() ps = current_heading.previous_sibling nhl = current_heading.level if level > 0: # demotion # subheading or top level heading if ps and nhl > ps.level: pl.remove(current_heading, taint=False) # find heading that is the new parent heading oh = ps h = ps while nhl > h.level: oh = h if h.children: h = h.children[-1] else: break np = h if nhl > h.level else oh # append current heading to new heading np.children.append(current_heading, taint=False) # if children are not included, distribute them among the # parent heading and it's siblings if not including_children: for h in current_heading.children[:]: if h and h.level <= nhl: cls._append_heading(h, np) current_heading.children.remove(h, taint=False) else: # promotion if p and nhl <= p.level: idx = current_heading.get_index_in_parent_list() + 1 # find the new parent heading oh = p h = p while nhl <= h.level: # append new children to current heading for child in h.children[idx:]: cls._append_heading(child, current_heading) h.children.remove_slice(idx, len(h.children), taint=False) idx = h.get_index_in_parent_list() + 1 if h.parent: h = h.parent else: break ns = oh.next_sibling while ns and ns.level > current_heading.level: nns = ns.next_sibling cls._append_heading(ns, current_heading) ns = nns # append current heading to new parent heading / document pl.remove(current_heading, taint=False) if nhl > h.level: h.children.insert(idx, current_heading, taint=False) else: d.headings.insert(idx, current_heading, taint=False) d.write() if indent_end_vim != current_heading.start_vim: vim.command( (u'normal %dggV%dgg=' % (current_heading.start_vim, indent_end_vim)).encode(u'utf-8')) # restore cursor position vim.current.window.cursor = (c[0], c[1] + level) return True
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_folding(cls): u""" Toggle folding similar to the way orgmode does This is just a convenience function, don't hesitate to use the z* keybindings vim offers to deal with folding! """ d = ORGMODE.get_document() heading = d.current_heading() if not heading: vim.eval(u'feedkeys("<Tab>", "n")'.encode(u'utf-8')) return cursor = vim.current.window.cursor[:] if int(vim.eval((u'foldclosed(%d)' % heading.start_vim).encode(u'utf-8'))) != -1: # open closed fold p = heading.number_of_parents if not p: p = heading.level vim.command((u'normal %dzo' % p).encode(u'utf-8')) vim.current.window.cursor = cursor return heading found_fold = False open_depth = 0 def fold_depth(h): if int(vim.eval((u'foldclosed(%d)' % h.start_vim).encode(u'utf-8'))) != -1: return (h.number_of_parents, True) else: res = [h.number_of_parents + 1] found = False for c in h.children: d, f = fold_depth(c) res.append(d) found |= f return (max(res), found) def open_fold(h): if h.number_of_parents <= open_depth: vim.command((u'normal %dgg%dzo' % (h.start_vim, open_depth)).encode(u'utf-8')) if h.children: for c in h.children: open_fold(c) # find deepest fold open_depth, found_fold = fold_depth(heading) # recursively open folds for child in heading.children: # find deepest fold if found_fold: open_fold(child) if not found_fold: vim.command((u'%d,%dfoldclose!' % (heading.start_vim, heading.end_of_last_child_vim)).encode(u'utf-8')) if heading.number_of_parents: # restore cursor position, it might have been changed by open_fold vim.current.window.cursor = cursor p = heading.number_of_parents if not p: p = heading.level # reopen fold again beacause the former closing of the fold closed all levels, including parents! vim.command((u'normal %dzo' % (p, )).encode(u'utf-8')) # restore cursor position vim.current.window.cursor = cursor return heading
def _focus_heading(cls, mode, direction=DIRECTION_FORWARD, skip_children=False): u""" Focus next or previous heading in the given direction :direction: True for next heading, False for previous heading :returns: next heading or None """ d = ORGMODE.get_document() current_heading = d.current_heading() heading = current_heading focus_heading = None if not heading: if direction == DIRECTION_FORWARD and d.headings \ and vim.current.window.cursor[0] < d.headings[0].start_vim: focus_heading = d.headings[0] if not (heading or focus_heading): if mode == u'visual': # restore visual selection when no heading was found vim.command(u'normal gv'.encode(u'utf-8')) else: echo(u'No heading found') return elif direction == DIRECTION_BACKWARD: if vim.current.window.cursor[0] != heading.start_vim: if mode == u'visual': # TODO maybe this has to be changed! line_start, col_start = [ int(i) for i in vim.eval(u'getpos("\'<")'.encode( u'utf-8'))[1:3] ] line_end, col_end = [ int(i) for i in vim.eval(u'getpos("\'>")'.encode( u'utf-8'))[1:3] ] if line_start >= heading.start_vim and line_end > heading.start_vim: focus_heading = heading else: focus_heading = heading if not focus_heading: if not skip_children and direction == DIRECTION_FORWARD and heading.children: focus_heading = heading.children[0] elif direction == DIRECTION_FORWARD and heading.next_sibling: focus_heading = heading.next_sibling elif direction == DIRECTION_BACKWARD and heading.previous_sibling: focus_heading = heading.previous_sibling if not skip_children: while focus_heading.children: focus_heading = focus_heading.children[-1] else: while heading.level > 1: if heading.parent: if direction == DIRECTION_FORWARD and heading.parent.next_sibling: focus_heading = heading.parent.next_sibling break elif direction == DIRECTION_BACKWARD: focus_heading = heading.parent break else: heading = heading.parent else: break noheadingfound = False if not focus_heading: if mode in (u'visual', u'operator'): # the cursor seems to be on the last or first heading of this # document and performes another next/previous-operation focus_heading = heading noheadingfound = True else: if direction == DIRECTION_FORWARD: echo(u'Already focussing last heading') else: echo(u'Already focussing first heading') return if mode == u'visual': cls._change_visual_selection(current_heading, focus_heading, mode, direction=direction, noheadingfound=noheadingfound) elif mode == u'operator': if direction == DIRECTION_FORWARD and vim.current.window.cursor[ 0] >= focus_heading.start_vim: vim.current.window.cursor = ( focus_heading.end_vim, len(vim.current.buffer[focus_heading.end])) else: vim.current.window.cursor = (focus_heading.start_vim, 0) else: vim.current.window.cursor = (focus_heading.start_vim, focus_heading.level + 1) if noheadingfound: return return focus_heading
def toggle_folding(cls, reverse=False): u""" Toggle folding similar to the way orgmode does This is just a convenience function, don't hesitate to use the z* keybindings vim offers to deal with folding! :reverse: If False open folding by one level otherwise close it by one. """ d = ORGMODE.get_document() heading = d.current_heading() if not heading: vim.eval(u'feedkeys("<Tab>", "n")'.encode(u'utf-8')) return cursor = vim.current.window.cursor[:] if int(vim.eval((u'foldclosed(%d)' % heading.start_vim).encode(u'utf-8'))) != -1: if not reverse: # open closed fold p = heading.number_of_parents if not p: p = heading.level vim.command((u'normal! %dzo' % p).encode(u'utf-8')) else: # reverse folding opens all folds under the cursor vim.command((u'%d,%dfoldopen!' % (heading.start_vim, heading.end_of_last_child_vim)).encode(u'utf-8')) vim.current.window.cursor = cursor return heading def open_fold(h): if h.number_of_parents <= open_depth: vim.command((u'normal! %dgg%dzo' % (h.start_vim, open_depth)).encode(u'utf-8')) for c in h.children: open_fold(c) def close_fold(h): for c in h.children: close_fold(c) if h.number_of_parents >= open_depth - 1 and \ int(vim.eval((u'foldclosed(%d)' % h.start_vim).encode(u'utf-8'))) == -1: vim.command((u'normal! %dggzc' % (h.start_vim, )).encode(u'utf-8')) # find deepest fold open_depth, found_fold = cls._fold_depth(heading) if not reverse: # recursively open folds if found_fold: for child in heading.children: open_fold(child) else: vim.command((u'%d,%dfoldclose!' % (heading.start_vim, heading.end_of_last_child_vim)).encode(u'utf-8')) if heading.number_of_parents: # restore cursor position, it might have been changed by open_fold vim.current.window.cursor = cursor p = heading.number_of_parents if not p: p = heading.level # reopen fold again beacause the former closing of the fold closed all levels, including parents! vim.command((u'normal! %dzo' % (p, )).encode(u'utf-8')) else: # close the last level of folds close_fold(heading) # restore cursor position vim.current.window.cursor = cursor return heading
def new_heading(cls, below=None, insert_mode=False, end_of_last_child=False): u""" :below: True, insert heading below current heading, False, insert heading above current heading, None, special behavior for insert mode, use the current text as heading :insert_mode: True, if action is performed in insert mode :end_of_last_child: True, insert heading at the end of last child, otherwise the newly created heading will "take over" the current heading's children """ d = ORGMODE.get_document() current_heading = d.current_heading() cursor = vim.current.window.cursor[:] if not current_heading: # the user is in meta data region pos = cursor[0] - 1 heading = Heading(title=d.meta_information[pos], body=d.meta_information[pos + 1:]) d.headings.insert(0, heading) del d.meta_information[pos:] d.write() vim.command((u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )).encode(u'utf-8')) return heading heading = Heading(level=current_heading.level) # it's weird but this is the behavior of original orgmode if below is None: below = cursor[1] != 0 or end_of_last_child # insert newly created heading l = current_heading.get_parent_list() idx = current_heading.get_index_in_parent_list() if l is not None and idx is not None: l.insert(idx + (1 if below else 0), heading) else: raise HeadingDomError( u'Current heading is not properly linked in DOM') if below and not end_of_last_child: # append heading at the end of current heading and also take # over the children of current heading for child in current_heading.children: heading.children.append(child, taint=False) current_heading.children.remove_slice(0, len(current_heading.children), \ taint=False) # if cursor is currently on a heading, insert parts of it into the # newly created heading if insert_mode and cursor[1] != 0 and cursor[ 0] == current_heading.start_vim: offset = cursor[1] - current_heading.level - 1 - (len(current_heading.todo) \ + 1 if current_heading.todo else 0) if offset < 0: offset = 0 if int(settings.get(u'org_improve_split_heading', u'1')) and \ offset > 0 and len(current_heading.title) == offset + 1 \ and current_heading.title[offset - 1] not in (u' ', u'\t'): offset += 1 heading.title = current_heading.title[offset:] current_heading.title = current_heading.title[:offset] heading.body = current_heading.body[:] current_heading.body = [] d.write() vim.command((u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )).encode(u'utf-8')) # return newly created heading return heading