From fdca6a609991111a8c3d68e5501de2300d4fab7b Mon Sep 17 00:00:00 2001 From: gryf Date: Wed, 29 Jun 2011 18:02:58 +0200 Subject: [PATCH] Updated gundo and buffergator. Corrected gui/terminal behaviour. --- .vimrc | 24 +- GetLatest/GetLatestVimScripts.dat | 4 +- doc/buffergator.txt | 29 +- doc/gundo.txt | 4 +- plugin/buffergator.vim | 51 ++- plugin/gundo.py | 573 ++++++++++++++++++++++++++++ plugin/gundo.vim | 614 ++---------------------------- pol.utf8.add.spl | Bin 86 -> 87 bytes 8 files changed, 682 insertions(+), 617 deletions(-) create mode 100644 plugin/gundo.py diff --git a/.vimrc b/.vimrc index 7c4a671..32a78d7 100644 --- a/.vimrc +++ b/.vimrc @@ -89,9 +89,9 @@ autocmd BufRead *.tmux.conf set filetype=tmux "}}} "TERMINAL: options for terminal emulators {{{ -if $TERM == 'rxvt-unicode' || $TERM == 'xterm' - set term=rxvt-unicode256 "Set terminal type - set t_Co=256 "Enable 256 colors support +if $TERM == 'rxvt-unicode-256color' || $TERM == 'xterm' + "Enable 256 colors support + set t_Co=256 "repair urxvt ctrl+pgup/down behaviour map [5^ map [6^ @@ -197,12 +197,6 @@ map :BuffergatorToggle map :bp map :bn -"Cycle through tabs. -if $TERM == 'rxvt-unicode' - map :tabn - map :tabp -endif - map :call Make() "QuickFix jumps @@ -322,7 +316,8 @@ function! OpenInFirefox() endfunction "}}} -" GUI: here goes all the gvim customizations {{{ +" GUI: detect graphics mode, set colorscheme {{{ +colorscheme wombat256grf if has('gui_running') "set guifont=Consolas\ 12 "I like this font, but it looks like crap on linux "set guifont=Consolas\ 13 "Let's try again @@ -336,14 +331,9 @@ if has('gui_running') nmenu 666 PopUp.&Open\ in\ browser :call OpenInFirefox() "Turn off annoying beep au GUIEnter * set vb t_vb= -endif -"}}} -" HIGHLIGHT: colorscheme and highlight, which should be applied on after {{{ -" some vim initialization -if $TERM == 'linux' +elseif $TERM == 'linux' + " fallback to basic 8-color colorscheme colorscheme pablo -else - colorscheme wombat256grf endif "}}} " vim:ts=4:sw=4:wrap:fdm=marker: diff --git a/GetLatest/GetLatestVimScripts.dat b/GetLatest/GetLatestVimScripts.dat index a641808..f01d7ac 100644 --- a/GetLatest/GetLatestVimScripts.dat +++ b/GetLatest/GetLatestVimScripts.dat @@ -1,11 +1,11 @@ ScriptID SourceID Filename -------------------------- ### plugins -3619 15879 buffergator +3619 16002 buffergator 102 13435 DirDiff.vim 1984 13961 :AutoInstall: FuzzyFinder 311 7645 grep.vim -3304 15744 gundo.vim +3304 15996 gundo.vim 2727 11120 jsbeautify.vim 3252 13948 :AutoInstall: L9 2289 8922 loremipsum diff --git a/doc/buffergator.txt b/doc/buffergator.txt index 0af3d3e..ba8e1b6 100644 --- a/doc/buffergator.txt +++ b/doc/buffergator.txt @@ -102,15 +102,38 @@ the catalog viewer. O, go Preview the currently-selected buffer in the previous window. -S, gs Preview the currently-selected buffer is a new split. -V, gv Preview the currently-selected buffer is a new vertical - split +S, gs Preview the currently-selected buffer is a new vertical + split. +I, gi Preview the currently-selected buffer is a new split T Preview the currently-selected buffer is a new tab page. , Go to the next buffer entry and preview it in the previous window. , Go to the previous buffer entry and preview it in the previous window. + +------------------------------------------------------------------------------- +Go to Existing Viewport Showing Buffer~ + +The following keys will try to find the selected buffer in an existing +viewport (whether on the current tab page or another). + +eo If currently-selected buffer is showing in an existing + viewport on this or any other tab page, go it it; + otherwise show it in the previous window. +es If currently-selected buffer is showing in an existing + viewport on this or any other tab page, go it it; + otherwise show it in a new vertical split. +ei If currently-selected buffer is showing in an existing + viewport on this or any other tab page, go it it; + otherwise show it in a new horizontal split. +et If currently-selected buffer is showing in an existing + viewport on this or any other tab page, go it it; + otherwise show it in a new tab page. +E If currently-selected buffer is showing in an existing + viewport on this or any other tab page, go it it; + otherwise do nothing. + ------------------------------------------------------------------------------- Window Control~ diff --git a/doc/gundo.txt b/doc/gundo.txt index 3706536..d4982b1 100644 --- a/doc/gundo.txt +++ b/doc/gundo.txt @@ -1,6 +1,6 @@ *gundo.txt* Graph your undo tree so you can actually USE it. -Making's Vim's undo tree usable by humans. +Making Vim's undo tree usable by humans. ============================================================================== CONTENTS *Gundo-contents* @@ -219,6 +219,8 @@ GitHub: http://github.com/sjl/gundo.vim/ ============================================================================== 7. Changelog *GundoChangelog* +v2.2.1 + * Refactoring and performance improvements. v2.2.0 * Add the g:gundo_close_on_revert setting. * Fix a bug with the splitbelow setting. diff --git a/plugin/buffergator.vim b/plugin/buffergator.vim index 00be22b..43982ca 100644 --- a/plugin/buffergator.vim +++ b/plugin/buffergator.vim @@ -88,7 +88,7 @@ let s:buffergator_viewport_split_modes = { \ "T" : "topleft sbuffer", \ "t" : "leftabove sbuffer", \ "B" : "botright sbuffer", - \ "b" : "rightbelow", + \ "b" : "rightbelow sbuffer", \ } " 2}}} @@ -463,6 +463,8 @@ function! s:NewCatalogViewer() " Opens the buffer for viewing, creating it if needed. " First argument, if given, should be number of calling buffer. + " Second argument, if given, should be false if the buffers info is *not* + " to be repopulated; defaults to 1 function! l:catalog_viewer.open(...) dict " store calling buffer @@ -473,7 +475,9 @@ function! s:NewCatalogViewer() endif " populate data - call self.update_buffers_info() + if (a:0 < 2 || a:2) "|| b:buffergator_catalog_viewer != self + call self.update_buffers_info() + endif " get buffer number of the catalog view buffer, creating it if neccessary if self.bufnum < 0 || !bufexists(self.bufnum) " create and render a new buffer @@ -663,6 +667,13 @@ function! s:NewCatalogViewer() noremap :call b:buffergator_catalog_viewer.goto_index_entry("n", 1, 1) noremap :call b:buffergator_catalog_viewer.goto_index_entry("p", 1, 1) + """"" Preview: go to existing window showing target + noremap E :call b:buffergator_catalog_viewer.visit_open_target(1, !g:buffergator_autodismiss_on_select, "") + noremap eo :call b:buffergator_catalog_viewer.visit_open_target(0, !g:buffergator_autodismiss_on_select, "") + noremap es :call b:buffergator_catalog_viewer.visit_open_target(0, !g:buffergator_autodismiss_on_select, "vert sb") + noremap ei :call b:buffergator_catalog_viewer.visit_open_target(0, !g:buffergator_autodismiss_on_select, "sb") + noremap et :call b:buffergator_catalog_viewer.visit_open_target(0, !g:buffergator_autodismiss_on_select, "tab sb") + else """" Catalog management @@ -1003,6 +1014,41 @@ function! s:NewCatalogViewer() call s:_buffergator_messenger.send_info(expand(bufname(l:jump_to_bufnum))) endfunction + " Go to the selected buffer, preferentially using a window that already is + " showing it; if not, create a window using split_cmd + function! l:catalog_viewer.visit_open_target(unconditional, keep_catalog, split_cmd) dict + let l:cur_line = line(".") + if !has_key(l:self.jump_map, l:cur_line) + call s:_buffergator_messenger.send_info("Not a valid navigation line") + return 0 + endif + let [l:jump_to_bufnum] = self.jump_map[l:cur_line].target + let wnr = bufwinnr(l:jump_to_bufnum) + if wnr != -1 + execute(wnr . "wincmd w") + if !a:keep_catalog + call self.close() + endif + return + endif + let l:cur_tab_num = tabpagenr() + for tabnum in range(1, tabpagenr('$')) + execute("tabnext " . tabnum) + let wnr = bufwinnr(l:jump_to_bufnum) + if wnr != -1 + execute(wnr . "wincmd w") + if !a:keep_catalog + call self.close() + endif + return + endif + endfor + execute("tabnext " . l:cur_tab_num) + if !a:unconditional + call self.visit_target(a:keep_catalog, 0, a:split_cmd) + endif + endfunction + function! l:catalog_viewer.delete_target(wipe, force) dict let l:cur_line = line(".") if !has_key(l:self.jump_map, l:cur_line) @@ -1171,7 +1217,6 @@ function! s:NewCatalogViewer() " Rebuilds catalog. function! l:catalog_viewer.rebuild_catalog() dict - call self.update_buffers_info() call self.open(1) endfunction diff --git a/plugin/gundo.py b/plugin/gundo.py new file mode 100644 index 0000000..84e079a --- /dev/null +++ b/plugin/gundo.py @@ -0,0 +1,573 @@ +# ============================================================================ +# File: gundo.py +# Description: vim global plugin to visualize your undo tree +# Maintainer: Steve Losh +# License: GPLv2+ -- look it up. +# Notes: Much of this code was thiefed from Mercurial, and the rest was +# heavily inspired by scratch.vim and histwin.vim. +# +# ============================================================================ + +import difflib +import itertools +import sys +import time +import vim + +# Mercurial's graphlog code +def asciiedges(seen, rev, parents): + """adds edge info to changelog DAG walk suitable for ascii()""" + if rev not in seen: + seen.append(rev) + nodeidx = seen.index(rev) + + knownparents = [] + newparents = [] + for parent in parents: + if parent in seen: + knownparents.append(parent) + else: + newparents.append(parent) + + ncols = len(seen) + seen[nodeidx:nodeidx + 1] = newparents + edges = [(nodeidx, seen.index(p)) for p in knownparents] + + if len(newparents) > 0: + edges.append((nodeidx, nodeidx)) + if len(newparents) > 1: + edges.append((nodeidx, nodeidx + 1)) + + nmorecols = len(seen) - ncols + return nodeidx, edges, ncols, nmorecols + +def get_nodeline_edges_tail( + node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): + if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: + # Still going in the same non-vertical direction. + if n_columns_diff == -1: + start = max(node_index + 1, p_node_index) + tail = ["|", " "] * (start - node_index - 1) + tail.extend(["/", " "] * (n_columns - start)) + return tail + else: + return ["\\", " "] * (n_columns - node_index - 1) + else: + return ["|", " "] * (n_columns - node_index - 1) + +def draw_edges(edges, nodeline, interline): + for (start, end) in edges: + if start == end + 1: + interline[2 * end + 1] = "/" + elif start == end - 1: + interline[2 * start + 1] = "\\" + elif start == end: + interline[2 * start] = "|" + else: + nodeline[2 * end] = "+" + if start > end: + (start, end) = (end, start) + for i in range(2 * start + 1, 2 * end): + if nodeline[i] != "+": + nodeline[i] = "-" + +def fix_long_right_edges(edges): + for (i, (start, end)) in enumerate(edges): + if end > start: + edges[i] = (start, end + 1) + +def ascii(buf, state, type, char, text, coldata): + """prints an ASCII graph of the DAG + + takes the following arguments (one call per node in the graph): + + - Somewhere to keep the needed state in (init to asciistate()) + - Column of the current node in the set of ongoing edges. + - Type indicator of node data == ASCIIDATA. + - Payload: (char, lines): + - Character to use as node's symbol. + - List of lines to display as the node's text. + - Edges; a list of (col, next_col) indicating the edges between + the current node and its parents. + - Number of columns (ongoing edges) in the current revision. + - The difference between the number of columns (ongoing edges) + in the next revision and the number of columns (ongoing edges) + in the current revision. That is: -1 means one column removed; + 0 means no columns added or removed; 1 means one column added. + """ + + idx, edges, ncols, coldiff = coldata + assert -2 < coldiff < 2 + if coldiff == -1: + # Transform + # + # | | | | | | + # o | | into o---+ + # |X / |/ / + # | | | | + fix_long_right_edges(edges) + + # add_padding_line says whether to rewrite + # + # | | | | | | | | + # | o---+ into | o---+ + # | / / | | | # <--- padding line + # o | | | / / + # o | | + add_padding_line = (len(text) > 2 and coldiff == -1 and + [x for (x, y) in edges if x + 1 < y]) + + # fix_nodeline_tail says whether to rewrite + # + # | | o | | | | o | | + # | | |/ / | | |/ / + # | o | | into | o / / # <--- fixed nodeline tail + # | |/ / | |/ / + # o | | o | | + fix_nodeline_tail = len(text) <= 2 and not add_padding_line + + # nodeline is the line containing the node character (typically o) + nodeline = ["|", " "] * idx + nodeline.extend([char, " "]) + + nodeline.extend( + get_nodeline_edges_tail(idx, state[1], ncols, coldiff, + state[0], fix_nodeline_tail)) + + # shift_interline is the line containing the non-vertical + # edges between this entry and the next + shift_interline = ["|", " "] * idx + if coldiff == -1: + n_spaces = 1 + edge_ch = "/" + elif coldiff == 0: + n_spaces = 2 + edge_ch = "|" + else: + n_spaces = 3 + edge_ch = "\\" + shift_interline.extend(n_spaces * [" "]) + shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) + + # draw edges from the current node to its parents + draw_edges(edges, nodeline, shift_interline) + + # lines is the list of all graph lines to print + lines = [nodeline] + if add_padding_line: + lines.append(get_padding_line(idx, ncols, edges)) + lines.append(shift_interline) + + # make sure that there are as many graph lines as there are + # log strings + while len(text) < len(lines): + text.append("") + if len(lines) < len(text): + extra_interline = ["|", " "] * (ncols + coldiff) + while len(lines) < len(text): + lines.append(extra_interline) + + # print lines + indentation_level = max(ncols, ncols + coldiff) + for (line, logstr) in zip(lines, text): + ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) + buf.write(ln.rstrip() + '\n') + + # ... and start over + state[0] = coldiff + state[1] = idx + +def generate(dag, edgefn, current): + seen, state = [], [0, 0] + buf = Buffer() + for node, parents in list(dag): + if node.time: + age_label = age(int(node.time)) + else: + age_label = 'Original' + line = '[%s] %s' % (node.n, age_label) + if node.n == current: + char = '@' + else: + char = 'o' + ascii(buf, state, 'C', char, [line], edgefn(seen, node, parents)) + return buf.b + +# Mercurial age function + +agescales = [("year", 3600 * 24 * 365), + ("month", 3600 * 24 * 30), + ("week", 3600 * 24 * 7), + ("day", 3600 * 24), + ("hour", 3600), + ("minute", 60), + ("second", 1)] + +def age(ts): + '''turn a timestamp into an age string.''' + + def plural(t, c): + if c == 1: + return t + return t + "s" + def fmt(t, c): + return "%d %s" % (c, plural(t, c)) + + now = time.time() + then = ts + if then > now: + return 'in the future' + + delta = max(1, int(now - then)) + if delta > agescales[0][1] * 2: + return time.strftime('%Y-%m-%d', time.gmtime(float(ts))) + + for t, s in agescales: + n = delta // s + if n >= 2 or s == 1: + return '%s ago' % fmt(t, n) + +# Python Vim utility functions + +normal = lambda s: vim.command('normal %s' % s) + +MISSING_BUFFER = "Cannot find Gundo's target buffer (%s)" +MISSING_WINDOW = "Cannot find window (%s) for Gundo's target buffer (%s)" + +def _check_sanity(): + '''Check to make sure we're not crazy. + + Does the following things: + + * Make sure the target buffer still exists. + ''' + b = int(vim.eval('g:gundo_target_n')) + + if not vim.eval('bufloaded(%d)' % b): + vim.command('echo "%s"' % (MISSING_BUFFER % b)) + return False + + w = int(vim.eval('bufwinnr(%d)' % b)) + if w == -1: + vim.command('echo "%s"' % (MISSING_WINDOW % (w, b))) + return False + + return True + +def _goto_window_for_buffer(b): + w = int(vim.eval('bufwinnr(%d)' % int(b))) + vim.command('%dwincmd w' % w) + +def _goto_window_for_buffer_name(bn): + b = vim.eval('bufnr("%s")' % bn) + return _goto_window_for_buffer(b) + +def _undo_to(n): + n = int(n) + if n == 0: + vim.command('silent earlier %s' % (int(vim.eval('&undolevels')) + 1)) + else: + vim.command('silent undo %d' % n) + + +INLINE_HELP = '''\ +" Gundo for %s (%d) +" j/k - move between undo states +" p - preview diff of selected and current states +" - revert to selected state + +''' + +# Python undo tree data structures and functions + +class Buffer(object): + def __init__(self): + self.b = '' + + def write(self, s): + self.b += s + +class Node(object): + def __init__(self, n, parent, time, curhead): + self.n = int(n) + self.parent = parent + self.children = [] + self.curhead = curhead + self.time = time + +def _make_nodes(alts, nodes, parent=None): + p = parent + + for alt in alts: + curhead = 'curhead' in alt + node = Node(n=alt['seq'], parent=p, time=alt['time'], curhead=curhead) + nodes.append(node) + if alt.get('alt'): + _make_nodes(alt['alt'], nodes, p) + p = node + +def make_nodes(): + ut = vim.eval('undotree()') + entries = ut['entries'] + + root = Node(0, None, False, 0) + nodes = [] + _make_nodes(entries, nodes, root) + nodes.append(root) + nmap = dict((node.n, node) for node in nodes) + return nodes, nmap + +def changenr(nodes): + _curhead_l = list(itertools.dropwhile(lambda n: not n.curhead, nodes)) + if _curhead_l: + current = _curhead_l[0].parent.n + else: + current = int(vim.eval('changenr()')) + return current + +# Gundo rendering + +# Rendering utility functions + +def _fmt_time(t): + return time.strftime('%Y-%m-%d %I:%M:%S %p', time.localtime(float(t))) + +def _output_preview_text(lines): + _goto_window_for_buffer_name('__Gundo_Preview__') + vim.command('setlocal modifiable') + vim.current.buffer[:] = lines + vim.command('setlocal nomodifiable') + +def _generate_preview_diff(current, node_before, node_after): + _goto_window_for_buffer(vim.eval('g:gundo_target_n')) + + if not node_after.n: # we're at the original file + before_lines = [] + + _undo_to(0) + after_lines = vim.current.buffer[:] + + before_name = 'n/a' + before_time = '' + after_name = 'Original' + after_time = '' + elif not node_before.n: # we're at a pseudo-root state + _undo_to(0) + before_lines = vim.current.buffer[:] + + _undo_to(node_after.n) + after_lines = vim.current.buffer[:] + + before_name = 'Original' + before_time = '' + after_name = node_after.n + after_time = _fmt_time(node_after.time) + else: + _undo_to(node_before.n) + before_lines = vim.current.buffer[:] + + _undo_to(node_after.n) + after_lines = vim.current.buffer[:] + + before_name = node_before.n + before_time = _fmt_time(node_before.time) + after_name = node_after.n + after_time = _fmt_time(node_after.time) + + _undo_to(current) + + return list(difflib.unified_diff(before_lines, after_lines, + before_name, after_name, + before_time, after_time)) + +def _generate_change_preview_diff(current, node_before, node_after): + _goto_window_for_buffer(vim.eval('g:gundo_target_n')) + + _undo_to(node_before.n) + before_lines = vim.current.buffer[:] + + _undo_to(node_after.n) + after_lines = vim.current.buffer[:] + + before_name = node_before.n or 'Original' + before_time = node_before.time and _fmt_time(node_before.time) or '' + after_name = node_after.n or 'Original' + after_time = node_after.time and _fmt_time(node_after.time) or '' + + _undo_to(current) + + return list(difflib.unified_diff(before_lines, after_lines, + before_name, after_name, + before_time, after_time)) + +def GundoRenderGraph(): + if not _check_sanity(): + return + + nodes, nmap = make_nodes() + + for node in nodes: + node.children = [n for n in nodes if n.parent == node] + + def walk_nodes(nodes): + for node in nodes: + if node.parent: + yield (node, [node.parent]) + else: + yield (node, []) + + dag = sorted(nodes, key=lambda n: int(n.n), reverse=True) + current = changenr(nodes) + + result = generate(walk_nodes(dag), asciiedges, current).rstrip().splitlines() + result = [' ' + l for l in result] + + target = (vim.eval('g:gundo_target_f'), int(vim.eval('g:gundo_target_n'))) + + if int(vim.eval('g:gundo_help')): + header = (INLINE_HELP % target).splitlines() + else: + header = [] + + vim.command('call s:GundoOpenGraph()') + vim.command('setlocal modifiable') + vim.current.buffer[:] = (header + result) + vim.command('setlocal nomodifiable') + + i = 1 + for line in result: + try: + line.split('[')[0].index('@') + i += 1 + break + except ValueError: + pass + i += 1 + vim.command('%d' % (i+len(header)-1)) + +def GundoRenderPreview(): + if not _check_sanity(): + return + + target_state = vim.eval('s:GundoGetTargetState()') + + # Check that there's an undo state. There may not be if we're talking about + # a buffer with no changes yet. + if target_state == None: + _goto_window_for_buffer_name('__Gundo__') + return + else: + target_state = int(target_state) + + _goto_window_for_buffer(vim.eval('g:gundo_target_n')) + + nodes, nmap = make_nodes() + current = changenr(nodes) + + node_after = nmap[target_state] + node_before = node_after.parent + + vim.command('call s:GundoOpenPreview()') + _output_preview_text(_generate_preview_diff(current, node_before, node_after)) + + _goto_window_for_buffer_name('__Gundo__') + +def GundoRenderChangePreview(): + if not _check_sanity(): + return + + target_state = vim.eval('s:GundoGetTargetState()') + + # Check that there's an undo state. There may not be if we're talking about + # a buffer with no changes yet. + if target_state == None: + _goto_window_for_buffer_name('__Gundo__') + return + else: + target_state = int(target_state) + + _goto_window_for_buffer(vim.eval('g:gundo_target_n')) + + nodes, nmap = make_nodes() + current = changenr(nodes) + + node_after = nmap[target_state] + node_before = nmap[current] + + vim.command('call s:GundoOpenPreview()') + _output_preview_text(_generate_change_preview_diff(current, node_before, node_after)) + + _goto_window_for_buffer_name('__Gundo__') + +# Gundo undo/redo + +def GundoRevert(): + if not _check_sanity(): + return + + target_n = int(vim.eval('s:GundoGetTargetState()')) + back = vim.eval('g:gundo_target_n') + + _goto_window_for_buffer(back) + _undo_to(target_n) + + vim.command('GundoRenderGraph') + _goto_window_for_buffer(back) + + if int(vim.eval('g:gundo_close_on_revert')): + vim.command('GundoToggle') + +def GundoPlayTo(): + if not _check_sanity(): + return + + target_n = int(vim.eval('s:GundoGetTargetState()')) + back = int(vim.eval('g:gundo_target_n')) + + vim.command('echo "%s"' % back) + + _goto_window_for_buffer(back) + normal('zR') + + nodes, nmap = make_nodes() + + start = nmap[changenr(nodes)] + end = nmap[target_n] + + def _walk_branch(origin, dest): + rev = origin.n < dest.n + + nodes = [] + if origin.n > dest.n: + current, final = origin, dest + else: + current, final = dest, origin + + while current.n >= final.n: + if current.n == final.n: + break + nodes.append(current) + current = current.parent + else: + return None + nodes.append(current) + + return reversed(nodes) if rev else nodes + + branch = _walk_branch(start, end) + + if not branch: + vim.command('unsilent echo "No path to that node from here!"') + return + + for node in branch: + _undo_to(node.n) + vim.command('GundoRenderGraph') + normal('zz') + _goto_window_for_buffer(back) + vim.command('redraw') + vim.command('sleep 60m') + +def initPythonModule(): + if sys.version_info[:2] < (2, 4): + vim.command('let s:has_supported_python = 0') diff --git a/plugin/gundo.vim b/plugin/gundo.vim index 29f1e9b..5d6d9d7 100644 --- a/plugin/gundo.vim +++ b/plugin/gundo.vim @@ -26,13 +26,6 @@ endif"}}} if has('python')"{{{ let s:has_supported_python = 1 - -python << ENDPYTHON -import sys -import vim -if sys.version_info[:2] < (2, 4): - vim.command('let s:has_supported_python = 0') -ENDPYTHON else let s:has_supported_python = 0 endif @@ -45,6 +38,8 @@ if !s:has_supported_python finish endif"}}} +let s:plugin_path = escape(expand(':p:h'), '\') + if !exists('g:gundo_width')"{{{ let g:gundo_width = 45 endif"}}} @@ -72,332 +67,6 @@ endif"}}} "}}} -"{{{ Mercurial's graphlog code -python << ENDPYTHON -def asciiedges(seen, rev, parents): - """adds edge info to changelog DAG walk suitable for ascii()""" - if rev not in seen: - seen.append(rev) - nodeidx = seen.index(rev) - - knownparents = [] - newparents = [] - for parent in parents: - if parent in seen: - knownparents.append(parent) - else: - newparents.append(parent) - - ncols = len(seen) - seen[nodeidx:nodeidx + 1] = newparents - edges = [(nodeidx, seen.index(p)) for p in knownparents] - - if len(newparents) > 0: - edges.append((nodeidx, nodeidx)) - if len(newparents) > 1: - edges.append((nodeidx, nodeidx + 1)) - - nmorecols = len(seen) - ncols - return nodeidx, edges, ncols, nmorecols - -def get_nodeline_edges_tail( - node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): - if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: - # Still going in the same non-vertical direction. - if n_columns_diff == -1: - start = max(node_index + 1, p_node_index) - tail = ["|", " "] * (start - node_index - 1) - tail.extend(["/", " "] * (n_columns - start)) - return tail - else: - return ["\\", " "] * (n_columns - node_index - 1) - else: - return ["|", " "] * (n_columns - node_index - 1) - -def draw_edges(edges, nodeline, interline): - for (start, end) in edges: - if start == end + 1: - interline[2 * end + 1] = "/" - elif start == end - 1: - interline[2 * start + 1] = "\\" - elif start == end: - interline[2 * start] = "|" - else: - nodeline[2 * end] = "+" - if start > end: - (start, end) = (end, start) - for i in range(2 * start + 1, 2 * end): - if nodeline[i] != "+": - nodeline[i] = "-" - -def fix_long_right_edges(edges): - for (i, (start, end)) in enumerate(edges): - if end > start: - edges[i] = (start, end + 1) - -def ascii(buf, state, type, char, text, coldata): - """prints an ASCII graph of the DAG - - takes the following arguments (one call per node in the graph): - - - Somewhere to keep the needed state in (init to asciistate()) - - Column of the current node in the set of ongoing edges. - - Type indicator of node data == ASCIIDATA. - - Payload: (char, lines): - - Character to use as node's symbol. - - List of lines to display as the node's text. - - Edges; a list of (col, next_col) indicating the edges between - the current node and its parents. - - Number of columns (ongoing edges) in the current revision. - - The difference between the number of columns (ongoing edges) - in the next revision and the number of columns (ongoing edges) - in the current revision. That is: -1 means one column removed; - 0 means no columns added or removed; 1 means one column added. - """ - - idx, edges, ncols, coldiff = coldata - assert -2 < coldiff < 2 - if coldiff == -1: - # Transform - # - # | | | | | | - # o | | into o---+ - # |X / |/ / - # | | | | - fix_long_right_edges(edges) - - # add_padding_line says whether to rewrite - # - # | | | | | | | | - # | o---+ into | o---+ - # | / / | | | # <--- padding line - # o | | | / / - # o | | - add_padding_line = (len(text) > 2 and coldiff == -1 and - [x for (x, y) in edges if x + 1 < y]) - - # fix_nodeline_tail says whether to rewrite - # - # | | o | | | | o | | - # | | |/ / | | |/ / - # | o | | into | o / / # <--- fixed nodeline tail - # | |/ / | |/ / - # o | | o | | - fix_nodeline_tail = len(text) <= 2 and not add_padding_line - - # nodeline is the line containing the node character (typically o) - nodeline = ["|", " "] * idx - nodeline.extend([char, " "]) - - nodeline.extend( - get_nodeline_edges_tail(idx, state[1], ncols, coldiff, - state[0], fix_nodeline_tail)) - - # shift_interline is the line containing the non-vertical - # edges between this entry and the next - shift_interline = ["|", " "] * idx - if coldiff == -1: - n_spaces = 1 - edge_ch = "/" - elif coldiff == 0: - n_spaces = 2 - edge_ch = "|" - else: - n_spaces = 3 - edge_ch = "\\" - shift_interline.extend(n_spaces * [" "]) - shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) - - # draw edges from the current node to its parents - draw_edges(edges, nodeline, shift_interline) - - # lines is the list of all graph lines to print - lines = [nodeline] - if add_padding_line: - lines.append(get_padding_line(idx, ncols, edges)) - lines.append(shift_interline) - - # make sure that there are as many graph lines as there are - # log strings - while len(text) < len(lines): - text.append("") - if len(lines) < len(text): - extra_interline = ["|", " "] * (ncols + coldiff) - while len(lines) < len(text): - lines.append(extra_interline) - - # print lines - indentation_level = max(ncols, ncols + coldiff) - for (line, logstr) in zip(lines, text): - ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) - buf.write(ln.rstrip() + '\n') - - # ... and start over - state[0] = coldiff - state[1] = idx - -def generate(dag, edgefn, current): - seen, state = [], [0, 0] - buf = Buffer() - for node, parents in list(dag): - if node.time: - age_label = age(int(node.time)) - else: - age_label = 'Original' - line = '[%s] %s' % (node.n, age_label) - if node.n == current: - char = '@' - else: - char = 'o' - ascii(buf, state, 'C', char, [line], edgefn(seen, node, parents)) - return buf.b -ENDPYTHON -"}}} - -"{{{ Mercurial age function -python << ENDPYTHON -import time - -agescales = [("year", 3600 * 24 * 365), - ("month", 3600 * 24 * 30), - ("week", 3600 * 24 * 7), - ("day", 3600 * 24), - ("hour", 3600), - ("minute", 60), - ("second", 1)] - -def age(ts): - '''turn a timestamp into an age string.''' - - def plural(t, c): - if c == 1: - return t - return t + "s" - def fmt(t, c): - return "%d %s" % (c, plural(t, c)) - - now = time.time() - then = ts - if then > now: - return 'in the future' - - delta = max(1, int(now - then)) - if delta > agescales[0][1] * 2: - return time.strftime('%Y-%m-%d', time.gmtime(float(ts))) - - for t, s in agescales: - n = delta // s - if n >= 2 or s == 1: - return '%s ago' % fmt(t, n) -ENDPYTHON -"}}} - -"{{{ Python Vim utility functions -python << ENDPYTHON -import vim - -normal = lambda s: vim.command('normal %s' % s) - -MISSING_BUFFER = "Cannot find Gundo's target buffer (%s)" -MISSING_WINDOW = "Cannot find window (%s) for Gundo's target buffer (%s)" - -def _check_sanity(): - '''Check to make sure we're not crazy. - - Does the following things: - - * Make sure the target buffer still exists. - ''' - b = int(vim.eval('g:gundo_target_n')) - - if not vim.eval('bufloaded(%d)' % b): - vim.command('echo "%s"' % (MISSING_BUFFER % b)) - return False - - w = int(vim.eval('bufwinnr(%d)' % b)) - if w == -1: - vim.command('echo "%s"' % (MISSING_WINDOW % (w, b))) - return False - - return True - -def _goto_window_for_buffer(b): - w = int(vim.eval('bufwinnr(%d)' % int(b))) - vim.command('%dwincmd w' % w) - -def _goto_window_for_buffer_name(bn): - b = vim.eval('bufnr("%s")' % bn) - return _goto_window_for_buffer(b) - -def _undo_to(n): - n = int(n) - if n == 0: - vim.command('silent earlier %s' % (int(vim.eval('&undolevels')) + 1)) - else: - vim.command('silent undo %d' % n) - - -INLINE_HELP = '''\ -" Gundo for %s (%d) -" j/k - move between undo states -" p - preview diff of selected and current states -" - revert to selected state - -''' -ENDPYTHON -"}}} - -"{{{ Python undo tree data structures and functions -python << ENDPYTHON -import itertools - -class Buffer(object): - def __init__(self): - self.b = '' - - def write(self, s): - self.b += s - -class Node(object): - def __init__(self, n, parent, time, curhead): - self.n = int(n) - self.parent = parent - self.children = [] - self.curhead = curhead - self.time = time - -def _make_nodes(alts, nodes, parent=None): - p = parent - - for alt in alts: - curhead = 'curhead' in alt - node = Node(n=alt['seq'], parent=p, time=alt['time'], curhead=curhead) - nodes.append(node) - if alt.get('alt'): - _make_nodes(alt['alt'], nodes, p) - p = node - -def make_nodes(): - ut = vim.eval('undotree()') - entries = ut['entries'] - - root = Node(0, None, False, 0) - nodes = [] - _make_nodes(entries, nodes, root) - nodes.append(root) - nmap = dict((node.n, node) for node in nodes) - return nodes, nmap - -def changenr(nodes): - _curhead_l = list(itertools.dropwhile(lambda n: not n.curhead, nodes)) - if _curhead_l: - current = _curhead_l[0].parent.n - else: - current = int(vim.eval('changenr()')) - return current -ENDPYTHON -"}}} - "{{{ Gundo utility functions function! s:GundoGetTargetState()"{{{ @@ -598,6 +267,22 @@ function! s:GundoClose()"{{{ endfunction"}}} function! s:GundoOpen()"{{{ + if !exists('g:gundo_py_loaded') + exe 'pyfile ' . s:plugin_path . '/gundo.py' + python initPythonModule() + + if !s:has_supported_python + function! s:GundoDidNotLoad() + echohl WarningMsg|echomsg "Gundo unavailable: requires Vim 7.3+"|echohl None + endfunction + command! -nargs=0 GundoToggle call s:GundoDidNotLoad() + call s:GundoDidNotLoad() + return + endif" + + let g:gundo_py_loaded = 1 + endif + " Save `splitbelow` value and set it to default to avoid problems with " positioning new windows. let saved_splitbelow = &splitbelow @@ -682,198 +367,16 @@ endfunction"}}} "{{{ Gundo rendering -"{{{ Rendering utility functions -python << ENDPYTHON -import difflib - -def _fmt_time(t): - return time.strftime('%Y-%m-%d %I:%M:%S %p', time.localtime(float(t))) - -def _output_preview_text(lines): - _goto_window_for_buffer_name('__Gundo_Preview__') - vim.command('setlocal modifiable') - vim.current.buffer[:] = lines - vim.command('setlocal nomodifiable') - -def _generate_preview_diff(current, node_before, node_after): - _goto_window_for_buffer(vim.eval('g:gundo_target_n')) - - if not node_after.n: # we're at the original file - before_lines = [] - - _undo_to(0) - after_lines = vim.current.buffer[:] - - before_name = 'n/a' - before_time = '' - after_name = 'Original' - after_time = '' - elif not node_before.n: # we're at a pseudo-root state - _undo_to(0) - before_lines = vim.current.buffer[:] - - _undo_to(node_after.n) - after_lines = vim.current.buffer[:] - - before_name = 'Original' - before_time = '' - after_name = node_after.n - after_time = _fmt_time(node_after.time) - else: - _undo_to(node_before.n) - before_lines = vim.current.buffer[:] - - _undo_to(node_after.n) - after_lines = vim.current.buffer[:] - - before_name = node_before.n - before_time = _fmt_time(node_before.time) - after_name = node_after.n - after_time = _fmt_time(node_after.time) - - _undo_to(current) - - return list(difflib.unified_diff(before_lines, after_lines, - before_name, after_name, - before_time, after_time)) - -def _generate_change_preview_diff(current, node_before, node_after): - _goto_window_for_buffer(vim.eval('g:gundo_target_n')) - - _undo_to(node_before.n) - before_lines = vim.current.buffer[:] - - _undo_to(node_after.n) - after_lines = vim.current.buffer[:] - - before_name = node_before.n or 'Original' - before_time = node_before.time and _fmt_time(node_before.time) or '' - after_name = node_after.n or 'Original' - after_time = node_after.time and _fmt_time(node_after.time) or '' - - _undo_to(current) - - return list(difflib.unified_diff(before_lines, after_lines, - before_name, after_name, - before_time, after_time)) -ENDPYTHON -"}}} - function! s:GundoRenderGraph()"{{{ -python << ENDPYTHON -def GundoRenderGraph(): - if not _check_sanity(): - return - - nodes, nmap = make_nodes() - - for node in nodes: - node.children = [n for n in nodes if n.parent == node] - - def walk_nodes(nodes): - for node in nodes: - if node.parent: - yield (node, [node.parent]) - else: - yield (node, []) - - dag = sorted(nodes, key=lambda n: int(n.n), reverse=True) - current = changenr(nodes) - - result = generate(walk_nodes(dag), asciiedges, current).rstrip().splitlines() - result = [' ' + l for l in result] - - target = (vim.eval('g:gundo_target_f'), int(vim.eval('g:gundo_target_n'))) - - if int(vim.eval('g:gundo_help')): - header = (INLINE_HELP % target).splitlines() - else: - header = [] - - vim.command('call s:GundoOpenGraph()') - vim.command('setlocal modifiable') - vim.current.buffer[:] = (header + result) - vim.command('setlocal nomodifiable') - - i = 1 - for line in result: - try: - line.split('[')[0].index('@') - i += 1 - break - except ValueError: - pass - i += 1 - vim.command('%d' % (i+len(header)-1)) - -GundoRenderGraph() -ENDPYTHON + python GundoRenderGraph() endfunction"}}} function! s:GundoRenderPreview()"{{{ -python << ENDPYTHON -def GundoRenderPreview(): - if not _check_sanity(): - return - - target_state = vim.eval('s:GundoGetTargetState()') - - # Check that there's an undo state. There may not be if we're talking about - # a buffer with no changes yet. - if target_state == None: - _goto_window_for_buffer_name('__Gundo__') - return - else: - target_state = int(target_state) - - _goto_window_for_buffer(vim.eval('g:gundo_target_n')) - - nodes, nmap = make_nodes() - current = changenr(nodes) - - node_after = nmap[target_state] - node_before = node_after.parent - - vim.command('call s:GundoOpenPreview()') - _output_preview_text(_generate_preview_diff(current, node_before, node_after)) - - _goto_window_for_buffer_name('__Gundo__') - -GundoRenderPreview() -ENDPYTHON + python GundoRenderPreview() endfunction"}}} function! s:GundoRenderChangePreview()"{{{ -python << ENDPYTHON -def GundoRenderChangePreview(): - if not _check_sanity(): - return - - target_state = vim.eval('s:GundoGetTargetState()') - - # Check that there's an undo state. There may not be if we're talking about - # a buffer with no changes yet. - if target_state == None: - _goto_window_for_buffer_name('__Gundo__') - return - else: - target_state = int(target_state) - - _goto_window_for_buffer(vim.eval('g:gundo_target_n')) - - nodes, nmap = make_nodes() - current = changenr(nodes) - - node_after = nmap[target_state] - node_before = nmap[current] - - vim.command('call s:GundoOpenPreview()') - _output_preview_text(_generate_change_preview_diff(current, node_before, node_after)) - - _goto_window_for_buffer_name('__Gundo__') - -GundoRenderChangePreview() -ENDPYTHON + python GundoRenderChangePreview() endfunction"}}} "}}} @@ -881,82 +384,11 @@ endfunction"}}} "{{{ Gundo undo/redo function! s:GundoRevert()"{{{ -python << ENDPYTHON -def GundoRevert(): - if not _check_sanity(): - return - - target_n = int(vim.eval('s:GundoGetTargetState()')) - back = vim.eval('g:gundo_target_n') - - _goto_window_for_buffer(back) - _undo_to(target_n) - - vim.command('GundoRenderGraph') - _goto_window_for_buffer(back) - - if int(vim.eval('g:gundo_close_on_revert')): - vim.command('GundoToggle') - -GundoRevert() -ENDPYTHON + python GundoRevert() endfunction"}}} function! s:GundoPlayTo()"{{{ -python << ENDPYTHON -def GundoPlayTo(): - if not _check_sanity(): - return - - target_n = int(vim.eval('s:GundoGetTargetState()')) - back = int(vim.eval('g:gundo_target_n')) - - vim.command('echo "%s"' % back) - - _goto_window_for_buffer(back) - normal('zR') - - nodes, nmap = make_nodes() - - start = nmap[changenr(nodes)] - end = nmap[target_n] - - def _walk_branch(origin, dest): - rev = origin.n < dest.n - - nodes = [] - if origin.n > dest.n: - current, final = origin, dest - else: - current, final = dest, origin - - while current.n >= final.n: - if current.n == final.n: - break - nodes.append(current) - current = current.parent - else: - return None - nodes.append(current) - - return reversed(nodes) if rev else nodes - - branch = _walk_branch(start, end) - - if not branch: - vim.command('unsilent echo "No path to that node from here!"') - return - - for node in branch: - _undo_to(node.n) - vim.command('GundoRenderGraph') - normal('zz') - _goto_window_for_buffer(back) - vim.command('redraw') - vim.command('sleep 60m') - -GundoPlayTo() -ENDPYTHON + python GundoPlayTo() endfunction"}}} "}}} diff --git a/pol.utf8.add.spl b/pol.utf8.add.spl index 8db08aa9e2ab18a4da73464d2500eeacdc24cabe..569ed23f9d46fcaa364302351e5c21b9b5415bbd 100644 GIT binary patch delta 6 NcmWFwpAg2#1po&N0iOT> delta 4 LcmWF!n-B&71K0tS