raylu 9 лет назад
Родитель
Сommit
4df897f224

+ 1 - 0
.gitignore

@@ -1 +1,2 @@
 vim/.netrwhist
+vim/autoload/mundo/__pycache__/

+ 0 - 577
vim/autoload/gundo.py

@@ -1,577 +0,0 @@
-# ============================================================================
-# File:        gundo.py
-# Description: vim global plugin to visualize your undo tree
-# Maintainer:  Steve Losh <steve@stevelosh.com>
-# 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
-" <cr> - 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)
-
-        if rev:
-            return reversed(nodes)
-        else:
-            return 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')

+ 0 - 449
vim/autoload/gundo.vim

@@ -1,449 +0,0 @@
-" ============================================================================
-" File:        gundo.vim
-" Description: vim global plugin to visualize your undo tree
-" Maintainer:  Steve Losh <steve@stevelosh.com>
-" 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.
-"
-" ============================================================================
-
-
-"{{{ Init
-
-if v:version < '703'"{{{
-    function! s:GundoDidNotLoad()
-        echohl WarningMsg|echomsg "Gundo unavailable: requires Vim 7.3+"|echohl None
-    endfunction
-    command! -nargs=0 GundoToggle call s:GundoDidNotLoad()
-    finish
-endif"}}}
-
-if !exists('g:gundo_width')"{{{
-    let g:gundo_width = 45
-endif"}}}
-if !exists('g:gundo_preview_height')"{{{
-    let g:gundo_preview_height = 15
-endif"}}}
-if !exists('g:gundo_preview_bottom')"{{{
-    let g:gundo_preview_bottom = 0
-endif"}}}
-if !exists('g:gundo_right')"{{{
-    let g:gundo_right = 0
-endif"}}}
-if !exists('g:gundo_help')"{{{
-    let g:gundo_help = 1
-endif"}}}
-if !exists("g:gundo_map_move_older")"{{{
-    let g:gundo_map_move_older = 'j'
-endif"}}}
-if !exists("g:gundo_map_move_newer")"{{{
-    let g:gundo_map_move_newer = 'k'
-endif"}}}
-if !exists("g:gundo_close_on_revert")"{{{
-    let g:gundo_close_on_revert = 0
-endif"}}}
-if !exists("g:gundo_prefer_python3")"{{{
-    let g:gundo_prefer_python3 = 0
-endif"}}}
-
-let s:has_supported_python = 0
-if g:gundo_prefer_python3 && has('python3')"{{{
-    let s:has_supported_python = 2
-elseif has('python')"
-    let s:has_supported_python = 1
-endif
-
-if !s:has_supported_python
-    function! s:GundoDidNotLoad()
-        echohl WarningMsg|echomsg "Gundo requires Vim to be compiled with Python 2.4+"|echohl None
-    endfunction
-    command! -nargs=0 GundoToggle call s:GundoDidNotLoad()
-    finish
-endif"}}}
-
-let s:plugin_path = escape(expand('<sfile>:p:h'), '\')
-"}}}
-
-"{{{ Gundo utility functions
-
-function! s:GundoGetTargetState()"{{{
-    let target_line = matchstr(getline("."), '\v\[[0-9]+\]')
-    return matchstr(target_line, '\v[0-9]+')
-endfunction"}}}
-
-function! s:GundoGoToWindowForBufferName(name)"{{{
-    if bufwinnr(bufnr(a:name)) != -1
-        exe bufwinnr(bufnr(a:name)) . "wincmd w"
-        return 1
-    else
-        return 0
-    endif
-endfunction"}}}
-
-function! s:GundoIsVisible()"{{{
-    if bufwinnr(bufnr("__Gundo__")) != -1 || bufwinnr(bufnr("__Gundo_Preview__")) != -1
-        return 1
-    else
-        return 0
-    endif
-endfunction"}}}
-
-function! s:GundoInlineHelpLength()"{{{
-    if g:gundo_help
-        return 6
-    else
-        return 0
-    endif
-endfunction"}}}
-
-"}}}
-
-"{{{ Gundo buffer settings
-
-function! s:GundoMapGraph()"{{{
-    exec 'nnoremap <script> <silent> <buffer> ' . g:gundo_map_move_older . " :call <sid>GundoMove(1)<CR>"
-    exec 'nnoremap <script> <silent> <buffer> ' . g:gundo_map_move_newer . " :call <sid>GundoMove(-1)<CR>"
-    nnoremap <script> <silent> <buffer> <CR>          :call <sid>GundoRevert()<CR>
-    nnoremap <script> <silent> <buffer> o             :call <sid>GundoRevert()<CR>
-    nnoremap <script> <silent> <buffer> <down>        :call <sid>GundoMove(1)<CR>
-    nnoremap <script> <silent> <buffer> <up>          :call <sid>GundoMove(-1)<CR>
-    nnoremap <script> <silent> <buffer> gg            gg:call <sid>GundoMove(1)<CR>
-    nnoremap <script> <silent> <buffer> P             :call <sid>GundoPlayTo()<CR>
-    nnoremap <script> <silent> <buffer> p             :call <sid>GundoRenderChangePreview()<CR>
-    nnoremap <script> <silent> <buffer> q             :call <sid>GundoClose()<CR>
-    cabbrev  <script> <silent> <buffer> q             call <sid>GundoClose()
-    cabbrev  <script> <silent> <buffer> quit          call <sid>GundoClose()
-    nnoremap <script> <silent> <buffer> <2-LeftMouse> :call <sid>GundoMouseDoubleClick()<CR>
-endfunction"}}}
-
-function! s:GundoMapPreview()"{{{
-    nnoremap <script> <silent> <buffer> q     :call <sid>GundoClose()<CR>
-    cabbrev  <script> <silent> <buffer> q     call <sid>GundoClose()
-    cabbrev  <script> <silent> <buffer> quit  call <sid>GundoClose()
-endfunction"}}}
-
-function! s:GundoSettingsGraph()"{{{
-    setlocal buftype=nofile
-    setlocal bufhidden=hide
-    setlocal noswapfile
-    setlocal nobuflisted
-    setlocal nomodifiable
-    setlocal filetype=gundo
-    setlocal nolist
-    setlocal nonumber
-    setlocal norelativenumber
-    setlocal nowrap
-    call s:GundoSyntaxGraph()
-    call s:GundoMapGraph()
-endfunction"}}}
-
-function! s:GundoSettingsPreview()"{{{
-    setlocal buftype=nofile
-    setlocal bufhidden=hide
-    setlocal noswapfile
-    setlocal nobuflisted
-    setlocal nomodifiable
-    setlocal filetype=diff
-    setlocal nonumber
-    setlocal norelativenumber
-    setlocal nowrap
-    setlocal foldlevel=20
-    setlocal foldmethod=diff
-    call s:GundoMapPreview()
-endfunction"}}}
-
-function! s:GundoSyntaxGraph()"{{{
-    let b:current_syntax = 'gundo'
-
-    syn match GundoCurrentLocation '@'
-    syn match GundoHelp '\v^".*$'
-    syn match GundoNumberField '\v\[[0-9]+\]'
-    syn match GundoNumber '\v[0-9]+' contained containedin=GundoNumberField
-
-    hi def link GundoCurrentLocation Keyword
-    hi def link GundoHelp Comment
-    hi def link GundoNumberField Comment
-    hi def link GundoNumber Identifier
-endfunction"}}}
-
-"}}}
-
-"{{{ Gundo buffer/window management
-
-function! s:GundoResizeBuffers(backto)"{{{
-    call s:GundoGoToWindowForBufferName('__Gundo__')
-    exe "vertical resize " . g:gundo_width
-
-    call s:GundoGoToWindowForBufferName('__Gundo_Preview__')
-    exe "resize " . g:gundo_preview_height
-
-    exe a:backto . "wincmd w"
-endfunction"}}}
-
-function! s:GundoOpenGraph()"{{{
-    let existing_gundo_buffer = bufnr("__Gundo__")
-
-    if existing_gundo_buffer == -1
-        call s:GundoGoToWindowForBufferName('__Gundo_Preview__')
-        exe "new __Gundo__"
-        if g:gundo_preview_bottom
-            if g:gundo_right
-                wincmd L
-            else
-                wincmd H
-            endif
-        endif
-        call s:GundoResizeBuffers(winnr())
-    else
-        let existing_gundo_window = bufwinnr(existing_gundo_buffer)
-
-        if existing_gundo_window != -1
-            if winnr() != existing_gundo_window
-                exe existing_gundo_window . "wincmd w"
-            endif
-        else
-            call s:GundoGoToWindowForBufferName('__Gundo_Preview__')
-            if g:gundo_preview_bottom
-                if g:gundo_right
-                    exe "botright vsplit +buffer" . existing_gundo_buffer
-                else
-                    exe "topleft vsplit +buffer" . existing_gundo_buffer
-                endif
-            else
-                exe "split +buffer" . existing_gundo_buffer
-            endif
-            call s:GundoResizeBuffers(winnr())
-        endif
-    endif
-    if exists("g:gundo_tree_statusline")
-        let &l:statusline = g:gundo_tree_statusline
-    endif
-endfunction"}}}
-
-function! s:GundoOpenPreview()"{{{
-    let existing_preview_buffer = bufnr("__Gundo_Preview__")
-
-    if existing_preview_buffer == -1
-        if g:gundo_preview_bottom
-            exe "botright new __Gundo_Preview__"
-        else
-            if g:gundo_right
-                exe "botright vnew __Gundo_Preview__"
-            else
-                exe "topleft vnew __Gundo_Preview__"
-            endif
-        endif
-    else
-        let existing_preview_window = bufwinnr(existing_preview_buffer)
-
-        if existing_preview_window != -1
-            if winnr() != existing_preview_window
-                exe existing_preview_window . "wincmd w"
-            endif
-        else
-            if g:gundo_preview_bottom
-                exe "botright split +buffer" . existing_preview_buffer
-            else
-                if g:gundo_right
-                    exe "botright vsplit +buffer" . existing_preview_buffer
-                else
-                    exe "topleft vsplit +buffer" . existing_preview_buffer
-                endif
-            endif
-        endif
-    endif
-    if exists("g:gundo_preview_statusline")
-        let &l:statusline = g:gundo_preview_statusline
-    endif
-endfunction"}}}
-
-function! s:GundoClose()"{{{
-    if s:GundoGoToWindowForBufferName('__Gundo__')
-        quit
-    endif
-
-    if s:GundoGoToWindowForBufferName('__Gundo_Preview__')
-        quit
-    endif
-
-    exe bufwinnr(g:gundo_target_n) . "wincmd w"
-endfunction"}}}
-
-function! s:GundoOpen()"{{{
-    if !exists('g:gundo_py_loaded')
-        if s:has_supported_python == 2 && g:gundo_prefer_python3
-            exe 'py3file ' . s:plugin_path . '/gundo.py'
-            python3 initPythonModule()
-        else
-            exe 'pyfile ' . s:plugin_path . '/gundo.py'
-            python initPythonModule()
-        endif
-
-        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
-    let &splitbelow = 0
-
-    call s:GundoOpenPreview()
-    exe bufwinnr(g:gundo_target_n) . "wincmd w"
-
-    call s:GundoRenderGraph()
-    call s:GundoRenderPreview()
-
-    " Restore `splitbelow` value.
-    let &splitbelow = saved_splitbelow
-endfunction"}}}
-
-function! s:GundoToggle()"{{{
-    if s:GundoIsVisible()
-        call s:GundoClose()
-    else
-        let g:gundo_target_n = bufnr('')
-        let g:gundo_target_f = @%
-        call s:GundoOpen()
-    endif
-endfunction"}}}
-
-function! s:GundoShow()"{{{
-    call s:GundoOpen()
-endfunction"}}}
-
-function! s:GundoHide()"{{{
-    call s:GundoClose()
-endfunction"}}}
-
-"}}}
-
-"{{{ Gundo mouse handling
-
-function! s:GundoMouseDoubleClick()"{{{
-    let start_line = getline('.')
-
-    if stridx(start_line, '[') == -1
-        return
-    else
-        call s:GundoRevert()
-    endif
-endfunction"}}}
-
-"}}}
-
-"{{{ Gundo movement
-
-function! s:GundoMove(direction) range"{{{
-    let start_line = getline('.')
-    if v:count1 == 0
-        let move_count = 1
-    else
-        let move_count = v:count1
-    endif
-    let distance = 2 * move_count
-
-    " If we're in between two nodes we move by one less to get back on track.
-    if stridx(start_line, '[') == -1
-        let distance = distance - 1
-    endif
-
-    let target_n = line('.') + (distance * a:direction)
-
-    " Bound the movement to the graph.
-    if target_n <= s:GundoInlineHelpLength() - 1
-        call cursor(s:GundoInlineHelpLength(), 0)
-    else
-        call cursor(target_n, 0)
-    endif
-
-    let line = getline('.')
-
-    " Move to the node, whether it's an @ or an o
-    let idx1 = stridx(line, '@')
-    let idx2 = stridx(line, 'o')
-    if idx1 != -1
-        call cursor(0, idx1 + 1)
-    else
-        call cursor(0, idx2 + 1)
-    endif
-
-    call s:GundoRenderPreview()
-endfunction"}}}
-
-"}}}
-
-"{{{ Gundo rendering
-
-function! s:GundoRenderGraph()"{{{
-    if s:has_supported_python == 2 && g:gundo_prefer_python3
-        python3 GundoRenderGraph()
-    else
-        python GundoRenderGraph()
-    endif
-endfunction"}}}
-
-function! s:GundoRenderPreview()"{{{
-    if s:has_supported_python == 2 && g:gundo_prefer_python3
-        python3 GundoRenderPreview()
-    else
-        python GundoRenderPreview()
-    endif
-endfunction"}}}
-
-function! s:GundoRenderChangePreview()"{{{
-    if s:has_supported_python == 2 && g:gundo_prefer_python3
-        python3 GundoRenderChangePreview()
-    else
-        python GundoRenderChangePreview()
-    endif
-endfunction"}}}
-
-"}}}
-
-"{{{ Gundo undo/redo
-
-function! s:GundoRevert()"{{{
-    if s:has_supported_python == 2 && g:gundo_prefer_python3
-        python3 GundoRevert()
-    else
-        python GundoRevert()
-    endif
-endfunction"}}}
-
-function! s:GundoPlayTo()"{{{
-    if s:has_supported_python == 2 && g:gundo_prefer_python3
-        python3 GundoPlayTo()
-    else
-        python GundoPlayTo()
-    endif
-endfunction"}}}
-
-"}}}
-
-"{{{ Misc
-
-function! gundo#GundoToggle()"{{{
-    call s:GundoToggle()
-endfunction"}}}
-
-function! gundo#GundoRenderGraph()"{{{
-    call s:GundoRenderGraph()
-endfunction"}}}
-
-augroup GundoAug
-    autocmd!
-    autocmd BufNewFile __Gundo__ call s:GundoSettingsGraph()
-    autocmd BufNewFile __Gundo_Preview__ call s:GundoSettingsPreview()
-augroup END
-
-"}}}

+ 484 - 0
vim/autoload/mundo.py

@@ -0,0 +1,484 @@
+# vim: set fdm=marker ts=4 sw=4 et:
+# ============================================================================
+# File:        mundo.py
+# Description: vim global plugin to visualize your undo tree
+# Maintainer:  Hyeon Kim <simnalamburt@gmail.com>
+# License:     GPLv2+ -- look it up.
+# Notes:       Much of this code was thieved from Mercurial, and the rest was
+#              heavily inspired by scratch.vim and histwin.vim.
+#
+# ============================================================================
+
+import re
+import sys
+import tempfile
+import vim
+
+from mundo.node import Nodes
+import mundo.util as util
+import mundo.graphlog as graphlog
+
+# Python Vim utility functions -----------------------------------------------------#{{{
+
+MISSING_BUFFER = "Cannot find Mundo's target buffer (%s)"
+MISSING_WINDOW = "Cannot find window (%s) for Mundo'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.
+    '''
+    global nodesData
+    if not nodesData:
+        nodesData = Nodes()
+    b = int(vim.eval('g:mundo_target_n'))
+
+    if not vim.eval('bufloaded(%d)' % int(b)):
+        vim.command('echo "%s"' % (MISSING_BUFFER % b))
+        return False
+
+    w = int(vim.eval('bufwinnr(%d)' % int(b)))
+    if w == -1:
+        vim.command('echo "%s"' % (MISSING_WINDOW % (w, b)))
+        return False
+
+    return True
+
+INLINE_HELP = '''\
+" Mundo (%d) - Press ? for Help:
+" %s/%s  - Next/Prev undo state.
+" J/K  - Next/Prev write state.
+" i    - Toggle 'inline diff' mode.
+" /    - Find changes that match string.
+" n/N  - Next/Prev undo that matches search.
+" P    - Play current state to selected undo.
+" d    - Vert diff of undo with current state.
+" p    - Diff of selected undo and current state.
+" r    - Diff of selected undo and prior undo.
+" q    - Quit!
+" <cr> - Revert to selected state.
+
+'''
+
+#}}}
+
+nodesData = Nodes()
+
+# from profilehooks import profile
+# @profile(immediate=True)
+def MundoRenderGraph(force=False):
+    if not _check_sanity():
+        return
+
+    first_visible_line = int(vim.eval("line('w0')"))
+    last_visible_line = int(vim.eval("line('w$')"))
+
+    verbose = vim.eval('g:mundo_verbose_graph') == "1"
+    target = (int(vim.eval('g:mundo_target_n')),
+                vim.eval('g:mundo_map_move_older'),
+                vim.eval('g:mundo_map_move_newer'))
+
+    if int(vim.eval('g:mundo_help')):
+        header = (INLINE_HELP % target).splitlines()
+    else:
+        header = [(INLINE_HELP % target).splitlines()[0], '\n']
+
+    show_inline_undo = int(vim.eval("g:mundo_inline_undo")) == 1
+    mundo_last_visible_line = int(vim.eval("g:mundo_last_visible_line"))
+    mundo_first_visible_line = int(vim.eval("g:mundo_first_visible_line"))
+
+    if not force and not nodesData.is_outdated() and (
+                not show_inline_undo or 
+                (
+                    mundo_first_visible_line == first_visible_line and
+                    mundo_last_visible_line == last_visible_line
+                )
+            ):
+        return
+
+    result = graphlog.generate(
+            verbose,
+            len(header)+1,
+            first_visible_line,
+            last_visible_line,
+            show_inline_undo,
+            nodesData
+    )
+    vim.command("let g:mundo_last_visible_line=%s"%last_visible_line)
+    vim.command("let g:mundo_first_visible_line=%s"%first_visible_line)
+
+    output = []
+    # right align the dag and flip over the y axis:
+    flip_dag = int(vim.eval("g:mundo_mirror_graph")) == 1
+    dag_width = 1
+    maxwidth = int(vim.eval("g:mundo_width"))
+    for line in result:
+        if len(line[0]) > dag_width:
+            dag_width = len(line[0])
+    for line in result:
+        if flip_dag:
+            dag_line = (line[0][::-1]).replace("/","\\")
+            output.append("%*s %s"% (dag_width,dag_line,line[1]))
+        else:
+            output.append("%-*s %s"% (dag_width,line[0],line[1]))
+
+    vim.command('call s:MundoOpenGraph()')
+    vim.command('setlocal modifiable')
+    lines = (header + output)
+    lines = [line.rstrip('\n') for line in lines]
+    vim.current.buffer[:] = lines
+    vim.command('setlocal nomodifiable')
+
+    i = 1
+    for line in output:
+        try:
+            line.split('[')[0].index('@')
+            i += 1
+            break
+        except ValueError:
+            pass
+        i += 1
+    vim.command('%d' % (i+len(header)-1))
+
+def MundoRenderPreview():
+    if not _check_sanity():
+        return
+
+    target_state = MundoGetTargetState()
+    # 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:
+        util._goto_window_for_buffer_name('__Mundo__')
+        return
+    else:
+        target_state = int(target_state)
+
+    util._goto_window_for_buffer(vim.eval('g:mundo_target_n'))
+
+    nodes, nmap = nodesData.make_nodes()
+
+    node_after = nmap[target_state]
+    node_before = node_after.parent
+
+    vim.command('call s:MundoOpenPreview()')
+    util._output_preview_text(nodesData.preview_diff(node_before, node_after))
+
+    util._goto_window_for_buffer_name('__Mundo__')
+
+def MundoGetTargetState():
+    """ Get the current undo number that mundo is at.  """
+    util._goto_window_for_buffer_name('__Mundo__')
+    target_line = vim.eval("getline('.')")
+    matches = re.match('^.* \[([0-9]+)\] .*$',target_line)
+    if matches:
+        return int(matches.group(1))
+    return 0
+
+def GetNextLine(direction,move_count,write,start="line('.')"):
+    start_line_no = int(vim.eval(start))
+    start_line = vim.eval("getline(%d)" % start_line_no)
+    mundo_verbose_graph = vim.eval('g:mundo_verbose_graph')
+    if mundo_verbose_graph != "0":
+        distance = 2
+
+        # If we're in between two nodes we move by one less to get back on track.
+        if start_line.find('[') == -1:
+            distance = distance - 1
+    else:
+      distance = 1
+      nextline = vim.eval("getline(%d)" % (start_line_no+direction))
+      idx1 = nextline.find('@')
+      idx2 = nextline.find('o')
+      idx3 = nextline.find('w')
+      # if the next line is not a revision - then go down one more.
+      if (idx1+idx2+idx3) == -3:
+          distance = distance + 1
+
+    next_line = start_line_no + distance*direction
+    if move_count > 1:
+        return GetNextLine(direction,move_count-1,write,str(next_line))
+    elif write:
+        newline = vim.eval("getline(%d)" % (next_line))
+        if newline.find('w ') == -1:
+            # make sure that if we can't go up/down anymore that we quit out.
+            if direction < 0 and next_line == 1:
+                return next_line
+            if direction > 0 and next_line >= len(vim.current.window.buffer):
+                return next_line
+            return GetNextLine(direction,1,write,str(next_line))
+    return next_line
+
+def MundoMove(direction,move_count=1,relative=True,write=False):
+    """
+    Move within the undo graph in the direction specified (or to the specific
+    undo node specified).
+
+    Parameters:
+
+      direction  - -1/1 (up/down). when 'relative' if False, the undo node to
+                   move to.
+      move_count - how many times to perform the operation (irrelevent for
+                   relative == False).
+      relative   - whether to move up/down, or to jump to a specific undo node.
+
+      write      - If True, move to the next written undo.
+    """
+    if relative:
+        target_n = GetNextLine(direction,move_count,write)
+    else:
+        updown = 1
+        if MundoGetTargetState() < direction:
+            updown = -1
+        target_n = GetNextLine(updown,abs(MundoGetTargetState()-direction),write)
+
+    # Bound the movement to the graph.
+    help_lines = 3
+    if int(vim.eval('g:mundo_help')):
+        help_lines = len(INLINE_HELP.split('\n'))
+    if target_n <= help_lines:
+        vim.command("call cursor(%d, 0)" % help_lines)
+    else:
+        vim.command("call cursor(%d, 0)" % target_n)
+
+    line = vim.eval("getline('.')")
+
+    # Move to the node, whether it's an @, o, or w
+    idx1 = line.find('@ ')
+    idx2 = line.find('o ')
+    idx3 = line.find('w ')
+    idxs = []
+    if idx1 != -1:
+        idxs.append(idx1)
+    if idx2 != -1:
+        idxs.append(idx2)
+    if idx3 != -1:
+        idxs.append(idx3)
+    minidx = min(idxs)
+    if idx1 == minidx:
+        vim.command("call cursor(0, %d + 1)" % idx1)
+    elif idx2 == minidx:
+        vim.command("call cursor(0, %d + 1)" % idx2)
+    else:
+        vim.command("call cursor(0, %d + 1)" % idx3)
+
+    if vim.eval('g:mundo_auto_preview') == '1':
+        MundoRenderPreview()
+
+def MundoSearch():
+    search = vim.eval("input('/')");
+    vim.command('let @/="%s"'% search.replace("\\","\\\\").replace('"','\\"'))
+    MundoNextMatch()
+
+def MundoPrevMatch():
+    MundoMatch(-1)
+
+def MundoNextMatch():
+    MundoMatch(1)
+
+def MundoMatch(down):
+    """ Jump to the next node that matches the current pattern.  If there is a
+    next node, search from the next node to the end of the list of changes. Stop
+    on a match. """
+    if not _check_sanity():
+        return
+
+    # save the current window number (should be the navigation window)
+    # then generate the undo nodes, and then go back to the current window.
+    util._goto_window_for_buffer(vim.eval('g:mundo_target_n'))
+
+    nodes, nmap = nodesData.make_nodes()
+    total = len(nodes) - 1
+
+    util._goto_window_for_buffer_name('__Mundo__')
+    curline = int(vim.eval("line('.')"))
+    mundo_node = MundoGetTargetState()
+
+    found_version = -1
+    if total > 0:
+        therange = range(mundo_node-1,-1,-1)
+        if down < 0:
+            therange = range(mundo_node+1,total+1)
+        for version in therange:
+            util._goto_window_for_buffer_name('__Mundo__')
+            undochanges = nodesData.preview_diff(nmap[version].parent, nmap[version])
+            # Look thru all of the changes, ignore the first two b/c those are the
+            # diff timestamp fields (not relevent):
+            for change in undochanges[3:]:
+                match_index = vim.eval('match("%s",@/)'% change.replace("\\","\\\\").replace('"','\\"'))
+                # only consider the matches that are actual additions or
+                # subtractions
+                if int(match_index) >= 0 and (change.startswith('-') or change.startswith('+')):
+                    found_version = version
+                    break
+            # found something, lets get out of here:
+            if found_version != -1:
+                break
+    util._goto_window_for_buffer_name('__Mundo__')
+    if found_version >= 0:
+        MundoMove(found_version,1,False)
+
+def MundoRenderPatchdiff():
+    """ Call MundoRenderChangePreview and display a vert diffpatch with the
+    current file. """
+    if MundoRenderChangePreview():
+        # if there are no lines, do nothing (show a warning).
+        util._goto_window_for_buffer_name('__Mundo_Preview__')
+        if vim.current.buffer[:] == ['']:
+            # restore the cursor position before exiting.
+            util._goto_window_for_buffer_name('__Mundo__')
+            vim.command('unsilent echo "No difference between current file and undo number!"')
+            return False
+
+        # quit out of mundo main screen
+        util._goto_window_for_buffer_name('__Mundo__')
+        vim.command('quit')
+
+        # save the __Mundo_Preview__ buffer to a temp file.
+        util._goto_window_for_buffer_name('__Mundo_Preview__')
+        (handle,filename) = tempfile.mkstemp()
+        vim.command('silent! w %s' % (filename))
+        # exit the __Mundo_Preview__ window
+        vim.command('bdelete')
+        # diff the temp file
+        vim.command('silent! keepalt vert diffpatch %s' % (filename))
+        vim.command('set buftype=nofile bufhidden=delete')
+        return True
+    return False
+
+def MundoGetChangesForLine():
+    if not _check_sanity():
+        return False
+
+    target_state = MundoGetTargetState()
+
+    # 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:
+        util._goto_window_for_buffer_name('__Mundo__')
+        return False
+    else:
+        target_state = int(target_state)
+
+    util._goto_window_for_buffer(vim.eval('g:mundo_target_n'))
+
+    nodes, nmap = nodesData.make_nodes()
+
+    node_after = nmap[target_state]
+    node_before = nmap[nodesData.current()]
+    return nodesData.change_preview_diff(node_before, node_after)
+
+def MundoRenderChangePreview():
+    """ Render the selected undo level with the current file.
+    Return True on success, False on failure. """
+    if not _check_sanity():
+        return
+
+    vim.command('call s:MundoOpenPreview()')
+    util._output_preview_text(MundoGetChangesForLine())
+
+    util._goto_window_for_buffer_name('__Mundo__')
+
+    return True
+
+def MundoRenderToggleInlineDiff():
+    show_inline = int(vim.eval('g:mundo_inline_undo'))
+    if show_inline == 0:
+        vim.command("let g:mundo_inline_undo=1")
+    else:
+        vim.command("let g:mundo_inline_undo=0")
+    line = int(vim.eval("line('.')"))
+    nodesData.clear_oneline_diffs()
+    MundoRenderGraph(True)
+    vim.command("call cursor(%d,0)" % line)
+
+def MundoToggleHelp():
+    show_help = int(vim.eval('g:mundo_help'))
+    if show_help == 0:
+        vim.command("let g:mundo_help=1")
+    else:
+        vim.command("let g:mundo_help=0")
+    line = int(vim.eval("line('.')"))
+    column = int(vim.eval("col('.')"))
+    old_line_count = int(vim.eval("line('$')"))
+    MundoRenderGraph(True)
+    new_line_count = int(vim.eval("line('$')"))
+    vim.command("call cursor(%d, %d)" % (line + new_line_count - old_line_count, column))
+
+# Mundo undo/redo
+def MundoRevert():
+    if not _check_sanity():
+        return
+
+    target_n = MundoGetTargetState()
+    back = vim.eval('g:mundo_target_n')
+
+    util._goto_window_for_buffer(back)
+    util._undo_to(target_n)
+
+    vim.command('MundoRenderGraph')
+    if int(vim.eval('g:mundo_return_on_revert')):
+        util._goto_window_for_buffer(back)
+
+    if int(vim.eval('g:mundo_close_on_revert')):
+        vim.command('MundoToggle')
+
+def MundoPlayTo():
+    if not _check_sanity():
+        return
+
+    target_n = MundoGetTargetState()
+    back = int(vim.eval('g:mundo_target_n'))
+    delay = int(vim.eval('g:mundo_playback_delay'))
+
+    vim.command('echo "%s"' % back)
+
+    util._goto_window_for_buffer(back)
+    util.normal('zR')
+
+    nodes, nmap = nodesData.make_nodes()
+
+    start = nmap[nodesData.current()]
+    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)
+
+        if rev:
+            return reversed(nodes)
+        else:
+            return 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:
+        util._undo_to(node.n)
+        vim.command('MundoRenderGraph')
+        util.normal('zz')
+        util._goto_window_for_buffer(back)
+        vim.command('redraw')
+        vim.command('sleep %dm' % delay)
+
+def initPythonModule():
+    if sys.version_info[:2] < (2, 4):
+        vim.command('let s:has_supported_python = 0')

+ 410 - 0
vim/autoload/mundo.vim

@@ -0,0 +1,410 @@
+" ============================================================================
+" File:        mundo.vim
+" Description: vim global plugin to visualize your undo tree
+" Maintainer:  Hyeon Kim <simnalamburt@gmail.com>
+" 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.
+"
+" ============================================================================
+
+
+"{{{ Init
+let s:save_cpo = &cpo
+set cpo&vim
+if v:version < '703'"{{{
+    function! s:MundoDidNotLoad()
+        echohl WarningMsg|echomsg "Mundo unavailable: requires Vim 7.3+"|echohl None
+    endfunction
+    command! -nargs=0 MundoToggle call s:MundoDidNotLoad()
+    finish
+endif"}}}
+
+call mundo#util#init()
+
+
+let s:has_supported_python = 0
+if g:mundo_prefer_python3 && has('python3')"{{{
+    let s:has_supported_python = 2
+elseif has('python')"
+    let s:has_supported_python = 1
+endif
+
+if !s:has_supported_python
+    function! s:MundoDidNotLoad()
+        echohl WarningMsg|echomsg "Mundo requires Vim to be compiled with Python 2.4+"|echohl None
+    endfunction
+    command! -nargs=0 MundoToggle call s:MundoDidNotLoad()
+    finish
+endif"}}}
+
+
+let s:plugin_path = escape(expand('<sfile>:p:h'), '\')
+"}}}
+
+"{{{ Mundo utility functions
+
+function! s:MundoGoToWindowForBufferName(name)"{{{
+    if bufwinnr(bufnr(a:name)) != -1
+        exe bufwinnr(bufnr(a:name)) . "wincmd w"
+        return 1
+    else
+        return 0
+    endif
+endfunction"}}}
+
+function! s:MundoIsVisible()"{{{
+    if bufwinnr(bufnr("__Mundo__")) != -1 || bufwinnr(bufnr("__Mundo_Preview__")) != -1
+        return 1
+    else
+        return 0
+    endif
+endfunction"}}}
+
+function! s:MundoInlineHelpLength()"{{{
+    if g:mundo_help
+        return 10
+    else
+        return 0
+    endif
+endfunction"}}}
+
+"}}}
+
+"{{{ Mundo buffer settings
+
+function! s:MundoMapGraph()"{{{
+    exec 'nnoremap <script> <silent> <buffer> ' . g:mundo_map_move_older . " :<C-u>call <sid>MundoPython('MundoMove(1,'. v:count .')')<CR>"
+    exec 'nnoremap <script> <silent> <buffer> ' . g:mundo_map_move_newer . " :<C-u>call <sid>MundoPython('MundoMove(-1,'. v:count .')')<CR>"
+    nnoremap <script> <silent> <buffer> <CR>          :call <sid>MundoPython('MundoRevert()')<CR>
+    nnoremap <script> <silent> <buffer> o             :call <sid>MundoPython('MundoRevert()')<CR>
+    nnoremap <script> <silent> <buffer> <down>        :<C-u>call <sid>MundoPython('MundoMove(1,'. v:count .')')<CR>
+    nnoremap <script> <silent> <buffer> <up>          :<C-u>call <sid>MundoPython('MundoMove(-1,'. v:count .')')<CR>
+    nnoremap <script> <silent> <buffer> J             :<C-u>call <sid>MundoPython('MundoMove(1,'. v:count .',True,True)')<CR>
+    nnoremap <script> <silent> <buffer> K             :<C-u>call <sid>MundoPython('MundoMove(-1,'. v:count .',True,True)')<CR>
+    nnoremap <script> <silent> <buffer> gg            gg:<C-u>call <sid>MundoPython('MundoMove(1,'. v:count .')')<CR>
+    nnoremap <script> <silent> <buffer> P             :call <sid>MundoPython('MundoPlayTo()')<CR>
+    nnoremap <script> <silent> <buffer> d             :call <sid>MundoPython('MundoRenderPatchdiff()')<CR>
+    nnoremap <script> <silent> <buffer> i             :call <sid>MundoPython('MundoRenderToggleInlineDiff()')<CR>
+    nnoremap <script> <silent> <buffer> /             :call <sid>MundoPython('MundoSearch()')<CR>
+    nnoremap <script> <silent> <buffer> n             :call <sid>MundoPython('MundoNextMatch()')<CR>
+    nnoremap <script> <silent> <buffer> N             :call <sid>MundoPython('MundoPrevMatch()')<CR>
+    nnoremap <script> <silent> <buffer> p             :call <sid>MundoPython('MundoRenderChangePreview()')<CR>
+    nnoremap <script> <silent> <buffer> r             :call <sid>MundoPython('MundoRenderPreview()')<CR>
+    nnoremap <script> <silent> <buffer> ?             :call <sid>MundoPython('MundoToggleHelp()')<CR>
+    nnoremap <script> <silent> <buffer> q             :call <sid>MundoClose()<CR>
+    cabbrev  <script> <silent> <buffer> q             call <sid>MundoClose()
+    cabbrev  <script> <silent> <buffer> quit          call <sid>MundoClose()
+    nnoremap <script> <silent> <buffer> <2-LeftMouse> :call <sid>MundoMouseDoubleClick()<CR>
+endfunction"}}}
+
+function! s:MundoMapPreview()"{{{
+    nnoremap <script> <silent> <buffer> q     :call <sid>MundoClose()<CR>
+    cabbrev  <script> <silent> <buffer> q     call <sid>MundoClose()
+    cabbrev  <script> <silent> <buffer> quit  call <sid>MundoClose()
+endfunction"}}}
+
+function! s:MundoSettingsGraph()"{{{
+    setlocal buftype=nofile
+    setlocal bufhidden=hide
+    setlocal noswapfile
+    setlocal nobuflisted
+    setlocal nomodifiable
+    setlocal filetype=mundo
+    setlocal nolist
+    setlocal nonumber
+    setlocal norelativenumber
+    setlocal nowrap
+    call s:MundoSyntaxGraph()
+    call s:MundoMapGraph()
+endfunction"}}}
+
+function! s:MundoSettingsPreview()"{{{
+    setlocal buftype=nofile
+    setlocal bufhidden=hide
+    setlocal noswapfile
+    setlocal nobuflisted
+    setlocal nomodifiable
+    setlocal filetype=diff
+    setlocal nonumber
+    setlocal norelativenumber
+    setlocal nowrap
+    setlocal foldlevel=20
+    setlocal foldmethod=diff
+    call s:MundoMapPreview()
+endfunction"}}}
+
+function! s:MundoSyntaxGraph()"{{{
+    let b:current_syntax = 'mundo'
+
+    syn match MundoCurrentLocation '@'
+    syn match MundoHelp '\v^".*$'
+    syn match MundoNumberField '\v\[[0-9]+\]'
+    syn match MundoNumber '\v[0-9]+' contained containedin=MundoNumberField
+    syn region MundoDiff start=/\v<ago> / end=/$/
+    syn match MundoDiffAdd '\v\+[^+-]+\+' contained containedin=MundoDiff
+    syn match MundoDiffDelete '\v-[^+-]+-' contained containedin=MundoDiff
+
+    hi def link MundoCurrentLocation Keyword
+    hi def link MundoHelp Comment
+    hi def link MundoNumberField Comment
+    hi def link MundoNumber Identifier
+    hi def link MundoDiffAdd DiffAdd
+    hi def link MundoDiffDelete DiffDelete
+endfunction"}}}
+
+"}}}
+
+"{{{ Mundo buffer/window management
+
+function! s:MundoResizeBuffers(backto)"{{{
+    call s:MundoGoToWindowForBufferName('__Mundo__')
+    exe "vertical resize " . g:mundo_width
+
+    call s:MundoGoToWindowForBufferName('__Mundo_Preview__')
+    exe "resize " . g:mundo_preview_height
+
+    exe a:backto . "wincmd w"
+endfunction"}}}
+
+function! s:MundoOpenGraph()"{{{
+    let existing_mundo_buffer = bufnr("__Mundo__")
+
+    if existing_mundo_buffer == -1
+        call s:MundoGoToWindowForBufferName('__Mundo_Preview__')
+        exe "new __Mundo__"
+        set fdm=manual
+        if g:mundo_preview_bottom
+            if g:mundo_right
+                wincmd L
+            else
+                wincmd H
+            endif
+        endif
+        call s:MundoResizeBuffers(winnr())
+    else
+        let existing_mundo_window = bufwinnr(existing_mundo_buffer)
+
+        if existing_mundo_window != -1
+            if winnr() != existing_mundo_window
+                exe existing_mundo_window . "wincmd w"
+            endif
+        else
+            call s:MundoGoToWindowForBufferName('__Mundo_Preview__')
+            if g:mundo_preview_bottom
+                if g:mundo_right
+                    exe "botright vsplit +buffer" . existing_mundo_buffer
+                else
+                    exe "topleft vsplit +buffer" . existing_mundo_buffer
+                endif
+            else
+                exe "split +buffer" . existing_mundo_buffer
+            endif
+            call s:MundoResizeBuffers(winnr())
+        endif
+    endif
+    if exists("g:mundo_tree_statusline")
+        let &l:statusline = g:mundo_tree_statusline
+    endif
+endfunction"}}}
+
+function! s:MundoOpenPreview()"{{{
+    let existing_preview_buffer = bufnr("__Mundo_Preview__")
+
+    if existing_preview_buffer == -1
+        if g:mundo_preview_bottom
+            exe "botright keepalt new __Mundo_Preview__"
+        else
+            if g:mundo_right
+                exe "botright keepalt vnew __Mundo_Preview__"
+            else
+                exe "topleft keepalt vnew __Mundo_Preview__"
+            endif
+        endif
+    else
+        let existing_preview_window = bufwinnr(existing_preview_buffer)
+
+        if existing_preview_window != -1
+            if winnr() != existing_preview_window
+                exe existing_preview_window . "wincmd w"
+            endif
+        else
+            if g:mundo_preview_bottom
+                exe "botright keepalt split +buffer" . existing_preview_buffer
+            else
+                if g:mundo_right
+                    exe "botright keepalt vsplit +buffer" . existing_preview_buffer
+                else
+                    exe "topleft keepalt vsplit +buffer" . existing_preview_buffer
+                endif
+            endif
+        endif
+    endif
+    if exists("g:mundo_preview_statusline")
+        let &l:statusline = g:mundo_preview_statusline
+    endif
+endfunction"}}}
+
+function! s:MundoClose()"{{{
+    if s:MundoGoToWindowForBufferName('__Mundo__')
+        quit
+    endif
+
+    if s:MundoGoToWindowForBufferName('__Mundo_Preview__')
+        quit
+    endif
+
+    exe bufwinnr(g:mundo_target_n) . "wincmd w"
+endfunction"}}}
+
+function! s:MundoOpen()"{{{
+    if !exists('g:mundo_py_loaded')
+        if s:has_supported_python == 2 && g:mundo_prefer_python3
+            exe 'py3file ' . escape(s:plugin_path, ' ') . '/mundo.py'
+            python3 initPythonModule()
+        else
+            exe 'pyfile ' . escape(s:plugin_path, ' ') . '/mundo.py'
+            python initPythonModule()
+        endif
+
+        if !s:has_supported_python
+            function! s:MundoDidNotLoad()
+                echohl WarningMsg|echomsg "Mundo unavailable: requires Vim 7.3+"|echohl None
+            endfunction
+            command! -nargs=0 MundoToggle call s:MundoDidNotLoad()
+            call s:MundoDidNotLoad()
+            return
+        endif
+
+        let g:mundo_py_loaded = 1
+    endif
+
+    " Save `splitbelow` value and set it to default to avoid problems with
+    " positioning new windows.
+    let saved_splitbelow = &splitbelow
+    let &splitbelow = 0
+
+    call s:MundoOpenPreview()
+    exe bufwinnr(g:mundo_target_n) . "wincmd w"
+    call s:MundoOpenGraph()
+
+    call s:MundoPython('MundoRenderGraph()')
+    call s:MundoPython('MundoRenderPreview()')
+
+    " Restore `splitbelow` value.
+    let &splitbelow = saved_splitbelow
+endfunction"}}}
+
+" This has to be outside of a function otherwise it just picks up the CWD
+let s:mundo_path = escape( expand( '<sfile>:p:h' ), '\' )
+
+function! s:MundoToggle()"{{{
+    if g:mundo_python_path_setup == 0
+        let g:mundo_python_path_setup = 1
+        call s:MundoPython('sys.path.insert(1, "'. s:mundo_path .'")')
+        call s:MundoPython('sys.path.insert(1, "'. s:mundo_path .'/mundo")')
+    end
+    if s:MundoIsVisible()
+        call s:MundoClose()
+    else
+        let g:mundo_target_n = bufnr('')
+        let g:mundo_target_f = @%
+        call s:MundoOpen()
+    endif
+endfunction"}}}
+
+function! s:MundoShow()"{{{
+    if !s:MundoIsVisible()
+        let g:mundo_target_n = bufnr('')
+        let g:mundo_target_f = @%
+        call s:MundoOpen()
+    endif
+endfunction"}}}
+
+function! s:MundoHide()"{{{
+    if s:MundoIsVisible()
+        call s:MundoClose()
+    endif
+endfunction"}}}
+
+"}}}
+
+"{{{ Mundo mouse handling
+
+function! s:MundoMouseDoubleClick()"{{{
+    let start_line = getline('.')
+
+    if stridx(start_line, '[') == -1
+        return
+    else
+        call <sid>MundoPython('MundoRevert()')
+    endif
+endfunction"}}}
+
+"}}}
+
+"{{{ Mundo rendering
+
+function! s:MundoPython(fn)"{{{
+    if s:has_supported_python == 2 && g:mundo_prefer_python3
+        exec "python3 ". a:fn
+    else
+        exec "python ". a:fn
+    endif
+endfunction"}}}
+
+"}}}
+
+"{{{ Misc
+
+function! mundo#MundoToggle()"{{{
+    call s:MundoToggle()
+endfunction"}}}
+
+function! mundo#MundoShow()"{{{
+    call s:MundoShow()
+endfunction"}}}
+
+function! mundo#MundoHide()"{{{
+    call s:MundoHide()
+endfunction"}}}
+
+function! mundo#MundoRenderGraph()"{{{
+    call s:MundoPython('MundoRenderGraph()')
+endfunction"}}}
+
+" automatically reload Mundo buffer if open
+function! s:MundoRefresh()"{{{
+  " abort when there were no changes
+
+  let mundoWin    = bufwinnr('__Mundo__')
+  let mundoPreWin = bufwinnr('__Mundo_Preview__')
+  let currentWin  = bufwinnr('%')
+
+  " abort if Mundo is closed or is current window
+  if (mundoWin == -1) || (mundoPreWin == -1) || (mundoPreWin == currentWin)
+    return
+  endif
+
+  let winView = winsaveview()
+  :MundoRenderGraph
+
+  " switch back to previous window
+  execute currentWin .'wincmd w'
+  call winrestview(winView)
+endfunction"}}}
+
+augroup MundoAug
+    autocmd!
+    autocmd BufNewFile __Mundo__ call s:MundoSettingsGraph()
+    autocmd BufNewFile __Mundo_Preview__ call s:MundoSettingsPreview()
+    autocmd CursorHold * call s:MundoRefresh()
+    autocmd CursorMoved * call s:MundoRefresh()
+    autocmd BufEnter * let b:mundoChangedtick = 0
+augroup END
+
+"}}}
+
+
+let &cpo = s:save_cpo
+unlet s:save_cpo

+ 0 - 0
vim/autoload/mundo/__init__.py


+ 105 - 0
vim/autoload/mundo/diff.py

@@ -0,0 +1,105 @@
+import difflib
+import itertools
+
+# one line diff functions.
+def one_line_diff_str(before,after,mx=15,pre=2):
+    """
+    Return a summary of the differences between two strings, concatenated.
+
+    Parameters:
+
+      before - string before.
+      after  - after string.
+      mx     - the max number of strings.
+      pre    - number of characters to show before diff (context)
+
+    Returns a string no longer than 'mx'.
+    """
+    old = one_line_diff(before,after)
+    result = ''
+    firstEl = True
+    # TODO instead of using +addition+ and -subtraction- it'd be nice to be able
+    # to highlight the change w/o requiring the +/- chars.
+    for v in old:
+        # if the first element doesn't have a change, then don't include it.
+        v = escape_returns(v)
+        if firstEl:
+            firstEl = False
+            # add in pre character context:
+            if not (v.startswith('+') or v.startswith('-')) and result == '':
+                v = v[-pre:]
+        # when we're going to be bigger than our max limit, lets ensure that the
+        # trailing +/- appears in the text:
+        if len(result) + len(v) > mx:
+            if v.startswith('+') or v.startswith('-'):
+                result += v[:mx - len(result) - 1]
+                result += v[0]
+            break
+        result += v
+    return result
+
+def escape_returns(result):
+    return result.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t')
+
+def one_line_diff(before, after):
+    """
+    Return a summary of the differences between two arbitrary strings.
+
+    Returns a list of strings, summarizing all the changes.
+    """
+    a, b, result = [], [], []
+    for line in itertools.chain(itertools.islice(
+        difflib.unified_diff(before.splitlines(),
+                             after.splitlines()), 2, None), ['@@']):
+        if line.startswith('@@'):
+            result.extend(one_line_diff_raw('\n'.join(a), '\n'.join(b)))
+            a, b = [], []
+            continue
+        if not line.startswith('+'):
+            a.append(line[1:])
+        if not line.startswith('-'):
+            b.append(line[1:])
+    if after.endswith('\n') and not before.endswith('\n'):
+        if result:
+            result[-1] = result[-1][:-1] + '\n+'
+        else:
+            result = ['+\n+']
+    return result
+
+def one_line_diff_raw(before,after):
+  s = difflib.SequenceMatcher(None,before,after)
+  results = []
+  for tag, i1, i2, j1, j2 in s.get_opcodes():
+    #print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" % (tag, i1, i2, before[i1:i2], j1, j2, after[j1:j2]))
+    if tag == 'equal':
+      _append_result(results,{
+        'equal': after[j1:j2]
+      })
+    if tag == 'insert':
+      _append_result(results,{
+        'plus': after[j1:j2]
+      })
+    elif tag == 'delete':
+      _append_result(results,{
+        'minus': before[i1:i2]
+      })
+    elif tag == 'replace':
+      _append_result(results,{
+        'minus': before[j1:j2],
+        'plus': after[j1:j2]
+      })
+  final_results = []
+  # finally, create a human readable string of information.
+  for v in results:
+    if 'minus' in v and 'plus' in v and len(v['minus']) > 0 and len(v['plus']) > 0:
+      final_results.append("-%s-+%s+"% (v['minus'],v['plus']))
+    elif 'minus' in v and len(v['minus']) > 0:
+      final_results.append("-%s-"% (v['minus']))
+    elif 'plus' in v and len(v['plus']) > 0:
+      final_results.append("+%s+"% (v['plus']))
+    elif 'equal' in v:
+      final_results.append("%s"% (v['equal']))
+  return final_results
+
+def _append_result(results,val):
+  results.append(val)

+ 253 - 0
vim/autoload/mundo/graphlog.py

@@ -0,0 +1,253 @@
+import time
+import util
+
+# 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(state, type, char, text, coldata, verbose):
+    """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.
+      - Verbosity: if enabled then the graph prints an extra '|'
+        between each line of information.
+
+    Returns a string representing the output.
+    """
+
+    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] and
+                        verbose)
+
+    # 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
+    if any("/" in s for s in lines) or verbose:
+        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)
+
+    indentation_level = max(ncols, ncols + coldiff)
+    result = []
+    for (line, logstr) in zip(lines, text):
+        graph = "%-*s" % (2 * indentation_level, "".join(line))
+        if not graph.isspace():
+            result.append([graph, logstr])
+
+    # ... and start over
+    state[0] = coldiff
+    state[1] = idx
+    return result
+
+def generate(verbose, num_header_lines, first_visible_line, last_visible_line, inline_graph, nodesData):
+    """
+    Generate an array of the graph, and text describing the node of the graph.
+    """
+    seen, state = [], [0, 0]
+    result = []
+    current = nodesData.current()
+
+    nodes, nmap = nodesData.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)
+    dag = walk_nodes(dag)
+
+    line_number = num_header_lines
+    for idx, part in list(enumerate(dag)):
+        node, parents = part
+        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 = '@'
+        elif node.saved:
+            char = 'w'
+        else:
+            char = 'o'
+        show_inine_diff = inline_graph and line_number >= first_visible_line and line_number <= last_visible_line
+        preview_diff = nodesData.preview_diff(node.parent, node,False,show_inine_diff)
+        line = '[%s] %-10s %s' % (node.n, age_label, preview_diff)
+        new_lines = ascii(state, 'C', char, [line], asciiedges(seen, node, parents), verbose)
+        line_number += len(new_lines)
+        result.extend(new_lines)
+    util._undo_to(current)
+    return result
+
+# Mercurial age function -----------------------------------------------------------
+agescales = [("yr", 3600 * 24 * 365),
+             ("mon", 3600 * 24 * 30),
+             ("wk", 3600 * 24 * 7),
+             ("dy", 3600 * 24),
+             ("hr", 3600),
+             ("min", 60)]
+
+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" % (int(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)
+
+    return "<1 min ago"

+ 196 - 0
vim/autoload/mundo/node.py

@@ -0,0 +1,196 @@
+import diff
+import difflib
+import itertools
+import time
+import util
+
+# Python undo tree data structures and functions ----------------------------------
+class Node(object):
+    def __init__(self, n, parent, time, curhead, saved):
+        self.n = int(n)
+        self.parent = parent
+        self.children = []
+        self.curhead = curhead
+        self.saved = saved
+        self.time = time
+
+    def __repr__(self):
+        return "[n=%s,parent=%s,time=%s,curhead=%s,saved=%s]" % \
+            (self.n,self.parent,self.time,self.curhead,self.saved)
+
+class Nodes(object):
+    def __init__(self):
+        self._clear_cache()
+
+    def _clear_cache(self):
+        self.nodes_made = None
+        self.target_f = None
+        self.changedtick = None
+        self.lines = {}
+        self.clear_oneline_diffs()
+
+    def clear_oneline_diffs(self):
+        self.diffs = {}
+        self.diff_has_oneline = {}
+
+    def _check_version_location(self):
+        util._goto_window_for_buffer(util.vim().eval('g:mundo_target_n'))
+        target_f = util.vim().eval('g:mundo_target_f')
+        if target_f != self.target_f:
+            self._clear_cache()
+
+    def _make_nodes(self,alts, nodes, parent=None):
+        p = parent
+
+        for alt in alts:
+            if alt:
+                curhead = 'curhead' in alt
+                saved = 'save' in alt
+                node = Node(n=alt['seq'], parent=p, time=alt['time'], curhead=curhead, saved=saved)
+                nodes.append(node)
+                if alt.get('alt'):
+                    self._make_nodes(alt['alt'], nodes, p)
+                p = node
+
+    def is_outdated(self):
+        util._goto_window_for_buffer(util.vim().eval('g:mundo_target_n'))
+        current_changedtick = util.vim().eval('b:changedtick')
+        return self.changedtick != current_changedtick
+
+    def make_nodes(self):
+        # If the current changedtick is unchanged, we don't need to do
+        # anything:
+        if not self.is_outdated():
+            return self.nodes_made
+
+        self._check_version_location()
+        target_f = util.vim().eval('g:mundo_target_f')
+        ut = util.vim().eval('undotree()')
+        entries = ut['entries']
+        seq_last = ut['seq_last']
+        current_changedtick = util.vim().eval('b:changedtick')
+
+        root = Node(0, None, False, 0, 0)
+        nodes = []
+        # TODO only compute new values (not all values)
+        self._make_nodes(entries, nodes, root)
+        nodes.append(root)
+        nmap = dict((node.n, node) for node in nodes)
+
+        # cache values for later use
+        self.target_f = target_f
+        self.seq_last = seq_last
+        self.nodes_made = (nodes, nmap)
+        self.changedtick = current_changedtick
+
+        return self.nodes_made
+
+    def current(self):
+        """ Return the number of the current change. """
+        self._check_version_location()
+        nodes, nmap = self.make_nodes()
+        _curhead_l = list(itertools.dropwhile(lambda n: not n.curhead, nodes))
+        if _curhead_l:
+            current = _curhead_l[0].parent.n
+        else:
+            current = int(util.vim().eval('changenr()'))
+        return current
+
+    def _fmt_time(self,t):
+        return time.strftime('%Y-%m-%d %I:%M:%S %p', time.localtime(float(t)))
+
+    def _get_lines(self,node):
+        n = 0
+        if node:
+            n = node.n
+        if n not in self.lines:
+            util._undo_to(n)
+            self.lines[n] = util.vim().current.buffer[:]
+        return self.lines[n]
+
+    def change_preview_diff(self,before,after):
+        self._check_version_location()
+        key = "%s-%s-cpd"%(before.n,after.n)
+        if key in self.diffs:
+            return self.diffs[key]
+
+        util._goto_window_for_buffer(util.vim().eval('g:mundo_target_n'))
+        before_lines = self._get_lines(before)
+        after_lines = self._get_lines(after)
+
+        before_name = str(before.n or 'Original')
+        before_time = before.time and self._fmt_time(before.time) or ''
+        after_name = str(after.n or 'Original')
+        after_time = after.time and self._fmt_time(after.time) or ''
+
+        util._undo_to(self.current())
+
+        self.diffs[key] = list(difflib.unified_diff(before_lines, after_lines,
+                                         before_name, after_name,
+                                         before_time, after_time))
+        return self.diffs[key]
+
+    def preview_diff(self, before, after, unified=True, inline=False):
+        """
+        Generate a diff comparing two versions of a file.
+
+        Parameters:
+
+          current - ?
+          before
+          after
+          unified - If True, generate a unified diff
+          inline - Generate a one line summary line.
+        """
+        self._check_version_location()
+        bn = 0
+        an = 0
+        if not after.n:    # we're at the original file
+            pass
+        elif not before.n: # we're at a pseudo-root state
+            an = after.n
+        else:
+            bn = before.n
+            an = after.n
+        key = "%s-%s-pd-%s"%(bn,an,unified)
+        needs_oneline = inline and key not in self.diff_has_oneline
+        if key in self.diffs and not needs_oneline:
+            return self.diffs[key]
+
+        if not after.n:    # we're at the original file
+            before_lines = []
+            after_lines = self._get_lines(None)
+
+            before_name = 'n/a'
+            before_time = ''
+            after_name = 'Original'
+            after_time = ''
+        elif not before.n: # we're at a pseudo-root state
+            before_lines = self._get_lines(None)
+            after_lines = self._get_lines(after)
+
+            before_name = 'Original'
+            before_time = ''
+            after_name = str(after.n)
+            after_time = self._fmt_time(after.time)
+        else:
+            before_lines = self._get_lines(before)
+            after_lines = self._get_lines(after)
+
+            before_name = str(before.n)
+            before_time = self._fmt_time(before.time)
+            after_name = str(after.n)
+            after_time = self._fmt_time(after.time)
+
+        if unified:
+            self.diffs[key] = list(difflib.unified_diff(before_lines, after_lines,
+                                             before_name, after_name,
+                                             before_time, after_time))
+        elif inline:
+            maxwidth = int(util.vim().eval("winwidth(0)"))
+            self.diffs[key] = diff.one_line_diff_str('\n'.join(before_lines),'\n'.join(after_lines),maxwidth)
+            self.diff_has_oneline[key] = True
+        else:
+            self.diffs[key] = ""
+
+        return self.diffs[key]

+ 34 - 0
vim/autoload/mundo/util.py

@@ -0,0 +1,34 @@
+# import vim
+
+normal = lambda s: vim().command('normal %s' % s)
+normal_silent = lambda s: vim().command('silent! normal %s' % s)
+
+def vim():
+    """ call Vim.
+    
+    This is wrapped so that it can easily be mocked.
+    """
+    import vim
+    return vim
+
+def _goto_window_for_buffer(b):
+    w = int(vim().eval('bufwinnr(%d)' % int(b)))
+    vim().command('%dwincmd w' % int(w))
+
+def _goto_window_for_buffer_name(bn):
+    b = vim().eval('bufnr("%s")' % bn)
+    return _goto_window_for_buffer(b)
+
+# Rendering utility functions
+def _output_preview_text(lines):
+    _goto_window_for_buffer_name('__Mundo_Preview__')
+    vim().command('setlocal modifiable')
+    vim().current.buffer[:] = [line.rstrip() for line in lines]
+    vim().command('setlocal nomodifiable')
+
+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' % int(n))

+ 124 - 0
vim/autoload/mundo/util.vim

@@ -0,0 +1,124 @@
+let s:save_cpo = &cpo
+set cpo&vim
+
+if exists('g:Mundo_PluginLoaded')
+    finish
+endif
+
+function! mundo#util#set_default(var, val, ...)  "{{{
+    if !exists(a:var)
+        let {a:var} = a:val
+    endif
+    let old_var = get(a:000, 0, '')
+    if exists(old_var)
+        echohl WarningMsg
+        echomsg "{".old_var."}is deprecated! Please change your setting to {"
+                    \.split(old_var,':')[0]
+                    \.':'
+                    \.substitute(split(old_var,':')[1],'gundo_','mundo_','g')
+                    \.'}'
+        echohl None
+    endif
+endfunction"}}}
+
+call mundo#util#set_default(
+            \ 'g:mundo_python_path_setup', 0,
+            \ 'g:gundo_python_path_setup')
+
+call mundo#util#set_default(
+            \ 'g:mundo_first_visible_line', 0,
+            \ 'g:gundo_first_visible_line')
+
+call mundo#util#set_default(
+            \ 'g:mundo_last_visible_line', 0,
+            \ 'g:gundo_last_visible_line')
+
+call mundo#util#set_default(
+            \ 'g:mundo_width', 45,
+            \ 'g:gundo_width')
+
+call mundo#util#set_default(
+            \ 'g:mundo_preview_height', 15,
+            \ 'g:gundo_preview_height')
+
+call mundo#util#set_default(
+            \ 'g:mundo_preview_bottom', 0,
+            \ 'g:gundo_preview_bottom')
+
+call mundo#util#set_default(
+            \ 'g:mundo_right', 0,
+            \ 'g:gundo_right')
+
+call mundo#util#set_default(
+            \ 'g:mundo_help', 0,
+            \ 'g:gundo_help')
+
+call mundo#util#set_default(
+            \ 'g:mundo_map_move_older', 'j',
+            \ 'g:gundo_map_move_older')
+
+call mundo#util#set_default(
+            \ 'g:mundo_map_move_newer', 'k',
+            \ 'g:gundo_map_move_newer')
+
+call mundo#util#set_default(
+            \ 'g:mundo_close_on_revert', 0,
+            \ 'g:gundo_close_on_revert')
+
+call mundo#util#set_default(
+            \ 'g:mundo_prefer_python3', 0,
+            \ 'g:gundo_prefer_python3')
+
+call mundo#util#set_default(
+            \ 'g:mundo_auto_preview', 1,
+            \ 'g:gundo_auto_preview')
+
+call mundo#util#set_default(
+            \ 'g:mundo_verbose_graph', 1,
+            \ 'g:gundo_verbose_graph')
+
+call mundo#util#set_default(
+            \ 'g:mundo_playback_delay', 60,
+            \ 'g:gundo_playback_delay')
+
+call mundo#util#set_default(
+            \ 'g:mundo_mirror_graph', 0,
+            \ 'g:gundo_mirror_graph')
+
+call mundo#util#set_default(
+            \ 'g:mundo_inline_undo', 0,
+            \ 'g:gundo_inline_undo')
+
+call mundo#util#set_default(
+            \ 'g:mundo_return_on_revert', 1,
+            \ 'g:gundo_return_on_revert')
+
+function! mundo#util#init() abort
+
+endfunction
+
+func! mundo#util#MundoToggle()
+    echohl WarningMsg
+    echomsg "GundoToggle commands are deprecated. Please change to their corresponding MundoToggle command"
+    echohl None
+endf
+func! mundo#util#MundoShow()
+    echohl WarningMsg
+    echomsg "GundoShow commands are deprecated. Please change to their corresponding MundoShow command"
+    echohl None
+endf
+func! mundo#util#MundoHide()
+    echohl WarningMsg
+    echomsg "GundoHide commands are deprecated. Please change to their corresponding MundoHide command"
+    echohl None
+endf
+func! mundo#util#MundoRenderGraph()
+    echohl WarningMsg
+    echomsg "GundoRenderGraph commands are deprecated. Please change to their corresponding MundoRenderGraph command"
+    echohl None
+endf
+
+let g:Mundo_PluginLoaded = 1
+
+let &cpo = s:save_cpo
+unlet s:save_cpo

+ 150 - 74
vim/doc/gundo.txt → vim/doc/mundo.txt

@@ -1,32 +1,38 @@
-*gundo.txt*   Graph your undo tree so you can actually USE it.
+*mundo.txt*   Graph your undo tree so you can actually USE it.
 
 Making Vim's undo tree usable by humans.
 
 ==============================================================================
-CONTENTS                                                      *Gundo-contents*
-
-    1. Intro .......................... |GundoIntro|
-    2. Usage .......................... |GundoUsage|
-    3. Configuration .................. |GundoConfig|
-        3.1 gundo_width ............... |gundo_width|
-        3.2 gundo_preview_height ...... |gundo_preview_height|
-        3.3 gundo_preview_bottom ...... |gundo_preview_bottom|
-        3.4 gundo_right ............... |gundo_right|
-        3.5 gundo_help ................ |gundo_help|
-        3.6 gundo_disable ............. |gundo_disable|
-        3.7 gundo_map_move_older ...... |gundo_map_move_older|
-            gundo_map_move_newer ...... |gundo_map_move_newer|
-        3.8 gundo_close_on_revert ..... |gundo_close_on_revert|
-        3.9 gundo_preview_statusline .. |gundo_preview_statusline|
-            gundo_tree_statusline ..... |gundo_tree_statusline|
-    4. License ........................ |GundoLicense|
-    5. Bugs ........................... |GundoBugs|
-    6. Contributing ................... |GundoContributing|
-    7. Changelog ...................... |GundoChangelog|
-    8. Credits ........................ |GundoCredits|
+CONTENTS                                                      *Mundo-contents*
+
+    1. Intro ........................... |MundoIntro|
+    2. Usage ........................... |MundoUsage|
+    3. Configuration ................... |MundoConfig|
+        3.1  mundo_width ............... |mundo_width|
+        3.2  mundo_preview_height ...... |mundo_preview_height|
+        3.3  mundo_preview_bottom ...... |mundo_preview_bottom|
+        3.4  mundo_right ............... |mundo_right|
+        3.5  mundo_help ................ |mundo_help|
+        3.6  mundo_disable ............. |mundo_disable|
+        3.7  mundo_map_move_older ...... |mundo_map_move_older|
+             mundo_map_move_newer ...... |mundo_map_move_newer|
+        3.8  mundo_close_on_revert ..... |mundo_close_on_revert|
+        3.9  mundo_preview_statusline .. |mundo_preview_statusline|
+             mundo_tree_statusline ..... |mundo_tree_statusline|
+        3.10 mundo_auto_preview ........ |mundo_auto_preview|
+        3.11 mundo_verbose_graph ....... |mundo_verbose_graph|
+        3.12 mundo_playback_delay ...... |mundo_playback_delay|
+        3.13 mundo_mirror_graph ........ |mundo_mirror_graph|
+        3.14 mundo_inline_undo ......... |mundo_inline_undo|
+        3.15 mundo_return_on_revert .... |mundo_return_on_revert|
+    4. License ......................... |MundoLicense|
+    5. Bugs ............................ |MundoBugs|
+    6. Contributing .................... |MundoContributing|
+    7. Changelog ....................... |MundoChangelog|
+    8. Credits ......................... |MundoCredits|
 
 ==============================================================================
-1. Intro                                                          *GundoIntro*
+1. Intro                                                          *MundoIntro*
 
 You know that Vim lets you undo changes like any text editor. What you might
 not know is that it doesn't just keep a list of your changes -- it keeps
@@ -40,17 +46,17 @@ The problem is that trying to do this in the real world is painful. Vim gives
 you an |:undolist| command that shows you the leaves of the tree. Good luck
 finding the change you want in that list.
 
-Gundo is a plugin to make browsing this ridiculously powerful undo tree less
+Mundo is a plugin to make browsing this ridiculously powerful undo tree less
 painful.
 
 ==============================================================================
-2. Usage                                                          *GundoUsage*
+2. Usage                                                          *MundoUsage*
 
 We'll get to the technical details later, but if you're a human the first
 thing you need to do is add a mapping to your |:vimrc| to toggle the undo
 graph: >
 
-    nnoremap <F5> :GundoToggle<CR>
+    nnoremap <F5> :MundoToggle<CR>
 
 Change the mapped key to suit your taste. We'll stick with F5 because that's
 what the author uses.
@@ -60,7 +66,7 @@ look something like this: >
 
       Undo graph                          File
     +-----------------------------------+------------------------------------+
-    | " Gundo for something.txt [1]     |one                                 |
+    | " Mundo for something.txt [1]     |one                                 |
     | " j/k  - move between undo states |two                                 |
     | " <cr> - revert to that state     |three                               |
     |                                   |five                                |
@@ -70,7 +76,7 @@ look something like this: >
     | | |                               |                                    |
     | o |  [3] 4 hours ago              |                                    |
     | | |                               |                                    |
-    | o |  [2] 4 hours ago              |                                    |
+    | w |  [2] 4 hours ago              |                                    |
     | |/                                |                                    |
     | o  [1] 4 hours ago                |                                    |
     | |                                 |                                    |
@@ -86,10 +92,11 @@ look something like this: >
     +-----------------------------------+------------------------------------+
       Preview pane
 
-Your current position in the undo tree is marked with an '@' character. Other
-nodes are marked with an 'o' character.
+Your current position in the undo tree is marked with an '@' character. Undo
+positions that were saved to disk are marked with a 'w'. Other nodes are marked
+with an 'o' character.
 
-When you toggle open the graph Gundo will put your cursor on your current
+When you toggle open the graph Mundo will put your cursor on your current
 position in the tree. You can move up and down the graph with the j and
 k keys.
 
@@ -116,31 +123,31 @@ Pressing q while in the undo graph will close it.  You can also just press your
 toggle mapping key.
 
 ==============================================================================
-3. Configuration                                                 *GundoConfig*
+3. Configuration                                                 *MundoConfig*
 
-You can tweak the behavior of Gundo by setting a few variables in your :vimrc
+You can tweak the behavior of Mundo by setting a few variables in your :vimrc
 file. For example: >
 
-    let g:gundo_width = 60
-    let g:gundo_preview_height = 40
-    let g:gundo_right = 1
+    let g:mundo_width = 60
+    let g:mundo_preview_height = 40
+    let g:mundo_right = 1
 
 ------------------------------------------------------------------------------
-3.1 g:gundo_width                                                *gundo_width*
+3.1 g:mundo_width                                                *mundo_width*
 
-Set the horizontal width of the Gundo graph (and preview).
+Set the horizontal width of the Mundo graph (and preview).
 
 Default: 45
 
 ------------------------------------------------------------------------------
-3.2 g:gundo_preview_height                              *gundo_preview_height*
+3.2 g:mundo_preview_height                              *mundo_preview_height*
 
-Set the vertical height of the Gundo preview.
+Set the vertical height of the Mundo preview.
 
 Default: 15
 
 ------------------------------------------------------------------------------
-3.3 g:gundo_preview_bottom                              *gundo_preview_bottom*
+3.3 g:mundo_preview_bottom                              *mundo_preview_bottom*
 
 Force the preview window below current windows instead of below the graph.
 This gives the preview window more space to show the unified diff.
@@ -157,78 +164,147 @@ Example:
 Default: 0
 
 ------------------------------------------------------------------------------
-3.4 g:gundo_right                                                *gundo_right*
+3.4 g:mundo_right                                                *mundo_right*
 
-Set this to 1 to make the Gundo graph (and preview) open on the right side
+Set this to 1 to make the Mundo graph (and preview) open on the right side
 instead of the left.
 
 Default: 0 (off, open on the left side)
 
 ------------------------------------------------------------------------------
-3.5 g:gundo_help                                                  *gundo_help*
+3.5 g:mundo_help                                                  *mundo_help*
 
-Set this to 0 to disable the help text in the Gundo graph window.
+Set this to 0 to disable the help text in the Mundo graph window.
 
 Default: 1 (show the help)
 
 ------------------------------------------------------------------------------
-3.6 g:gundo_disable                                            *gundo_disable*
+3.6 g:mundo_disable                                            *mundo_disable*
 
-Set this to 1 to disable Gundo entirely.
+Set this to 1 to disable Mundo entirely.
 
 Useful if you use the same ~/.vim folder on multiple machines, and some of
 them may not have Python support.
 
-Default: 0 (Gundo is enabled as usual)
+Default: 0 (Mundo is enabled as usual)
 
 ------------------------------------------------------------------------------
-3.7 g:gundo_map_move_older, g:gundo_map_move_newer      *gundo_map_move_older*
-                                                        *gundo_map_move_newer*
+3.7 g:mundo_map_move_older, g:mundo_map_move_newer      *mundo_map_move_older*
+                                                        *mundo_map_move_newer*
 
 These options let you change the keys that navigate the undo graph. This is
 useful if you use a Dvorak keyboard and have changed your movement keys.
 
-Default: gundo_map_move_older = "j"
-         gundo_map_move_newer = "k"
+Default: mundo_map_move_older = "j"
+         mundo_map_move_newer = "k"
 
 ------------------------------------------------------------------------------
-3.8 g:gundo_close_on_revert                            *gundo_close_on_revert*
+3.8 g:mundo_close_on_revert                            *mundo_close_on_revert*
 
-Set this to 1 to automatically close the Gundo windows when reverting.
+Set this to 1 to automatically close the Mundo windows when reverting.
 
 Default: 0 (windows do not automatically close)
 
 ------------------------------------------------------------------------------
-3.9 g:gundo_preview_statusline                      *gundo_preview_statusline*
-    g:gundo_tree_statusline                            *gundo_tree_statusline*
+3.9 g:mundo_preview_statusline                      *mundo_preview_statusline*
+    g:mundo_tree_statusline                            *mundo_tree_statusline*
 
-Set these to a string to display it as the status line for each Gundo window.
+Set these to a string to display it as the status line for each Mundo window.
 
 Default: unset (windows use the default statusline)
 
+------------------------------------------------------------------------------
+3.10 g:mundo_auto_preview                                 *mundo_auto_preview*
+
+Set this to 0 to disable automatically rendering preview diffs as you move
+through the undo tree (you can still render a specific diff with r).  This can
+be useful on large files and undo trees to speed up Mundo.
+
+Default: 1 (automatically preview diffs)
+
+------------------------------------------------------------------------------
+3.11 g:mundo_verbose_graph                               *mundo_verbose_graph*
+
+Set this to 0 to create shorter graphs: the 'o' characters will only be used
+when multiple branches exist, and extra lines of '|' are suppressed making for
+a graph half as long.
+
+Default: 1 (verbose graphs)
+
+------------------------------------------------------------------------------
+3.12 g:mundo_playback_delay                             *mundo_playback_delay*
+
+This is the delay in milliseconds between each change when running 'play to'
+mode. Set this to a higher number for a slower playback or to a lower number
+for a faster playback.
+
+Default: 60
+
+------------------------------------------------------------------------------
+3.13 g:mundo_mirror_graph                                *mundo_mirror_graph*
+
+Set this to 0 to align the graph to the left; set to 1 to align to the right.
+
+Default: 1 (mirror graph)
+
+------------------------------------------------------------------------------
+3.14 g:mundo_inline_graph                                *mundo_inline_undo*
+
+When enabled, a small one line diff is displayed to the right of the graph undo.
+Although not as detailed as a full diff provided in the preview window, it
+provides a quick summary of the diff w/o having to navigate.
+
+Default: 0 (no inline graph)
+
+------------------------------------------------------------------------------
+3.15 g:mundo_return_on_revert                         *mundo_return_on_revert*
+
+Set this to 0 to keep focus in the Mundo window after a revert.
+
+Default: 1
+
 ==============================================================================
-4. License                                                      *GundoLicense*
+4. License                                                      *MundoLicense*
 
 GPLv2+. Look it up.
 
 ==============================================================================
-5. Bugs                                                            *GundoBugs*
+5. Bugs                                                            *MundoBugs*
 
 If you find a bug please post it on the issue tracker:
-http://bitbucket.org/sjl/gundo.vim/issues?status=new&status=open
+
+https://github.com/simnalamburt/vim-mundo/issues
 
 ==============================================================================
-6. Contributing                                            *GundoContributing*
+6. Contributing                                            *MundoContributing*
 
 Think you can make this plugin better? Awesome. Fork it on BitBucket or GitHub
 and send a pull request.
 
-BitBucket: http://bitbucket.org/sjl/gundo.vim/
-GitHub: http://github.com/sjl/gundo.vim/
+GitHub: https://github.com/simnalamburt/vim-mundo
 
 ==============================================================================
-7. Changelog                                                  *GundoChangelog*
-
+7. Changelog                                                  *MundoChangelog*
+
+v3.0.0
+    * Rename plugin
+    * Add one line diff in __Mundo__ window.
+    * Adds g:mundo_mirror_graph setting.
+    * Adds g:mundo_return_on_revert setting.
+    * Adds ability to toggle help on/off in __Mundo__ window.
+    * Adds J and K commands (navigate between written undos)
+    * Adds /, n and N commands (search undos)
+    * Adds g:mundo_verbose_graph setting.
+    * Adds g:mundo_mirror_graph setting.
+    * Show written undos in the Mundo graph.
+    * Adds live reload of Mundo as you edit.
+v2.5.0
+    * Fix the help window to take custom mappings into account.
+    * Add g:mundo_playback_delay option.
+v2.4.0
+    * Add auto preview option.
+    * Add 'r' mapping to preview current state.
+    * Add public mundo#MundoShow() and mundo#MundoHide() functions.
 v2.3.0
     * Add statusline configuration.
 v2.2.2
@@ -236,23 +312,23 @@ v2.2.2
 v2.2.1
     * Refactoring and performance improvements.
 v2.2.0
-    * Add the g:gundo_close_on_revert setting.
+    * Add the g:mundo_close_on_revert setting.
     * Fix a bug with the splitbelow setting.
 v2.1.1
     * Fix a bug with the movement key mappings.
 v2.1.0
     * Warnings about having an incompatible Vim and/or Python installation
-      are now deferred until the first time you try to use Gundo, instead
+      are now deferred until the first time you try to use Mundo, instead
       of being displayed on launch.
     * The <j> and <k> mappings are now configurable with
-      g:gundo_map_move_older and g:gundo_map_move_newer.
-    * The o, <Up> and <Down> keys are now mapped in the Gundo pane.
-    * Improve and add several unit tests for Gundo.
+      g:mundo_map_move_older and g:mundo_map_move_newer.
+    * The o, <Up> and <Down> keys are now mapped in the Mundo pane.
+    * Improve and add several unit tests for Mundo.
 v2.0.0
-    * Make GundoToggle close the Gundo windows if they're visible but not the
+    * Make MundoToggle close the Mundo windows if they're visible but not the
       current window, instead of moving to them.
-    * Add the g:gundo_help setting.
-    * Add the g:gundo_disable setting.
+    * Add the g:mundo_help setting.
+    * Add the g:mundo_disable setting.
     * Add the 'p' mapping to preview the result of reverting to the selected
       state.
     * Fix movement commands with counts in the graph.
@@ -260,7 +336,7 @@ v1.0.0
     * Initial stable release.
 
 ==============================================================================
-8. Credits                                                      *GundoCredits*
+8. Credits                                                      *MundoCredits*
 
 The graphing code was all taken from Mercurial, hence the GPLv2+ license.
 

+ 1 - 0
vim/init.vim

@@ -0,0 +1 @@
+../vimrc

+ 0 - 24
vim/plugin/gundo.vim

@@ -1,24 +0,0 @@
-" ============================================================================
-" File:        gundo.vim
-" Description: vim global plugin to visualize your undo tree
-" Maintainer:  Steve Losh <steve@stevelosh.com>
-" 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.
-"
-" ============================================================================
-
-
-"{{{ Init
-if !exists('g:gundo_debug') && (exists('g:gundo_disable') || exists('loaded_gundo') || &cp)"{{{
-    finish
-endif
-let loaded_gundo = 1"}}}
-"}}}
-
-"{{{ Misc
-command! -nargs=0 GundoToggle call gundo#GundoToggle()
-command! -nargs=0 GundoShow call gundo#GundoShow()
-command! -nargs=0 GundoHide call gundo#GundoHide()
-command! -nargs=0 GundoRenderGraph call gundo#GundoRenderGraph()
-"}}}

+ 28 - 0
vim/plugin/mundo.vim

@@ -0,0 +1,28 @@
+" ============================================================================
+" File:        mundo.vim
+" Description: vim global plugin to visualize your undo tree
+" Maintainer:  Hyeon Kim <simnalamburt@gmail.com>
+" 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.
+"
+" ============================================================================
+
+
+"{{{ Init
+if !exists('g:mundo_debug') && (exists('g:mundo_disable') && g:mundo_disable == 1 || exists('loaded_mundo') || &cp)"{{{
+    finish
+endif
+let loaded_mundo = 1"}}}
+"}}}
+
+"{{{ Misc
+command! -nargs=0 MundoToggle call mundo#MundoToggle()
+command! -nargs=0 MundoShow call mundo#MundoShow()
+command! -nargs=0 MundoHide call mundo#MundoHide()
+command! -nargs=0 MundoRenderGraph call mundo#MundoRenderGraph()
+command! -nargs=0 GundoToggle call mundo#util#MundoToggle()
+command! -nargs=0 GundoShow call mundo#util#MundoShow()
+command! -nargs=0 GundoHide call mundo#util#MundoHide()
+command! -nargs=0 GundoRenderGraph call mundo#util#MundoRenderGraph()
+"}}}

+ 4 - 1
vimrc

@@ -50,7 +50,9 @@ nmap <C-k> :lprevious<cr>
 vmap P "_dP
 inoremap <C-d> <C-t>
 inoremap <C-a> <C-d>
-nnoremap U :GundoToggle<CR>
+
+let g:mundo_prefer_python3 = 1
+nnoremap U :MundoToggle<CR>
 
 let g:syntastic_python_checkers = ['pyflakes']
 let g:syntastic_always_populate_loc_list = 1
@@ -74,6 +76,7 @@ au BufNewFile,BufRead *.ccss set ft=clevercss
 
 autocmd FileType c,cpp,perl,php,java,glsl set cindent foldmethod=syntax
 autocmd FileType python set autoindent foldmethod=indent
+autocmd FileType ruby set foldmethod=indent foldnestmax=4
 autocmd FileType sh set autoindent foldmethod=syntax
 autocmd FileType go set foldmethod=syntax foldlevel=0 foldnestmax=1
 autocmd FileType javascript,less,html set foldnestmax=3 formatoptions-=o