--- /dev/null
+*undotree.txt* Display your undo history in a graph
+
+Author: Ming Bai <mbbill AT gmail DOT COM>
+Licence: BSD
+Homepage: https://github.com/mbbill/undotree/
+Version: 4.3
+
+==============================================================================
+CONTENTS *undotree-contents*
+
+ 1. Intro ................................ |undotree-intro|
+ 2. Usage ................................ |undotree-usage|
+ 3. Configuration ........................ |undotree-config|
+ 3.1 undotree_WindowLayout .......... |undotree_WindowLayout|
+ 3.2 undotree_SplitWidth ............ |undotree_SplitWidth|
+ 3.3 undotree_DiffpanelHeight ....... |undotree_DiffpanelHeight|
+ 3.4 undotree_DiffAutoOpen .......... |undotree_DiffAutoOpen|
+ 3.5 undotree_SetFocusWhenToggle .... |undotree_SetFocusWhenToggle|
+ 3.6 undotree_TreeNodeShape ......... |undotree_TreeNodeShape|
+ 3.7 undotree_DiffCommand ........... |undotree_DiffCommand|
+ 3.8 undotree_RelativeTimestamp ..... |undotree_RelativeTimestamp|
+ 3.9 undotree_HighlightChangedText .. |undotree_HighlightChangedText|
+ 3.10 undotree_HighlightSyntaxAdd .... |undotree_HighlightSyntaxAdd|
+ undotree_HighlightSyntaxChange . |undotree_HighlightSyntaxChange|
+ 3.11 Undotree_CustomMap ............. |Undotree_CustomMap|
+ 4. Bugs ................................. |undotree-bugs|
+ 5. Changelog ............................ |undotree-changelog|
+ 6. License .............................. |undotree-license|
+
+==============================================================================
+1. Intro *undotree-intro*
+
+Vim 7.0 added a new feature named Undo branches. Basically it's a kind of
+ability to go back to the text after any change, even if they were undone. Vim
+stores undo history in a tree which you can browse and manipulate through a
+bunch of commands. But that was not enough straightforward and a bit hard to
+use. You may use :help |new-undo-branches| or :help |undo-tree| to get more
+detailed help. Now this plug-in will free you from those commands and bring
+back the power of undo tree.
+
+==============================================================================
+2. Usage *undotree-usage*
+
+Use :UndotreeToggle to toggle the undo-tree panel. You may want to map this
+command to whatever hotkey by adding the following line to your vimrc, take F5
+for example.
+>
+ nnoremap <F5> :UndotreeToggle<cr>
+<
+Then you can try to do some modification, and the undo tree will automatically
+updated afterwards.
+
+There are some hotkeys provided by vim to switch between the changes in
+history, like |u|, |<c-r>|, |g+|, |g-| as well as the |:earlier| and |:later|
+commands.
+
+You may also switch to undotree panel and use the hotkeys to switch between
+history versions. Press ? in undotree window for quick help of hotkeys.
+
+You can monitor the changed text in diff panel which is automatically updated
+when undo/redo happens.
+
+Persistent undo.
+It is highly recommend to enable the persistent undo. If you don't like your
+working directory be messed up with the undo file everywhere, you may add the
+following line to your vimrc in order to make them stored together.
+>
+ if has("persistent_undo")
+ set undodir='~/.undodir/'
+ set undofile
+ endif
+<
+==============================================================================
+3. Configuration *undotree-config*
+
+------------------------------------------------------------------------------
+3.1 g:undotree_WindowLayout *undotree_WindowLayout*
+
+Set the undotree window layout.
+
+Style 1
+>
+ +----------+------------------------+
+ | | |
+ | | |
+ | undotree | |
+ | | |
+ | | |
+ +----------+ |
+ | | |
+ | diff | |
+ | | |
+ +----------+------------------------+
+<
+Style 2
+>
+ +----------+------------------------+
+ | | |
+ | | |
+ | undotree | |
+ | | |
+ | | |
+ +----------+------------------------+
+ | |
+ | diff |
+ | |
+ +-----------------------------------+
+<
+Style 3
+>
+ +------------------------+----------+
+ | | |
+ | | |
+ | | undotree |
+ | | |
+ | | |
+ | +----------+
+ | | |
+ | | diff |
+ | | |
+ +------------------------+----------+
+<
+Style 4
+>
+ +------------------------+----------+
+ | | |
+ | | |
+ | | undotree |
+ | | |
+ | | |
+ +------------------------+----------+
+ | |
+ | diff |
+ | |
+ +-----------------------------------+
+<
+Default: 1
+
+------------------------------------------------------------------------------
+3.2 g:undotree_SplitWidth *undotree_SplitWidth*
+
+Set the undotree window width.
+
+Default: 30
+
+------------------------------------------------------------------------------
+3.3 g:undotree_DiffpanelHeight *undotree_DiffpanelHeight*
+
+Set the diff window height.
+
+Default: 10
+
+------------------------------------------------------------------------------
+3.4 g:undotree_DiffAutoOpen *undotree_DiffAutoOpen*
+
+Set this to 1 to auto open the diff window.
+
+Default: 1
+
+------------------------------------------------------------------------------
+3.5 g:undotree_SetFocusWhenToggle *undotree_SetFocusWhenToggle*
+
+If set to 1, the undotree window will get focus after being opened, otherwise
+focus will stay in current window.
+
+Default: 0
+
+------------------------------------------------------------------------------
+3.6 g:undotree_TreeNodeShape *undotree_TreeNodeShape*
+
+Set the tree node shape.
+
+Default: '*'
+
+------------------------------------------------------------------------------
+3.7 g:undotree_DiffCommand *undotree_DiffCommand*
+
+Set the command used to get the diff output.
+
+Default: "diff"
+
+------------------------------------------------------------------------------
+3.8 g:undotree_RelativeTimestamp *undotree_RelativeTimestamp*
+
+Set to 1 to use relative timestamp.
+
+Default: 1
+
+------------------------------------------------------------------------------
+3.9 g:undotree_HighlightChangedText *undotree_HighlightChangedText*
+
+Set to 1 to highlight the changed text.
+
+Default: 1
+
+------------------------------------------------------------------------------
+3.10 g:undotree_HighlightSyntaxAdd *undotree_HighlightSyntaxAdd*
+ g:undotree_HighlightSyntaxChange *undotree_HighlightSyntaxChange*
+
+Set the highlight linked syntax type.
+You may chose your favorite through ":hi" command.
+
+Default: "DiffAdd" and "DiffChange"
+
+------------------------------------------------------------------------------
+3.11 g:Undotree_CustomMap *Undotree_CustomMap*
+
+There are two ways of changing the default key mappings:
+The first way is to define global mappings as the following example:
+>
+ nmap <buffer> J <plug>UndotreeGoNextState
+ nmap <buffer> K <plug>UndotreeGoPreviousState
+<
+A better approach is to define the callback function g:Undotree_CustomMap().
+The function will be called after the undotree windows is initialized, so the
+key mappings only works on the undotree windows.
+>
+ function g:Undotree_CustomMap()
+ nmap <buffer> J <plug>UndotreeGoNextState
+ nmap <buffer> K <plug>UndotreeGoPreviousState
+ endfunc
+<
+List of the commands available for redefinition.
+>
+ <plug>UndotreeHelp
+ <plug>UndotreeClose
+ <plug>UndotreeFocusTarget
+ <plug>UndotreeClearHistory
+ <plug>UndotreeTimestampToggle
+ <plug>UndotreeDiffToggle
+ <plug>UndotreeGoNextState
+ <plug>UndotreeGoPreviousState
+ <plug>UndotreeGoNextSaved
+ <plug>UndotreeGoPreviousSaved
+ <plug>UndotreeRedo
+ <plug>UndotreeUndo
+ <plug>UndotreeEnter
+ <plug>UndotreeEnter
+<
+==============================================================================
+4. Bugs *undotree-bugs*
+
+Post any issue and feature request here:
+https://github.com/mbbill/undotree/issues
+
+==============================================================================
+5. Changelog *undotree-changelog*
+
+4.3 (2013-02-18)
+ - Several fixes and enhancements.
+
+4.2 (2012-11-24)
+ - Fixed some small issue.
+
+4.1 (2012-09-05)
+ - Enhanced tree style.
+ - Multi-window switching support.
+
+4.0 (2012-08-30)
+ - Live updated highlight for changed text.
+ - Customizable key mappings.
+ - Fixed some minor bugs.
+
+3.1 (2012-08-25)
+ - Add saved status.
+ - Add relative timestamp.
+ - Add ability of clear undo history.
+
+3.0 (2012-08-24)
+ - Add diff panel.
+ - Performance improvement.
+
+2.2 (2012-08-21)
+ - Stable version.
+
+2.1 (2012-08-20)
+ - Fixed some annoying issues.
+
+2.0 (2012-08-19)
+ - Hotkey support.
+ - Handle undo levels.
+ - Auto refresh.
+ - And so on.
+
+1.0 (2012-08-18)
+ - Initial upload
+
+==============================================================================
+6. License *undotree-license*
+
+BSD
--- /dev/null
+"=================================================
+" File: undotree.vim
+" Description: Manage your undo history in a graph.
+" Author: Ming Bai <mbbill@gmail.com>
+" License: BSD
+
+" Avoid installing twice.
+if exists('g:loaded_undotree')
+ finish
+endif
+let g:loaded_undotree = 0
+
+" At least version 7.3 with 005 patch is needed for undo branches.
+" Refer to https://github.com/mbbill/undotree/issues/4 for details.
+" Thanks kien
+if v:version < 703
+ "command! -n=0 -bar UndotreeToggle :echoerr "undotree.vim needs Vim version >= 7.3"
+ finish
+endif
+if (v:version == 703 && !has("patch005"))
+ "command! -n=0 -bar UndotreeToggle :echoerr "undotree.vim needs vim7.3 with patch005 applied."
+ finish
+endif
+let g:loaded_undotree = 1 " Signal plugin availability with a value of 1.
+
+"=================================================
+"Options:
+
+" Window layout
+" style 1
+" +----------+------------------------+
+" | | |
+" | | |
+" | undotree | |
+" | | |
+" | | |
+" +----------+ |
+" | | |
+" | diff | |
+" | | |
+" +----------+------------------------+
+" Style 2
+" +----------+------------------------+
+" | | |
+" | | |
+" | undotree | |
+" | | |
+" | | |
+" +----------+------------------------+
+" | |
+" | diff |
+" | |
+" +-----------------------------------+
+" Style 3
+" +------------------------+----------+
+" | | |
+" | | |
+" | | undotree |
+" | | |
+" | | |
+" | +----------+
+" | | |
+" | | diff |
+" | | |
+" +------------------------+----------+
+" Style 4
+" +-----------------------++----------+
+" | | |
+" | | |
+" | | undotree |
+" | | |
+" | | |
+" +------------------------+----------+
+" | |
+" | diff |
+" | |
+" +-----------------------------------+
+if !exists('g:undotree_WindowLayout')
+ let g:undotree_WindowLayout = 1
+endif
+
+" undotree window width
+if !exists('g:undotree_SplitWidth')
+ let g:undotree_SplitWidth = 30
+endif
+
+" diff window height
+if !exists('g:undotree_DiffpanelHeight')
+ let g:undotree_DiffpanelHeight = 10
+endif
+
+" auto open diff window
+if !exists('g:undotree_DiffAutoOpen')
+ let g:undotree_DiffAutoOpen = 1
+endif
+
+" if set, let undotree window get focus after being opened, otherwise
+" focus will stay in current window.
+if !exists('g:undotree_SetFocusWhenToggle')
+ let g:undotree_SetFocusWhenToggle = 0
+endif
+
+" tree node shape.
+if !exists('g:undotree_TreeNodeShape')
+ let g:undotree_TreeNodeShape = '*'
+endif
+
+if !exists('g:undotree_DiffCommand')
+ let g:undotree_DiffCommand = "diff"
+endif
+
+" relative timestamp
+if !exists('g:undotree_RelativeTimestamp')
+ let g:undotree_RelativeTimestamp = 1
+endif
+
+" Highlight changed text
+if !exists('g:undotree_HighlightChangedText')
+ let g:undotree_HighlightChangedText = 1
+endif
+
+" Highlight linked syntax type.
+" You may chose your favorite through ":hi" command
+if !exists('g:undotree_HighlightSyntaxAdd')
+ let g:undotree_HighlightSyntaxAdd = "DiffAdd"
+endif
+if !exists('g:undotree_HighlightSyntaxChange')
+ let g:undotree_HighlightSyntaxChange = "DiffChange"
+endif
+
+" Deprecates the old style configuration.
+if exists('g:undotree_SplitLocation')
+ echo "g:undotree_SplitLocation is deprecated,
+ \ please use g:undotree_WindowLayout instead."
+endif
+
+"Custom key mappings: add this function to your vimrc.
+"You can define whatever mapping as you like, this is a hook function which
+"will be called after undotree window initialized.
+"
+"function g:Undotree_CustomMap()
+" map <buffer> <c-n> J
+" map <buffer> <c-p> K
+"endfunction
+
+"=================================================
+" Help text
+let s:helpmore = ['" ===== Marks ===== ',
+ \'" >num< : current change',
+ \'" {num} : change to redo',
+ \'" [num] : the last change',
+ \'" s : saved changes',
+ \'" S : last saved change',
+ \'" ===== Hotkeys =====']
+let s:helpless = ['" Press ? for help.']
+
+" Keymap
+let s:keymap = []
+" action, key, help.
+let s:keymap += [['Help','?','Toggle quick help']]
+let s:keymap += [['Close','q','Close this panel']]
+let s:keymap += [['FocusTarget','<tab>','Set Focus to editor']]
+let s:keymap += [['ClearHistory','C','Clear undo history']]
+let s:keymap += [['TimestampToggle','T','Toggle relative timestamp']]
+let s:keymap += [['DiffToggle','D','Toggle diff panel']]
+let s:keymap += [['GoNextState','K','Revert to next state']]
+let s:keymap += [['GoPreviousState','J','Revert to previous state']]
+let s:keymap += [['GoNextSaved','>','Revert to next saved state']]
+let s:keymap += [['GoPreviousSaved','<','Revert to previous saved state']]
+let s:keymap += [['Redo','<c-r>','Redo']]
+let s:keymap += [['Undo','u','Undo']]
+let s:keymap += [['Enter','<2-LeftMouse>','Revert to current']]
+let s:keymap += [['Enter','<cr>','Revert to current']]
+
+function! s:new(obj)
+ let newobj = deepcopy(a:obj)
+ call newobj.Init()
+ return newobj
+endfunction
+
+" Get formatted time
+function! s:gettime(time)
+ if a:time == 0
+ return "Original"
+ endif
+ if !g:undotree_RelativeTimestamp
+ let today = substitute(strftime("%c",localtime())," .*$",'','g')
+ if today == substitute(strftime("%c",a:time)," .*$",'','g')
+ return strftime("%H:%M:%S",a:time)
+ else
+ return strftime("%H:%M:%S %b%d %Y",a:time)
+ endif
+ else
+ let sec = localtime() - a:time
+ if sec < 0
+ let sec = 0
+ endif
+ if sec < 60
+ if sec == 1
+ return '1 second ago'
+ else
+ return sec.' seconds ago'
+ endif
+ endif
+ if sec < 3600
+ if (sec/60) == 1
+ return '1 minute ago'
+ else
+ return (sec/60).' minutes ago'
+ endif
+ endif
+ if sec < 86400 "3600*24
+ if (sec/3600) == 1
+ return '1 hour ago'
+ else
+ return (sec/3600).' hours ago'
+ endif
+ endif
+ return (sec/86400).' days ago'
+ endif
+endfunction
+
+function! s:exec(cmd)
+ call s:log("s:exec() ".a:cmd)
+ silent exe a:cmd
+endfunction
+
+" Don't trigger any events(like BufEnter which could cause redundant refresh)
+function! s:exec_silent(cmd)
+ call s:log("s:exec_silent() ".a:cmd)
+ let ei_bak= &eventignore
+ set eventignore=all
+ silent exe a:cmd
+ let &eventignore = ei_bak
+endfunction
+
+" Return a unique id each time.
+let s:cntr = 0
+function! s:getUniqueID()
+ let s:cntr = s:cntr + 1
+ return s:cntr
+endfunction
+
+" Debug...
+let s:debug = 0
+let s:debugfile = $HOME.'/undotree_debug.log'
+" If debug file exists, enable debug output.
+if filewritable(s:debugfile)
+ let s:debug = 1
+ exec 'redir >> '. s:debugfile
+ silent echo "=======================================\n"
+ redir END
+endif
+
+function! s:log(msg)
+ if s:debug
+ exec 'redir >> ' . s:debugfile
+ silent echon strftime('%H:%M:%S') . ': ' . string(a:msg) . "\n"
+ redir END
+ endif
+endfunction
+
+"=================================================
+"Base class for panels.
+let s:panel = {}
+
+function! s:panel.Init()
+ let self.bufname = "invalid"
+endfunction
+
+function! s:panel.SetFocus()
+ let winnr = bufwinnr(self.bufname)
+ " already focused.
+ if winnr == winnr()
+ return
+ endif
+ if winnr == -1
+ echoerr "Fatal: window does not exist!"
+ return
+ endif
+ call s:log("SetFocus() winnr:".winnr." bufname:".self.bufname)
+ " wincmd would cause cursor outside window.
+ call s:exec_silent("norm! ".winnr."\<c-w>\<c-w>")
+endfunction
+
+function! s:panel.IsVisible()
+ if bufwinnr(self.bufname) != -1
+ return 1
+ else
+ return 0
+ endif
+endfunction
+
+function! s:panel.Hide()
+ call s:log(self.bufname." Hide()")
+ if !self.IsVisible()
+ return
+ endif
+ call self.SetFocus()
+ call s:exec("quit")
+endfunction
+
+"=================================================
+" undotree panel class.
+" extended from panel.
+"
+
+" {rawtree}
+" |
+" | ConvertInput() {seq2index}--> [seq1:index1]
+" v [seq2:index2] ---+
+" {tree} ... |
+" | [asciimeta] |
+" | Render() | |
+" v v |
+" [asciitree] --> [" * | SEQ DDMMYY "] <==> [node1{seq,time,..}] |
+" [" |/ "] [node2{seq,time,..}] <---+
+" ... ...
+
+let s:undotree = s:new(s:panel)
+
+function! s:undotree.Init()
+ let self.bufname = "undotree_".s:getUniqueID()
+ " Increase to make it unique.
+ let self.width = g:undotree_SplitWidth
+ let self.opendiff = g:undotree_DiffAutoOpen
+ let self.targetid = -1
+ let self.targetBufnr = -1
+ let self.rawtree = {} "data passed from undotree()
+ let self.tree = {} "data converted to internal format.
+ let self.seq_last = -1
+ let self.save_last = -1
+ let self.save_last_bak = -1
+
+ " seqs
+ let self.seq_cur = -1
+ let self.seq_curhead = -1
+ let self.seq_newhead = -1
+ let self.seq_saved = {} "{saved value -> seq} pair
+
+ "backup, for mark
+ let self.seq_cur_bak = -1
+ let self.seq_curhead_bak = -1
+ let self.seq_newhead_bak = -1
+
+ let self.asciitree = [] "output data.
+ let self.asciimeta = [] "meta data behind ascii tree.
+ let self.seq2index = {} "table used to convert seq to index.
+ let self.showHelp = 0
+endfunction
+
+function! s:undotree.BindKey()
+ if v:version > 703 || (v:version == 703 && has("patch1261"))
+ let map_options = ' <nowait> '
+ else
+ let map_options = ''
+ endif
+ let map_options = map_options.' <silent> <buffer> '
+ for i in s:keymap
+ silent exec 'nmap '.map_options.i[1].' <plug>Undotree'.i[0]
+ silent exec 'nnoremap '.map_options.'<plug>Undotree'.i[0]
+ \ .' :call <sid>undotreeAction("'.i[0].'")<cr>'
+ endfor
+ if exists('*g:Undotree_CustomMap')
+ call g:Undotree_CustomMap()
+ endif
+endfunction
+
+function! s:undotree.BindAu()
+ " Auto exit if it's the last window
+ augroup Undotree_Main
+ au!
+ au BufEnter <buffer> call s:exitIfLast()
+ au BufEnter,BufLeave <buffer> if exists('t:undotree') |
+ \let t:undotree.width = winwidth(winnr()) | endif
+ au BufWinLeave <buffer> if exists('t:diffpanel') |
+ \call t:diffpanel.Hide() | endif
+ augroup end
+endfunction
+
+function! s:undotree.Action(action)
+ call s:log("undotree.Action() ".a:action)
+ if !self.IsVisible() || bufname("%") != self.bufname
+ echoerr "Fatal: window does not exists."
+ return
+ endif
+ if !has_key(self,'Action'.a:action)
+ echoerr "Fatal: Action does not exists!"
+ return
+ endif
+ silent exec 'call self.Action'.a:action.'()'
+endfunction
+
+" Helper function, do action in target window, and then update itself.
+function! s:undotree.ActionInTarget(cmd)
+ if !self.SetTargetFocus()
+ return
+ endif
+ " Target should be a normal buffer.
+ if (&bt == '') && (&modifiable == 1) && (mode() == 'n')
+ call s:exec(a:cmd)
+ call self.Update()
+ endif
+ " Update not always set current focus.
+ call self.SetFocus()
+endfunction
+
+function! s:undotree.ActionHelp()
+ let self.showHelp = !self.showHelp
+ call self.Draw()
+ call self.MarkSeqs()
+endfunction
+
+function! s:undotree.ActionFocusTarget()
+ call self.SetTargetFocus()
+endfunction
+
+function! s:undotree.ActionEnter()
+ let index = self.Screen2Index(line('.'))
+ if index < 0
+ return
+ endif
+ let seq = self.asciimeta[index].seq
+ if seq == -1
+ return
+ endif
+ if seq == 0
+ call self.ActionInTarget('norm 9999u')
+ return
+ endif
+ call self.ActionInTarget('u '.self.asciimeta[index].seq)
+endfunction
+
+function! s:undotree.ActionUndo()
+ call self.ActionInTarget('undo')
+endfunction
+
+function! s:undotree.ActionRedo()
+ call self.ActionInTarget("redo")
+endfunction
+
+function! s:undotree.ActionGoPreviousState()
+ call self.ActionInTarget('earlier')
+endfunction
+
+function! s:undotree.ActionGoNextState()
+ call self.ActionInTarget('later')
+endfunction
+
+function! s:undotree.ActionGoPreviousSaved()
+ call self.ActionInTarget('earlier 1f')
+endfunction
+
+function! s:undotree.ActionGoNextSaved()
+ call self.ActionInTarget('later 1f')
+endfunction
+
+function! s:undotree.ActionDiffToggle()
+ let self.opendiff = !self.opendiff
+ call t:diffpanel.Toggle()
+ call self.UpdateDiff()
+endfunction
+
+function! s:undotree.ActionTimestampToggle()
+ if !self.SetTargetFocus()
+ return
+ endif
+ let g:undotree_RelativeTimestamp = !g:undotree_RelativeTimestamp
+ let self.targetBufnr = -1 "force update
+ call self.Update()
+ " Update not always set current focus.
+ call self.SetFocus()
+endfunction
+
+function! s:undotree.ActionClearHistory()
+ if confirm("Are you sure to clear ALL undo history?","&Yes\n&No") != 1
+ return
+ endif
+ if !self.SetTargetFocus()
+ return
+ endif
+ let ul_bak = &undolevels
+ let &undolevels = -1
+ call s:exec("norm! a \<BS>\<Esc>")
+ let &undolevels = ul_bak
+ unlet ul_bak
+ let self.targetBufnr = -1 "force update
+ call self.Update()
+endfunction
+
+function! s:undotree.ActionClose()
+ call self.Toggle()
+endfunction
+
+function! s:undotree.UpdateDiff()
+ call s:log("undotree.UpdateDiff()")
+ if !t:diffpanel.IsVisible()
+ return
+ endif
+ call t:diffpanel.Update(self.seq_cur,self.targetBufnr,self.targetid)
+endfunction
+
+" May fail due to target window closed.
+function! s:undotree.SetTargetFocus()
+ for winnr in range(1, winnr('$')) "winnr starts from 1
+ if getwinvar(winnr,'undotree_id') == self.targetid
+ if winnr() != winnr
+ call s:exec("norm! ".winnr."\<c-w>\<c-w>")
+ return 1
+ endif
+ endif
+ endfor
+ return 0
+endfunction
+
+function! s:undotree.Toggle()
+ call s:log(self.bufname." Toggle()")
+ if self.IsVisible()
+ call self.Hide()
+ call t:diffpanel.Hide()
+ call self.SetTargetFocus()
+ else
+ call self.Show()
+ if !g:undotree_SetFocusWhenToggle
+ call self.SetTargetFocus()
+ endif
+ endif
+endfunction
+
+function! s:undotree.GetStatusLine()
+ if self.seq_cur != -1
+ let seq_cur = self.seq_cur
+ else
+ let seq_cur = 'None'
+ endif
+ if self.seq_curhead != -1
+ let seq_curhead = self.seq_curhead
+ else
+ let seq_curhead = 'None'
+ endif
+ return 'current: '.seq_cur.' redo: '.seq_curhead
+endfunction
+
+function! s:undotree.Show()
+ call s:log("undotree.Show()")
+ if self.IsVisible()
+ return
+ endif
+
+ let self.targetid = w:undotree_id
+
+ " Create undotree window.
+ if g:undotree_WindowLayout == 1 || g:undotree_WindowLayout == 2
+ let cmd = "topleft vertical" .
+ \self.width . ' new ' . self.bufname
+ else
+ let cmd = "botright vertical" .
+ \self.width . ' new ' . self.bufname
+ endif
+ call s:exec("silent keepalt ".cmd)
+ call self.SetFocus()
+ setlocal winfixwidth
+ setlocal noswapfile
+ setlocal buftype=nowrite
+ setlocal bufhidden=delete
+ setlocal nowrap
+ setlocal foldcolumn=0
+ setlocal nobuflisted
+ setlocal nospell
+ setlocal nonumber
+ setlocal cursorline
+ setlocal nomodifiable
+ setlocal statusline=%!t:undotree.GetStatusLine()
+ setfiletype undotree
+
+ call self.BindKey()
+ call self.BindAu()
+
+ let ei_bak= &eventignore
+ set eventignore=all
+
+ call self.SetTargetFocus()
+ let self.targetBufnr = -1 "force update
+ call self.Update()
+
+ let &eventignore = ei_bak
+
+ if self.opendiff
+ call t:diffpanel.Show()
+ call self.UpdateDiff()
+ endif
+endfunction
+
+" called outside undotree window
+function! s:undotree.Update()
+ if !self.IsVisible()
+ return
+ endif
+ " do nothing if we're in the undotree or diff panel
+ let bufname = bufname('%')
+ if bufname == self.bufname || bufname == t:diffpanel.bufname
+ return
+ endif
+ if (&bt != '') || (&modifiable == 0) || (mode() != 'n')
+ if self.targetBufnr == bufnr('%') && self.targetid == w:undotree_id
+ call s:log("undotree.Update() invalid buffer NOupdate")
+ return
+ endif
+ let emptybuf = 1 "This is not a valid buffer, could be help or something.
+ call s:log("undotree.Update() invalid buffer update")
+ else
+ let emptybuf = 0
+ "update undotree,set focus
+ if self.targetBufnr == bufnr('%')
+ let self.targetid = w:undotree_id
+ let newrawtree = undotree()
+ if self.rawtree == newrawtree
+ return
+ endif
+
+ " same buffer, but seq changed.
+ if newrawtree.seq_last == self.seq_last
+ call s:log("undotree.Update() update seqs")
+ let self.rawtree = newrawtree
+ call self.ConvertInput(0) "only update seqs.
+ if (self.seq_cur == self.seq_cur_bak) &&
+ \(self.seq_curhead == self.seq_curhead_bak)&&
+ \(self.seq_newhead == self.seq_newhead_bak)&&
+ \(self.save_last == self.save_last_bak)
+ return
+ endif
+ call self.SetFocus()
+ call self.MarkSeqs()
+ call self.UpdateDiff()
+ return
+ endif
+ endif
+ endif
+ call s:log("undotree.Update() update whole tree")
+
+ let self.targetBufnr = bufnr('%')
+ let self.targetid = w:undotree_id
+ if emptybuf " Show an empty undo tree instead of do nothing.
+ let self.rawtree = {'seq_last':0,'entries':[],'time_cur':0,'save_last':0,'synced':1,'save_cur':0,'seq_cur':0}
+ else
+ let self.rawtree = undotree()
+ endif
+ let self.seq_last = self.rawtree.seq_last
+ let self.seq_cur = -1
+ let self.seq_curhead = -1
+ let self.seq_newhead = -1
+ call self.ConvertInput(1) "update all.
+ call self.Render()
+ call self.SetFocus()
+ call self.Draw()
+ call self.MarkSeqs()
+ call self.UpdateDiff()
+endfunction
+
+function! s:undotree.AppendHelp()
+ call append(0,'') "empty line
+ if self.showHelp
+ for i in s:keymap
+ call append(0,'" '.i[1].' : '.i[2])
+ endfor
+ call append(0,s:helpmore)
+ else
+ call append(0,s:helpless)
+ endif
+endfunction
+
+function! s:undotree.Index2Screen(index)
+ " calculate line number according to the help text.
+ " index starts from zero and lineNr starts from 1
+ if self.showHelp
+ " 2 means 1 empty line + 1 index padding (index starts from zero)
+ let lineNr = a:index + len(s:keymap) + len(s:helpmore) + 2
+ else
+ let lineNr = a:index + len(s:helpless) + 2
+ endif
+ return lineNr
+endfunction
+
+" <0 if index is invalid. e.g. current line is in help text.
+function! s:undotree.Screen2Index(line)
+ if self.showHelp
+ let index = a:line - len(s:keymap) - len(s:helpmore) - 2
+ else
+ let index = a:line - len(s:helpless) - 2
+ endif
+ return index
+endfunction
+
+" Current window must be undotree.
+function! s:undotree.Draw()
+ " remember the current cursor position.
+ let savedview = winsaveview()
+
+ setlocal modifiable
+ " Delete text into blackhole register.
+ call s:exec('1,$ d _')
+ call append(0,self.asciitree)
+
+ call self.AppendHelp()
+
+ "remove the last empty line
+ call s:exec('$d _')
+
+ " restore previous cursor position.
+ call winrestview(savedview)
+
+ setlocal nomodifiable
+endfunction
+
+function! s:undotree.MarkSeqs()
+ call s:log("bak(cur,curhead,newhead): ".
+ \self.seq_cur_bak.' '.
+ \self.seq_curhead_bak.' '.
+ \self.seq_newhead_bak)
+ call s:log("(cur,curhead,newhead): ".
+ \self.seq_cur.' '.
+ \self.seq_curhead.' '.
+ \self.seq_newhead)
+ setlocal modifiable
+ " reset bak seq lines.
+ if self.seq_cur_bak != -1
+ let index = self.seq2index[self.seq_cur_bak]
+ call setline(self.Index2Screen(index),self.asciitree[index])
+ endif
+ if self.seq_curhead_bak != -1
+ let index = self.seq2index[self.seq_curhead_bak]
+ call setline(self.Index2Screen(index),self.asciitree[index])
+ endif
+ if self.seq_newhead_bak != -1
+ let index = self.seq2index[self.seq_newhead_bak]
+ call setline(self.Index2Screen(index),self.asciitree[index])
+ endif
+ " mark save seqs
+ for i in keys(self.seq_saved)
+ let index = self.seq2index[self.seq_saved[i]]
+ let lineNr = self.Index2Screen(index)
+ call setline(lineNr,substitute(self.asciitree[index],
+ \' \d\+ \zs \ze','s',''))
+ endfor
+ let max_saved_num = max(keys(self.seq_saved))
+ if max_saved_num > 0
+ let lineNr = self.Index2Screen(self.seq2index[self.seq_saved[max_saved_num]])
+ call setline(lineNr,substitute(getline(lineNr),'s','S',''))
+ endif
+ " mark new seqs.
+ if self.seq_cur != -1
+ let index = self.seq2index[self.seq_cur]
+ let lineNr = self.Index2Screen(index)
+ call setline(lineNr,substitute(getline(lineNr),
+ \'\zs \(\d\+\) \ze [sS ] ','>\1<',''))
+ " move cursor to that line.
+ call s:exec("normal! " . lineNr . "G")
+ endif
+ if self.seq_curhead != -1
+ let index = self.seq2index[self.seq_curhead]
+ let lineNr = self.Index2Screen(index)
+ call setline(lineNr,substitute(getline(lineNr),
+ \'\zs \(\d\+\) \ze [sS ] ','{\1}',''))
+ endif
+ if self.seq_newhead != -1
+ let index = self.seq2index[self.seq_newhead]
+ let lineNr = self.Index2Screen(index)
+ call setline(lineNr,substitute(getline(lineNr),
+ \'\zs \(\d\+\) \ze [sS ] ','[\1]',''))
+ endif
+ setlocal nomodifiable
+endfunction
+
+" tree node class
+let s:node = {}
+
+function! s:node.Init()
+ let self.seq = -1
+ let self.p = []
+ let self.time = -1
+endfunction
+
+function! s:undotree._parseNode(in,out)
+ " type(in) == type([]) && type(out) == type({})
+ if empty(a:in) "empty
+ return
+ endif
+ let curnode = a:out
+ for i in a:in
+ if has_key(i,'alt')
+ call self._parseNode(i.alt,curnode)
+ endif
+ let newnode = s:new(s:node)
+ let newnode.seq = i.seq
+ let newnode.time = i.time
+ if has_key(i,'newhead')
+ let self.seq_newhead = i.seq
+ endif
+ if has_key(i,'curhead')
+ let self.seq_curhead = i.seq
+ let self.seq_cur = curnode.seq
+ endif
+ if has_key(i,'save')
+ let self.seq_saved[i.save] = i.seq
+ endif
+ call extend(curnode.p,[newnode])
+ let curnode = newnode
+ endfor
+endfunction
+
+"Sample:
+"let s:test={'seq_last': 4, 'entries': [{'seq': 3, 'alt': [{'seq': 1, 'time': 1345131443}, {'seq': 2, 'time': 1345131445}], 'time': 1345131490}, {'seq': 4, 'time': 1345131492, 'newhead': 1}], 'time_cur': 1345131493, 'save_last': 0, 'synced': 0, 'save_cur': 0, 'seq_cur': 4}
+
+" updatetree: 0: no update, just assign seqs; 1: update and assign seqs.
+function! s:undotree.ConvertInput(updatetree)
+ "reset seqs
+ let self.seq_cur_bak = self.seq_cur
+ let self.seq_curhead_bak = self.seq_curhead
+ let self.seq_newhead_bak = self.seq_newhead
+ let self.save_last_bak = self.save_last
+
+ let self.seq_cur = -1
+ let self.seq_curhead = -1
+ let self.seq_newhead = -1
+ let self.seq_saved = {}
+
+ "Generate root node
+ let root = s:new(s:node)
+ let root.seq = 0
+ let root.time = 0
+
+ call self._parseNode(self.rawtree.entries,root)
+
+ let self.save_last = self.rawtree.save_last
+ " Note: Normally, the current node should be the one that seq_cur points to,
+ " but in fact it's not. May be bug, bug anyway I found a workaround:
+ " first try to find the parent node of 'curhead', if not found, then use
+ " seq_cur.
+ if self.seq_cur == -1
+ let self.seq_cur = self.rawtree.seq_cur
+ endif
+ " undo history is cleared
+ if empty(self.rawtree.entries)
+ let self.seq_cur = 0
+ endif
+ if a:updatetree
+ let self.tree = root
+ endif
+endfunction
+
+
+"=================================================
+" Ascii undo tree generator
+"
+" Example:
+" 6 8 7
+" |/ |
+" 2 4
+" \ |
+" 1 3 5
+" \ | /
+" 0
+
+" Tree sieve, p:fork, x:none
+"
+" x 8
+" 8x | 7
+" 87 \ \
+" x87 6 | |
+" 687 |/ /
+" p7x | | 5
+" p75 | 4 |
+" p45 | 3 |
+" p35 | |/
+" pp 2 |
+" 2p 1 |
+" 1p |/
+" p 0
+" 0
+"
+" Data sample:
+"let example = {'seq':0,'p':[{'seq':1,'p':[{'seq':2,'p':[{'seq':6,'p':[]},{'seq':8,'p':[]}]}]},{'seq':3,'p':[{'seq':4,'p':[{'seq':7,'p':[]}]}]},{'seq':5,'p':[]}]}
+"
+" Convert self.tree -> self.asciitree
+function! s:undotree.Render()
+ " We gonna modify self.tree so we'd better make a copy first.
+ " Can not make a copy because variable nested too deep, gosh.. okay,
+ " fine..
+ " let tree = deepcopy(self.tree)
+ let tree = self.tree
+ let slots = [tree]
+ let out = []
+ let outmeta = []
+ let seq2index = {}
+ let TYPE_E = type({})
+ let TYPE_P = type([])
+ let TYPE_X = type('x')
+ while slots != []
+ "find next node
+ let foundx = 0 " 1 if x element is found.
+ let index = 0 " Next element to be print.
+
+ " Find x element first.
+ for i in range(len(slots))
+ if type(slots[i]) == TYPE_X
+ let foundx = 1
+ let index = i
+ break
+ endif
+ endfor
+
+ " Then, find the element with minimun seq.
+ let minseq = 99999999
+ let minnode = {}
+ if foundx == 0
+ "assume undo level isn't more than this... of course
+ for i in range(len(slots))
+ if type(slots[i]) == TYPE_E
+ if slots[i].seq < minseq
+ let minseq = slots[i].seq
+ let index = i
+ let minnode = slots[i]
+ continue
+ endif
+ endif
+ if type(slots[i]) == TYPE_P
+ for j in slots[i]
+ if j.seq < minseq
+ let minseq = j.seq
+ let index = i
+ let minnode = j
+ continue
+ endif
+ endfor
+ endif
+ endfor
+ endif
+
+ " output.
+ let onespace = " "
+ let newline = onespace
+ let newmeta = {}
+ let node = slots[index]
+ if type(node) == TYPE_X
+ let newmeta = s:new(s:node) "invalid node.
+ if index+1 != len(slots) " not the last one, append '\'
+ for i in range(len(slots))
+ if i < index
+ let newline = newline.'| '
+ endif
+ if i > index
+ let newline = newline.' \'
+ endif
+ endfor
+ endif
+ call remove(slots,index)
+ endif
+ if type(node) == TYPE_E
+ let newmeta = node
+ let seq2index[node.seq]=len(out)
+ for i in range(len(slots))
+ if index == i
+ let newline = newline.g:undotree_TreeNodeShape.' '
+ else
+ let newline = newline.'| '
+ endif
+ endfor
+ let newline = newline.' '.(node.seq).' '.
+ \'('.s:gettime(node.time).')'
+ " update the printed slot to its child.
+ if empty(node.p)
+ let slots[index] = 'x'
+ endif
+ if len(node.p) == 1 "only one child.
+ let slots[index] = node.p[0]
+ endif
+ if len(node.p) > 1 "insert p node
+ let slots[index] = node.p
+ endif
+ let node.p = [] "cut reference.
+ endif
+ if type(node) == TYPE_P
+ let newmeta = s:new(s:node) "invalid node.
+ for k in range(len(slots))
+ if k < index
+ let newline = newline."| "
+ endif
+ if k == index
+ let newline = newline."|/ "
+ endif
+ if k > index
+ let newline = newline."/ "
+ endif
+ endfor
+ call remove(slots,index)
+ if len(node) == 2
+ if node[0].seq > node[1].seq
+ call insert(slots,node[1],index)
+ call insert(slots,node[0],index)
+ else
+ call insert(slots,node[0],index)
+ call insert(slots,node[1],index)
+ endif
+ endif
+ " split P to E+P if elements in p > 2
+ if len(node) > 2
+ call remove(node,index(node,minnode))
+ call insert(slots,minnode,index)
+ call insert(slots,node,index)
+ endif
+ endif
+ unlet node
+ if newline != onespace
+ let newline = substitute(newline,'\s*$','','g') "remove trailing space.
+ call insert(out,newline,0)
+ call insert(outmeta,newmeta,0)
+ endif
+ endwhile
+ let self.asciitree = out
+ let self.asciimeta = outmeta
+ " revert index.
+ let totallen = len(out)
+ for i in keys(seq2index)
+ let seq2index[i] = totallen - 1 - seq2index[i]
+ endfor
+ let self.seq2index = seq2index
+endfunction
+
+"=================================================
+"diff panel
+let s:diffpanel = s:new(s:panel)
+
+function! s:diffpanel.Update(seq,targetBufnr,targetid)
+ call s:log('diffpanel.Update(),seq:'.a:seq.' bufname:'.bufname(a:targetBufnr))
+ if !self.diffexecutable
+ return
+ endif
+ let diffresult = []
+ let self.changes.add = 0
+ let self.changes.del = 0
+
+ if a:seq == 0
+ let diffresult = []
+ else
+ if has_key(self.cache,a:targetBufnr.'_'.a:seq)
+ call s:log("diff cache hit.")
+ let diffresult = self.cache[a:targetBufnr.'_'.a:seq]
+ else
+ let ei_bak = &eventignore
+ set eventignore=all
+ let targetWinnr = -1
+
+ " Double check the target winnr and bufnr
+ for winnr in range(1, winnr('$')) "winnr starts from 1
+ if (getwinvar(winnr,'undotree_id') == a:targetid)
+ \&& winbufnr(winnr) == a:targetBufnr
+ let targetWinnr = winnr
+ endif
+ endfor
+ if targetWinnr == -1
+ return
+ endif
+ call s:exec_silent(targetWinnr." wincmd w")
+
+ " remember and restore cursor and window position.
+ let savedview = winsaveview()
+
+ let new = getbufline(a:targetBufnr,'^','$')
+ silent undo
+ let old = getbufline(a:targetBufnr,'^','$')
+ silent redo
+
+ call winrestview(savedview)
+
+ " diff files.
+ let tempfile1 = tempname()
+ let tempfile2 = tempname()
+ if writefile(old,tempfile1) == -1
+ echoerr "Can not write to temp file:".tempfile1
+ endif
+ if writefile(new,tempfile2) == -1
+ echoerr "Can not write to temp file:".tempfile2
+ endif
+ let diffresult = split(system(g:undotree_DiffCommand.' '.tempfile1.' '.tempfile2),"\n")
+ call s:log("diffresult: ".string(diffresult))
+ if delete(tempfile1) != 0
+ echoerr "Can not delete temp file:".tempfile1
+ endif
+ if delete(tempfile2) != 0
+ echoerr "Can not delete temp file:".tempfile2
+ endif
+ let &eventignore = ei_bak
+ "Update cache
+ let self.cache[a:targetBufnr.'_'.a:seq] = diffresult
+ endif
+ endif
+
+ call self.ParseDiff(diffresult)
+
+ call self.SetFocus()
+
+ setlocal modifiable
+ call s:exec('1,$ d _')
+
+ call append(0,diffresult)
+ call append(0,'- seq: '.a:seq.' -')
+
+ "remove the last empty line
+ if getline("$") == ""
+ call s:exec('$d _')
+ endif
+ call s:exec('norm! gg') "move cursor to line 1.
+ setlocal nomodifiable
+ call t:undotree.SetFocus()
+endfunction
+
+function! s:diffpanel.ParseDiff(diffresult)
+ " set target focus first.
+ call t:undotree.SetTargetFocus()
+
+ if empty(a:diffresult)
+ return
+ endif
+ " clear previous highlighted syntax
+ " matchadd associates with windows.
+ if exists("w:undotree_diffmatches")
+ for i in w:undotree_diffmatches
+ call matchdelete(i)
+ endfor
+ endif
+ let w:undotree_diffmatches = []
+ let lineNr = 0
+ for line in a:diffresult
+ let matchnum = matchstr(line,'^[0-9,\,]*[ac]\zs\d*\ze')
+ if !empty(matchnum)
+ let lineNr = str2nr(matchnum)
+ let matchwhat = matchstr(line,'^[0-9,\,]*\zs[ac]\ze\d*')
+ continue
+ endif
+ if matchstr(line,'^<.*$') != ''
+ let self.changes.del += 1
+ endif
+ let matchtext = matchstr(line,'^>\zs .*$')
+ if empty(matchtext)
+ continue
+ endif
+ let self.changes.add += 1
+ if g:undotree_HighlightChangedText
+ if matchtext != ' '
+ let matchtext = '\%'.lineNr.'l\V'.escape(matchtext[1:],'"\') "remove beginning space.
+ call s:log("matchadd(".matchwhat.") -> ".matchtext)
+ call add(w:undotree_diffmatches,matchadd((matchwhat ==# 'a' ? g:undotree_HighlightSyntaxAdd : g:undotree_HighlightSyntaxChange),matchtext))
+ endif
+ endif
+
+ let lineNr = lineNr+1
+ endfor
+endfunction
+
+function! s:diffpanel.GetStatusLine()
+ let max = winwidth(0) - 4
+ let sum = self.changes.add + self.changes.del
+ if sum > max
+ let add = self.changes.add * max / sum + 1
+ let del = self.changes.del * max / sum + 1
+ else
+ let add = self.changes.add
+ let del = self.changes.del
+ endif
+ return string(sum).' '.repeat('+',add).repeat('-',del)
+endfunction
+
+function! s:diffpanel.Init()
+ let self.bufname = "diffpanel_".s:getUniqueID()
+ let self.cache = {}
+ let self.changes = {'add':0, 'del':0}
+ let self.diffexecutable = executable('diff')
+ if !self.diffexecutable
+ echoerr '"diff" is not executable.'
+ endif
+endfunction
+
+function! s:diffpanel.Toggle()
+ call s:log(self.bufname." Toggle()")
+ if self.IsVisible()
+ call self.Hide()
+ else
+ call self.Show()
+ endif
+endfunction
+
+function! s:diffpanel.Show()
+ call s:log("diffpanel.Show()")
+ if self.IsVisible()
+ return
+ endif
+ " Create diffpanel window.
+ call t:undotree.SetFocus() "can not exist without undotree
+ " remember and restore cursor and window position.
+ let savedview = winsaveview()
+
+ let ei_bak= &eventignore
+ set eventignore=all
+
+ if g:undotree_WindowLayout == 1 || g:undotree_WindowLayout == 3
+ let cmd = 'belowright '.g:undotree_DiffpanelHeight.'new '.self.bufname
+ else
+ let cmd = 'botright '.g:undotree_DiffpanelHeight.'new '.self.bufname
+ endif
+ call s:exec_silent(cmd)
+
+ setlocal winfixwidth
+ setlocal winfixheight
+ setlocal noswapfile
+ setlocal buftype=nowrite
+ setlocal bufhidden=delete
+ setlocal nowrap
+ setlocal nobuflisted
+ setlocal nospell
+ setlocal nonumber
+ setlocal nocursorline
+ setlocal nomodifiable
+ setlocal statusline=%!t:diffpanel.GetStatusLine()
+
+ let &eventignore = ei_bak
+
+ " syntax need filetype autocommand
+ setfiletype diff
+ setlocal foldcolumn=0
+ setlocal nofoldenable
+
+ call self.BindAu()
+ call t:undotree.SetFocus()
+ call winrestview(savedview)
+endfunction
+
+function! s:diffpanel.BindAu()
+ " Auto exit if it's the last window or undotree closed.
+ augroup Undotree_Diff
+ au!
+ au BufEnter <buffer> call s:exitIfLast()
+ au BufEnter <buffer> if !t:undotree.IsVisible()
+ \|call t:diffpanel.Hide() |endif
+ augroup end
+endfunction
+
+function! s:diffpanel.CleanUpHighlight()
+ call s:log("CleanUpHighlight()")
+ " save current position
+ let curwinnr = winnr()
+ let savedview = winsaveview()
+
+ " clear w:undotree_diffmatches in all windows.
+ let winnum = winnr('$')
+ for i in range(1,winnum)
+ call s:exec_silent("norm! ".i."\<c-w>\<c-w>")
+ if exists("w:undotree_diffmatches")
+ for j in w:undotree_diffmatches
+ call matchdelete(j)
+ endfor
+ let w:undotree_diffmatches = []
+ endif
+ endfor
+
+ "restore position
+ call s:exec_silent("norm! ".curwinnr."\<c-w>\<c-w>")
+ call winrestview(savedview)
+endfunction
+
+function! s:diffpanel.Hide()
+ call s:log(self.bufname." Hide()")
+ if !self.IsVisible()
+ return
+ endif
+ call self.SetFocus()
+ call s:exec("quit")
+ call self.CleanUpHighlight()
+endfunction
+
+"=================================================
+" It will set the target of undotree window to the current editing buffer.
+function! s:undotreeAction(action)
+ call s:log("undotreeAction()")
+ if !exists('t:undotree')
+ echoerr "Fatal: t:undotree does not exists!"
+ return
+ endif
+ call t:undotree.Action(a:action)
+endfunction
+
+function! s:exitIfLast()
+ let num = 0
+ if t:undotree.IsVisible()
+ let num = num + 1
+ endif
+ if t:diffpanel.IsVisible()
+ let num = num + 1
+ endif
+ if winnr('$') == num
+ call t:undotree.Hide()
+ call t:diffpanel.Hide()
+ endif
+endfunction
+
+"=================================================
+"called outside undotree window
+function! UndotreeUpdate()
+ if !exists('t:undotree')
+ return
+ endif
+ if !exists('w:undotree_id')
+ let w:undotree_id = 'id_'.s:getUniqueID()
+ call s:log("Unique window id assigned: ".w:undotree_id)
+ endif
+ " assume window layout won't change during updating.
+ let thiswinnr = winnr()
+ call t:undotree.Update()
+ " focus moved
+ if winnr() != thiswinnr
+ call s:exec("norm! ".thiswinnr."\<c-w>\<c-w>")
+ endif
+endfunction
+
+function! UndotreeToggle()
+ call s:log(">>> UndotreeToggle()")
+ if !exists('w:undotree_id')
+ let w:undotree_id = 'id_'.s:getUniqueID()
+ call s:log("Unique window id assigned: ".w:undotree_id)
+ endif
+
+ if !exists('t:undotree')
+ let t:undotree = s:new(s:undotree)
+ let t:diffpanel = s:new(s:diffpanel)
+ endif
+ call t:undotree.Toggle()
+ call s:log("<<< UndotreeToggle() leave")
+endfunction
+
+function! UndotreeIsVisible()
+ return (exists('t:undotree') && t:undotree.IsVisible())
+endfunction
+
+function! UndotreeHide()
+ if UndotreeIsVisible()
+ call UndotreeToggle()
+ endif
+endfunction
+
+function! UndotreeShow()
+ if ! UndotreeIsVisible()
+ call UndotreeToggle()
+ else
+ call t:undotree.SetFocus()
+ endif
+endfunction
+
+function! UndotreeFocus()
+ if UndotreeIsVisible()
+ call t:undotree.SetFocus()
+ endif
+endfunction
+
+
+"let s:auEvents = "InsertEnter,InsertLeave,WinEnter,WinLeave,CursorMoved"
+let s:auEvents = "BufEnter,InsertLeave,CursorMoved,BufWritePost"
+augroup Undotree
+ exec "au! ".s:auEvents." * call UndotreeUpdate()"
+augroup END
+
+"=================================================
+" User commands.
+command! -n=0 -bar UndotreeToggle :call UndotreeToggle()
+command! -n=0 -bar UndotreeHide :call UndotreeHide()
+command! -n=0 -bar UndotreeShow :call UndotreeShow()
+command! -n=0 -bar UndotreeFocus :call UndotreeFocus()
+
+" vim: set et fdm=marker sts=4 sw=4: