undotree.vim 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  1. "=================================================
  2. " File: autoload/undotree.vim
  3. " Description: Manage your undo history in a graph.
  4. " Author: David Knoble <ben.knoble@gmail.com>
  5. " License: BSD
  6. " Avoid installing twice.
  7. if exists('g:autoloaded_undotree')
  8. finish
  9. endif
  10. let g:autoloaded_undotree = 0
  11. " At least version 7.3 with 005 patch is needed for undo branches.
  12. " Refer to https://github.com/mbbill/undotree/issues/4 for details.
  13. " Thanks kien
  14. if v:version < 703
  15. finish
  16. endif
  17. if (v:version == 703 && !has("patch005"))
  18. finish
  19. endif
  20. let g:loaded_undotree = 1 " Signal plugin availability with a value of 1.
  21. " Short time indicators
  22. if g:undotree_ShortIndicators == 1
  23. let s:timeSecond = '1 s'
  24. let s:timeSeconds = ' s'
  25. let s:timeMinute = '1 m'
  26. let s:timeMinutes = ' m'
  27. let s:timeHour = '1 h'
  28. let s:timeHours = ' h'
  29. let s:timeDay = '1 d'
  30. let s:timeDays = ' d'
  31. let s:timeOriginal = 'Orig'
  32. else
  33. let s:timeSecond = '1 second ago'
  34. let s:timeSeconds = ' seconds ago'
  35. let s:timeMinute = '1 minute ago'
  36. let s:timeMinutes = ' minutes ago'
  37. let s:timeHour = '1 hour ago'
  38. let s:timeHours = ' hours ago'
  39. let s:timeDay = '1 day ago'
  40. let s:timeDays = ' days ago'
  41. let s:timeOriginal = 'Original'
  42. endif
  43. "=================================================
  44. " Help text
  45. let s:helpmore = ['" ===== Marks ===== ',
  46. \'" >num< : The current state',
  47. \'" {num} : The next redo state',
  48. \'" [num] : The latest state',
  49. \'" s : Saved states',
  50. \'" S : The last saved state',
  51. \'" ===== Hotkeys =====']
  52. if !g:undotree_HelpLine
  53. let s:helpless = []
  54. else
  55. let s:helpless = ['" Press ? for help.']
  56. endif
  57. "Custom key mappings: add this function to your vimrc.
  58. "You can define whatever mapping as you like, this is a hook function which
  59. "will be called after undotree window initialized.
  60. "
  61. "function g:Undotree_CustomMap()
  62. " map <buffer> <c-n> J
  63. " map <buffer> <c-p> K
  64. "endfunction
  65. " Keymap
  66. let s:keymap = []
  67. " action, key, help.
  68. let s:keymap += [['Help','?','Toggle quick help']]
  69. let s:keymap += [['Close','q','Close undotree panel']]
  70. let s:keymap += [['FocusTarget','<tab>','Set Focus back to the editor']]
  71. let s:keymap += [['ClearHistory','C','Clear undo history (with confirmation)']]
  72. let s:keymap += [['TimestampToggle','T','Toggle relative timestamp']]
  73. let s:keymap += [['DiffToggle','D','Toggle the diff panel']]
  74. let s:keymap += [['NextState','K','Move to the next undo state']]
  75. let s:keymap += [['PreviousState','J','Move to the previous undo state']]
  76. let s:keymap += [['NextSavedState','>','Move to the next saved state']]
  77. let s:keymap += [['PreviousSavedState','<','Move to the previous saved state']]
  78. let s:keymap += [['Redo','<c-r>','Redo']]
  79. let s:keymap += [['Undo','u','Undo']]
  80. let s:keymap += [['Enter','<2-LeftMouse>','Move to the current state']]
  81. let s:keymap += [['Enter','<cr>','Move to the current state']]
  82. " 'Diff' sign definitions. There are two 'delete' signs; a 'normal' one and one
  83. " that is used if the very end of the buffer has been deleted (in which case the
  84. " deleted text is actually bejond the end of the current buffer version and therefore
  85. " it is not possible to place a sign on the exact line - because it doesn't exist.
  86. " Instead, a 'special' delete sign is placed on the (existing) last line of the
  87. " buffer)
  88. exe 'sign define UndotreeAdd text=++ texthl='.undotree_HighlightSyntaxAdd
  89. exe 'sign define UndotreeChg text=~~ texthl='.undotree_HighlightSyntaxChange
  90. exe 'sign define UndotreeDel text=-- texthl='.undotree_HighlightSyntaxDel
  91. exe 'sign define UndotreeDelEnd text=-v texthl='.undotree_HighlightSyntaxDel
  92. " Id to use for all signs. This is an arbirary number that is hoped to be unique
  93. " within the instance of vim. There is no way of guaranteeing it IS unique, which
  94. " is a shame because it needs to be!
  95. "
  96. " Note that all signs are placed with the same Id - as long as we keep a count of
  97. " how many we have placed (so we can remove them all again), this is ok
  98. let s:signId = 2123654789
  99. "=================================================
  100. function! s:new(obj) abort
  101. let newobj = deepcopy(a:obj)
  102. call newobj.Init()
  103. return newobj
  104. endfunction
  105. " Get formatted time
  106. function! s:gettime(time) abort
  107. if a:time == 0
  108. return s:timeOriginal
  109. endif
  110. if !g:undotree_RelativeTimestamp
  111. let today = substitute(strftime("%c",localtime())," .*$",'','g')
  112. if today == substitute(strftime("%c",a:time)," .*$",'','g')
  113. return strftime("%H:%M:%S",a:time)
  114. else
  115. return strftime("%H:%M:%S %b%d %Y",a:time)
  116. endif
  117. else
  118. let sec = localtime() - a:time
  119. if sec < 0
  120. let sec = 0
  121. endif
  122. if sec < 60
  123. if sec == 1
  124. return s:timeSecond
  125. else
  126. return sec.s:timeSeconds
  127. endif
  128. endif
  129. if sec < 3600
  130. if (sec/60) == 1
  131. return s:timeMinute
  132. else
  133. return (sec/60).s:timeMinutes
  134. endif
  135. endif
  136. if sec < 86400 "3600*24
  137. if (sec/3600) == 1
  138. return s:timeHour
  139. else
  140. return (sec/3600).s:timeHours
  141. endif
  142. endif
  143. if (sec/86400) == 1
  144. return s:timeDay
  145. else
  146. return (sec/86400).s:timeDays
  147. endif
  148. endif
  149. endfunction
  150. function! s:exec(cmd) abort
  151. call s:log("s:exec() ".a:cmd)
  152. silent exe a:cmd
  153. endfunction
  154. " Don't trigger any events(like BufEnter which could cause redundant refresh)
  155. function! s:exec_silent(cmd) abort
  156. call s:log("s:exec_silent() ".a:cmd)
  157. let ei_bak= &eventignore
  158. set eventignore=BufEnter,BufLeave,BufWinLeave,InsertLeave,CursorMoved,BufWritePost
  159. silent exe a:cmd
  160. let &eventignore = ei_bak
  161. endfunction
  162. " Return a unique id each time.
  163. let s:cntr = 0
  164. function! s:getUniqueID() abort
  165. let s:cntr = s:cntr + 1
  166. return s:cntr
  167. endfunction
  168. " Set to 1 to enable debug log
  169. let s:debug = 0
  170. let s:debugfile = $HOME.'/undotree_debug.log'
  171. " If debug file exists, enable debug output.
  172. if filewritable(s:debugfile)
  173. let s:debug = 1
  174. exec 'redir >> '. s:debugfile
  175. silent echo "=======================================\n"
  176. redir END
  177. endif
  178. function! s:log(msg) abort
  179. if s:debug
  180. exec 'redir >> ' . s:debugfile
  181. silent echon strftime('%H:%M:%S') . ': ' . string(a:msg) . "\n"
  182. redir END
  183. endif
  184. endfunction
  185. function! s:ObserveOptions()
  186. augroup Undotree_OptionsObserver
  187. try
  188. autocmd!
  189. if exists('+fdo')
  190. let s:open_folds = &fdo =~# 'undo'
  191. if exists('##OptionSet')
  192. autocmd OptionSet foldopen let s:open_folds = v:option_new =~# 'undo'
  193. endif
  194. endif
  195. finally
  196. augroup END
  197. endtry
  198. endfunction
  199. " Whether to open folds on undo/redo.
  200. " Is 1 when 'undo' is in &fdo (see :help 'foldopen').
  201. " default: 1
  202. let s:open_folds = 1
  203. if exists('v:vim_did_enter')
  204. if !v:vim_did_enter
  205. autocmd VimEnter * call s:ObserveOptions()
  206. else
  207. call s:ObserveOptions()
  208. endif
  209. else
  210. autocmd VimEnter * call s:ObserveOptions()
  211. call s:ObserveOptions()
  212. endif
  213. "=================================================
  214. "Base class for panels.
  215. let s:panel = {}
  216. function! s:panel.Init() abort
  217. let self.bufname = "invalid"
  218. endfunction
  219. function! s:panel.SetFocus() abort
  220. let winnr = bufwinnr(self.bufname)
  221. " already focused.
  222. if winnr == winnr()
  223. return
  224. endif
  225. if winnr == -1
  226. echoerr "Fatal: window does not exist!"
  227. return
  228. endif
  229. call s:log("SetFocus() winnr:".winnr." bufname:".self.bufname)
  230. " wincmd would cause cursor outside window.
  231. call s:exec_silent("norm! ".winnr."\<c-w>\<c-w>")
  232. endfunction
  233. function! s:panel.IsVisible() abort
  234. if bufwinnr(self.bufname) != -1
  235. return 1
  236. else
  237. return 0
  238. endif
  239. endfunction
  240. function! s:panel.Hide() abort
  241. call s:log(self.bufname." Hide()")
  242. if !self.IsVisible()
  243. return
  244. endif
  245. call self.SetFocus()
  246. call s:exec("quit")
  247. endfunction
  248. "=================================================
  249. " undotree panel class.
  250. " extended from panel.
  251. "
  252. " {rawtree}
  253. " |
  254. " | ConvertInput() {seq2index}--> [seq1:index1]
  255. " v [seq2:index2] ---+
  256. " {tree} ... |
  257. " | [asciimeta] |
  258. " | Render() | |
  259. " v v |
  260. " [asciitree] --> [" * | SEQ DDMMYY "] <==> [node1{seq,time,..}] |
  261. " [" |/ "] [node2{seq,time,..}] <---+
  262. " ... ...
  263. let s:undotree = s:new(s:panel)
  264. function! s:undotree.Init() abort
  265. let self.bufname = "undotree_".s:getUniqueID()
  266. " Increase to make it unique.
  267. let self.width = g:undotree_SplitWidth
  268. let self.opendiff = g:undotree_DiffAutoOpen
  269. let self.targetid = -1
  270. let self.targetBufnr = -1
  271. let self.rawtree = {} "data passed from undotree()
  272. let self.tree = {} "data converted to internal format.
  273. let self.seq_last = -1
  274. let self.save_last = -1
  275. let self.save_last_bak = -1
  276. " seqs
  277. let self.seq_cur = -1
  278. let self.seq_curhead = -1
  279. let self.seq_newhead = -1
  280. let self.seq_saved = {} "{saved value -> seq} pair
  281. "backup, for mark
  282. let self.seq_cur_bak = -1
  283. let self.seq_curhead_bak = -1
  284. let self.seq_newhead_bak = -1
  285. let self.asciitree = [] "output data.
  286. let self.asciimeta = [] "meta data behind ascii tree.
  287. let self.seq2index = {} "table used to convert seq to index.
  288. let self.showHelp = 0
  289. endfunction
  290. function! s:undotree.BindKey() abort
  291. if v:version > 703 || (v:version == 703 && has("patch1261"))
  292. let map_options = ' <nowait> '
  293. else
  294. let map_options = ''
  295. endif
  296. let map_options = map_options.' <silent> <buffer> '
  297. for i in s:keymap
  298. silent exec 'nmap '.map_options.i[1].' <plug>Undotree'.i[0]
  299. silent exec 'nnoremap '.map_options.'<plug>Undotree'.i[0]
  300. \ .' :call <sid>undotreeAction("'.i[0].'")<cr>'
  301. endfor
  302. if exists('*g:Undotree_CustomMap')
  303. call g:Undotree_CustomMap()
  304. endif
  305. endfunction
  306. function! s:undotree.BindAu() abort
  307. " Auto exit if it's the last window
  308. augroup Undotree_Main
  309. au!
  310. au BufEnter <buffer> call s:exitIfLast()
  311. au BufEnter,BufLeave <buffer> if exists('t:undotree') |
  312. \let t:undotree.width = winwidth(winnr()) | endif
  313. au BufWinLeave <buffer> if exists('t:diffpanel') |
  314. \call t:diffpanel.Hide() | endif
  315. augroup end
  316. endfunction
  317. function! s:undotree.Action(action) abort
  318. call s:log("undotree.Action() ".a:action)
  319. if !self.IsVisible() || !exists('b:isUndotreeBuffer')
  320. echoerr "Fatal: window does not exist."
  321. return
  322. endif
  323. if !has_key(self,'Action'.a:action)
  324. echoerr "Fatal: Action does not exist!"
  325. return
  326. endif
  327. silent exec 'call self.Action'.a:action.'()'
  328. endfunction
  329. " Helper function, do action in target window, and then update itself.
  330. function! s:undotree.ActionInTarget(cmd) abort
  331. if !self.SetTargetFocus()
  332. return
  333. endif
  334. " Target should be a normal buffer.
  335. if (&bt == '' || &bt == 'acwrite') && (&modifiable == 1) && (mode() == 'n')
  336. call s:exec(a:cmd)
  337. " Open folds so that the change being undone/redone is visible.
  338. if s:open_folds
  339. call s:exec('normal! zv')
  340. endif
  341. call self.Update()
  342. endif
  343. " Update not always set current focus.
  344. call self.SetFocus()
  345. endfunction
  346. function! s:undotree.ActionHelp() abort
  347. let self.showHelp = !self.showHelp
  348. call self.Draw()
  349. call self.MarkSeqs()
  350. endfunction
  351. function! s:undotree.ActionFocusTarget() abort
  352. call self.SetTargetFocus()
  353. endfunction
  354. function! s:undotree.ActionEnter() abort
  355. let index = self.Screen2Index(line('.'))
  356. if index < 0
  357. return
  358. endif
  359. let seq = self.asciimeta[index].seq
  360. if seq == -1
  361. return
  362. endif
  363. if seq == 0
  364. call self.ActionInTarget('norm 9999u')
  365. return
  366. endif
  367. call self.ActionInTarget('u '.self.asciimeta[index].seq)
  368. endfunction
  369. function! s:undotree.ActionUndo() abort
  370. call self.ActionInTarget('undo')
  371. endfunction
  372. function! s:undotree.ActionRedo() abort
  373. call self.ActionInTarget("redo")
  374. endfunction
  375. function! s:undotree.ActionPreviousState() abort
  376. call self.ActionInTarget('earlier')
  377. endfunction
  378. function! s:undotree.ActionNextState() abort
  379. call self.ActionInTarget('later')
  380. endfunction
  381. function! s:undotree.ActionPreviousSavedState() abort
  382. call self.ActionInTarget('earlier 1f')
  383. endfunction
  384. function! s:undotree.ActionNextSavedState() abort
  385. call self.ActionInTarget('later 1f')
  386. endfunction
  387. function! s:undotree.ActionDiffToggle() abort
  388. let self.opendiff = !self.opendiff
  389. call t:diffpanel.Toggle()
  390. call self.UpdateDiff()
  391. endfunction
  392. function! s:undotree.ActionTimestampToggle() abort
  393. if !self.SetTargetFocus()
  394. return
  395. endif
  396. let g:undotree_RelativeTimestamp = !g:undotree_RelativeTimestamp
  397. let self.targetBufnr = -1 "force update
  398. call self.Update()
  399. " Update not always set current focus.
  400. call self.SetFocus()
  401. endfunction
  402. function! s:undotree.ActionClearHistory() abort
  403. if input("Clear ALL undo history? Type \"YES\" to continue: ") != "YES"
  404. return
  405. endif
  406. if !self.SetTargetFocus()
  407. return
  408. endif
  409. let ul_bak = &undolevels
  410. let mod_bak = &modified
  411. let &undolevels = -1
  412. call s:exec("norm! a \<BS>\<Esc>")
  413. let &undolevels = ul_bak
  414. let &modified = mod_bak
  415. unlet ul_bak mod_bak
  416. let self.targetBufnr = -1 "force update
  417. call self.Update()
  418. endfunction
  419. function! s:undotree.ActionClose() abort
  420. call self.Toggle()
  421. endfunction
  422. function! s:undotree.UpdateDiff() abort
  423. call s:log("undotree.UpdateDiff()")
  424. if !t:diffpanel.IsVisible()
  425. return
  426. endif
  427. call t:diffpanel.Update(self.seq_cur,self.targetBufnr,self.targetid)
  428. endfunction
  429. " May fail due to target window closed.
  430. function! s:undotree.SetTargetFocus() abort
  431. for winnr in range(1, winnr('$')) "winnr starts from 1
  432. if getwinvar(winnr,'undotree_id') == self.targetid
  433. if winnr() != winnr
  434. call s:exec("norm! ".winnr."\<c-w>\<c-w>")
  435. return 1
  436. endif
  437. endif
  438. endfor
  439. return 0
  440. endfunction
  441. function! s:undotree.Toggle() abort
  442. "Global auto commands to keep undotree up to date.
  443. let auEvents = "BufEnter,InsertLeave,CursorMoved,BufWritePost"
  444. call s:log(self.bufname." Toggle()")
  445. if self.IsVisible()
  446. call self.Hide()
  447. call t:diffpanel.Hide()
  448. call self.SetTargetFocus()
  449. augroup Undotree
  450. autocmd!
  451. augroup END
  452. else
  453. call self.Show()
  454. if !g:undotree_SetFocusWhenToggle
  455. call self.SetTargetFocus()
  456. endif
  457. augroup Undotree
  458. au!
  459. exec "au! ".auEvents." * call undotree#UndotreeUpdate()"
  460. augroup END
  461. endif
  462. endfunction
  463. function! s:undotree.GetStatusLine() abort
  464. if self.seq_cur != -1
  465. let seq_cur = self.seq_cur
  466. else
  467. let seq_cur = 'None'
  468. endif
  469. if self.seq_curhead != -1
  470. let seq_curhead = self.seq_curhead
  471. else
  472. let seq_curhead = 'None'
  473. endif
  474. return 'current: '.seq_cur.' redo: '.seq_curhead
  475. endfunction
  476. function! s:undotree.Show() abort
  477. call s:log("undotree.Show()")
  478. if self.IsVisible()
  479. return
  480. endif
  481. let self.targetid = w:undotree_id
  482. " Create undotree window.
  483. if exists("g:undotree_CustomUndotreeCmd")
  484. let cmd = g:undotree_CustomUndotreeCmd . ' ' .
  485. \self.bufname
  486. elseif g:undotree_WindowLayout == 1 || g:undotree_WindowLayout == 2
  487. let cmd = "topleft vertical" .
  488. \self.width . ' new ' . self.bufname
  489. else
  490. let cmd = "botright vertical" .
  491. \self.width . ' new ' . self.bufname
  492. endif
  493. call s:exec("silent keepalt ".cmd)
  494. call self.SetFocus()
  495. " We need a way to tell if the buffer is belong to undotree,
  496. " bufname() is not always reliable.
  497. let b:isUndotreeBuffer = 1
  498. setlocal winfixwidth
  499. setlocal noswapfile
  500. setlocal buftype=nowrite
  501. setlocal bufhidden=delete
  502. setlocal nowrap
  503. setlocal nolist
  504. setlocal foldcolumn=0
  505. setlocal nobuflisted
  506. setlocal nospell
  507. setlocal nonumber
  508. setlocal norelativenumber
  509. if g:undotree_CursorLine
  510. setlocal cursorline
  511. else
  512. setlocal nocursorline
  513. endif
  514. setlocal nomodifiable
  515. setlocal statusline=%!t:undotree.GetStatusLine()
  516. setfiletype undotree
  517. call self.BindKey()
  518. call self.BindAu()
  519. let ei_bak= &eventignore
  520. set eventignore=all
  521. call self.SetTargetFocus()
  522. let self.targetBufnr = -1 "force update
  523. call self.Update()
  524. let &eventignore = ei_bak
  525. if self.opendiff
  526. call t:diffpanel.Show()
  527. call self.UpdateDiff()
  528. endif
  529. endfunction
  530. " called outside undotree window
  531. function! s:undotree.Update() abort
  532. if !self.IsVisible()
  533. return
  534. endif
  535. " do nothing if we're in the undotree or diff panel
  536. if exists('b:isUndotreeBuffer')
  537. return
  538. endif
  539. if (&bt != '' && &bt != 'acwrite') || (&modifiable == 0) || (mode() != 'n')
  540. if &bt == 'quickfix' || &bt == 'nofile'
  541. "Do nothing for quickfix and q:
  542. call s:log("undotree.Update() ignore quickfix")
  543. return
  544. endif
  545. if self.targetBufnr == bufnr('%') && self.targetid == w:undotree_id
  546. call s:log("undotree.Update() invalid buffer NOupdate")
  547. return
  548. endif
  549. let emptybuf = 1 "This is not a valid buffer, could be help or something.
  550. call s:log("undotree.Update() invalid buffer update")
  551. else
  552. let emptybuf = 0
  553. "update undotree,set focus
  554. if self.targetBufnr == bufnr('%')
  555. let self.targetid = w:undotree_id
  556. let newrawtree = undotree()
  557. if self.rawtree == newrawtree
  558. return
  559. endif
  560. " same buffer, but seq changed.
  561. if newrawtree.seq_last == self.seq_last
  562. call s:log("undotree.Update() update seqs")
  563. let self.rawtree = newrawtree
  564. call self.ConvertInput(0) "only update seqs.
  565. if (self.seq_cur == self.seq_cur_bak) &&
  566. \(self.seq_curhead == self.seq_curhead_bak)&&
  567. \(self.seq_newhead == self.seq_newhead_bak)&&
  568. \(self.save_last == self.save_last_bak)
  569. return
  570. endif
  571. call self.SetFocus()
  572. call self.MarkSeqs()
  573. call self.UpdateDiff()
  574. return
  575. endif
  576. endif
  577. endif
  578. call s:log("undotree.Update() update whole tree")
  579. let self.targetBufnr = bufnr('%')
  580. let self.targetid = w:undotree_id
  581. if emptybuf " Show an empty undo tree instead of do nothing.
  582. let self.rawtree = {'seq_last':0,'entries':[],'time_cur':0,'save_last':0,'synced':1,'save_cur':0,'seq_cur':0}
  583. else
  584. let self.rawtree = undotree()
  585. endif
  586. let self.seq_last = self.rawtree.seq_last
  587. let self.seq_cur = -1
  588. let self.seq_curhead = -1
  589. let self.seq_newhead = -1
  590. call self.ConvertInput(1) "update all.
  591. call self.Render()
  592. call self.SetFocus()
  593. call self.Draw()
  594. call self.MarkSeqs()
  595. call self.UpdateDiff()
  596. endfunction
  597. function! s:undotree.AppendHelp() abort
  598. if self.showHelp
  599. call append(0,'') "empty line
  600. for i in s:keymap
  601. call append(0,'" '.i[1].' : '.i[2])
  602. endfor
  603. call append(0,s:helpmore)
  604. else
  605. if g:undotree_HelpLine
  606. call append(0,'')
  607. endif
  608. call append(0,s:helpless)
  609. endif
  610. endfunction
  611. function! s:undotree.Index2Screen(index) abort
  612. " index starts from zero
  613. let index_padding = 1
  614. let empty_line = 1
  615. let lineNr = a:index + index_padding + empty_line
  616. " calculate line number according to the help text.
  617. " index starts from zero and lineNr starts from 1
  618. if self.showHelp
  619. let lineNr += len(s:keymap) + len(s:helpmore)
  620. else
  621. let lineNr += len(s:helpless)
  622. if !g:undotree_HelpLine
  623. let lineNr -= empty_line
  624. endif
  625. endif
  626. return lineNr
  627. endfunction
  628. " <0 if index is invalid. e.g. current line is in help text.
  629. function! s:undotree.Screen2Index(line) abort
  630. let index_padding = 1
  631. let empty_line = 1
  632. let index = a:line - index_padding - empty_line
  633. if self.showHelp
  634. let index -= len(s:keymap) + len(s:helpmore)
  635. else
  636. let index -= len(s:helpless)
  637. if !g:undotree_HelpLine
  638. let index += empty_line
  639. endif
  640. endif
  641. return index
  642. endfunction
  643. " Current window must be undotree.
  644. function! s:undotree.Draw() abort
  645. " remember the current cursor position.
  646. let savedview = winsaveview()
  647. setlocal modifiable
  648. " Delete text into blackhole register.
  649. call s:exec('1,$ d _')
  650. call append(0,self.asciitree)
  651. call self.AppendHelp()
  652. "remove the last empty line
  653. call s:exec('$d _')
  654. " restore previous cursor position.
  655. call winrestview(savedview)
  656. setlocal nomodifiable
  657. endfunction
  658. function! s:undotree.MarkSeqs() abort
  659. call s:log("bak(cur,curhead,newhead): ".
  660. \self.seq_cur_bak.' '.
  661. \self.seq_curhead_bak.' '.
  662. \self.seq_newhead_bak)
  663. call s:log("(cur,curhead,newhead): ".
  664. \self.seq_cur.' '.
  665. \self.seq_curhead.' '.
  666. \self.seq_newhead)
  667. setlocal modifiable
  668. " reset bak seq lines.
  669. if self.seq_cur_bak != -1
  670. let index = self.seq2index[self.seq_cur_bak]
  671. call setline(self.Index2Screen(index),self.asciitree[index])
  672. endif
  673. if self.seq_curhead_bak != -1
  674. let index = self.seq2index[self.seq_curhead_bak]
  675. call setline(self.Index2Screen(index),self.asciitree[index])
  676. endif
  677. if self.seq_newhead_bak != -1
  678. let index = self.seq2index[self.seq_newhead_bak]
  679. call setline(self.Index2Screen(index),self.asciitree[index])
  680. endif
  681. " mark save seqs
  682. for i in keys(self.seq_saved)
  683. let index = self.seq2index[self.seq_saved[i]]
  684. let lineNr = self.Index2Screen(index)
  685. call setline(lineNr,substitute(self.asciitree[index],
  686. \' \d\+ \zs \ze','s',''))
  687. endfor
  688. let max_saved_num = max(keys(self.seq_saved))
  689. if max_saved_num > 0
  690. let lineNr = self.Index2Screen(self.seq2index[self.seq_saved[max_saved_num]])
  691. call setline(lineNr,substitute(getline(lineNr),'s','S',''))
  692. endif
  693. " mark new seqs.
  694. if self.seq_cur != -1
  695. let index = self.seq2index[self.seq_cur]
  696. let lineNr = self.Index2Screen(index)
  697. call setline(lineNr,substitute(getline(lineNr),
  698. \'\zs \(\d\+\) \ze [sS ] ','>\1<',''))
  699. " move cursor to that line.
  700. call s:exec("normal! " . lineNr . "G")
  701. endif
  702. if self.seq_curhead != -1
  703. let index = self.seq2index[self.seq_curhead]
  704. let lineNr = self.Index2Screen(index)
  705. call setline(lineNr,substitute(getline(lineNr),
  706. \'\zs \(\d\+\) \ze [sS ] ','{\1}',''))
  707. endif
  708. if self.seq_newhead != -1
  709. let index = self.seq2index[self.seq_newhead]
  710. let lineNr = self.Index2Screen(index)
  711. call setline(lineNr,substitute(getline(lineNr),
  712. \'\zs \(\d\+\) \ze [sS ] ','[\1]',''))
  713. endif
  714. setlocal nomodifiable
  715. endfunction
  716. " tree node class
  717. let s:node = {}
  718. function! s:node.Init() abort
  719. let self.seq = -1
  720. let self.p = []
  721. let self.time = -1
  722. endfunction
  723. function! s:undotree._parseNode(in,out) abort
  724. " type(in) == type([]) && type(out) == type({})
  725. if empty(a:in) "empty
  726. return
  727. endif
  728. let curnode = a:out
  729. for i in a:in
  730. if has_key(i,'alt')
  731. call self._parseNode(i.alt,curnode)
  732. endif
  733. let newnode = s:new(s:node)
  734. let newnode.seq = i.seq
  735. let newnode.time = i.time
  736. if has_key(i,'newhead')
  737. let self.seq_newhead = i.seq
  738. endif
  739. if has_key(i,'curhead')
  740. let self.seq_curhead = i.seq
  741. let self.seq_cur = curnode.seq
  742. endif
  743. if has_key(i,'save')
  744. let self.seq_saved[i.save] = i.seq
  745. endif
  746. call extend(curnode.p,[newnode])
  747. let curnode = newnode
  748. endfor
  749. endfunction
  750. "Sample:
  751. "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}
  752. " updatetree: 0: no update, just assign seqs; 1: update and assign seqs.
  753. function! s:undotree.ConvertInput(updatetree) abort
  754. "reset seqs
  755. let self.seq_cur_bak = self.seq_cur
  756. let self.seq_curhead_bak = self.seq_curhead
  757. let self.seq_newhead_bak = self.seq_newhead
  758. let self.save_last_bak = self.save_last
  759. let self.seq_cur = -1
  760. let self.seq_curhead = -1
  761. let self.seq_newhead = -1
  762. let self.seq_saved = {}
  763. "Generate root node
  764. let root = s:new(s:node)
  765. let root.seq = 0
  766. let root.time = 0
  767. call self._parseNode(self.rawtree.entries,root)
  768. let self.save_last = self.rawtree.save_last
  769. " Note: Normally, the current node should be the one that seq_cur points to,
  770. " but in fact it's not. May be bug, bug anyway I found a workaround:
  771. " first try to find the parent node of 'curhead', if not found, then use
  772. " seq_cur.
  773. if self.seq_cur == -1
  774. let self.seq_cur = self.rawtree.seq_cur
  775. endif
  776. " undo history is cleared
  777. if empty(self.rawtree.entries)
  778. let self.seq_cur = 0
  779. endif
  780. if a:updatetree
  781. let self.tree = root
  782. endif
  783. endfunction
  784. "=================================================
  785. " Ascii undo tree generator
  786. "
  787. " Example:
  788. " 6 8 7
  789. " |/ |
  790. " 2 4
  791. " \ |
  792. " 1 3 5
  793. " \ | /
  794. " 0
  795. " Tree sieve, p:fork, x:none
  796. "
  797. " x 8
  798. " 8x | 7
  799. " 87 \ \
  800. " x87 6 | |
  801. " 687 |/ /
  802. " p7x | | 5
  803. " p75 | 4 |
  804. " p45 | 3 |
  805. " p35 | |/
  806. " pp 2 |
  807. " 2p 1 |
  808. " 1p |/
  809. " p 0
  810. " 0
  811. "
  812. " Data sample:
  813. "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':[]}]}
  814. "
  815. " Convert self.tree -> self.asciitree
  816. function! s:undotree.Render() abort
  817. " We gonna modify self.tree so we'd better make a copy first.
  818. " Cannot make a copy because variable nested too deep, gosh.. okay,
  819. " fine..
  820. " let tree = deepcopy(self.tree)
  821. let tree = self.tree
  822. let slots = [tree]
  823. let out = []
  824. let outmeta = []
  825. let seq2index = {}
  826. let TYPE_E = type({})
  827. let TYPE_P = type([])
  828. let TYPE_X = type('x')
  829. while slots != []
  830. "find next node
  831. let foundx = 0 " 1 if x element is found.
  832. let index = 0 " Next element to be print.
  833. " Find x element first.
  834. for i in range(len(slots))
  835. if type(slots[i]) == TYPE_X
  836. let foundx = 1
  837. let index = i
  838. break
  839. endif
  840. endfor
  841. " Then, find the element with minimun seq.
  842. let minseq = 99999999
  843. let minnode = {}
  844. if foundx == 0
  845. "assume undo level isn't more than this... of course
  846. for i in range(len(slots))
  847. if type(slots[i]) == TYPE_E
  848. if slots[i].seq < minseq
  849. let minseq = slots[i].seq
  850. let index = i
  851. let minnode = slots[i]
  852. continue
  853. endif
  854. endif
  855. if type(slots[i]) == TYPE_P
  856. for j in slots[i]
  857. if j.seq < minseq
  858. let minseq = j.seq
  859. let index = i
  860. let minnode = j
  861. continue
  862. endif
  863. endfor
  864. endif
  865. endfor
  866. endif
  867. " output.
  868. let onespace = " "
  869. let newline = onespace
  870. let newmeta = {}
  871. let node = slots[index]
  872. if type(node) == TYPE_X
  873. let newmeta = s:new(s:node) "invalid node.
  874. if index+1 != len(slots) " not the last one, append '\'
  875. for i in range(len(slots))
  876. if i < index
  877. let newline = newline.g:undotree_TreeVertShape.' '
  878. endif
  879. if i > index
  880. let newline = newline.' '.g:undotree_TreeReturnShape
  881. endif
  882. endfor
  883. endif
  884. call remove(slots,index)
  885. endif
  886. if type(node) == TYPE_E
  887. let newmeta = node
  888. let seq2index[node.seq]=len(out)
  889. for i in range(len(slots))
  890. if index == i
  891. let newline = newline.g:undotree_TreeNodeShape.' '
  892. else
  893. let newline = newline.g:undotree_TreeVertShape.' '
  894. endif
  895. endfor
  896. let newline = newline.' '.(node.seq).' '.
  897. \'('.s:gettime(node.time).')'
  898. " update the printed slot to its child.
  899. if empty(node.p)
  900. let slots[index] = 'x'
  901. endif
  902. if len(node.p) == 1 "only one child.
  903. let slots[index] = node.p[0]
  904. endif
  905. if len(node.p) > 1 "insert p node
  906. let slots[index] = node.p
  907. endif
  908. let node.p = [] "cut reference.
  909. endif
  910. if type(node) == TYPE_P
  911. let newmeta = s:new(s:node) "invalid node.
  912. for k in range(len(slots))
  913. if k < index
  914. let newline = newline.g:undotree_TreeVertShape." "
  915. endif
  916. if k == index
  917. let newline = newline.g:undotree_TreeVertShape.g:undotree_TreeSplitShape." "
  918. endif
  919. if k > index
  920. let newline = newline.g:undotree_TreeSplitShape." "
  921. endif
  922. endfor
  923. call remove(slots,index)
  924. if len(node) == 2
  925. if node[0].seq > node[1].seq
  926. call insert(slots,node[1],index)
  927. call insert(slots,node[0],index)
  928. else
  929. call insert(slots,node[0],index)
  930. call insert(slots,node[1],index)
  931. endif
  932. endif
  933. " split P to E+P if elements in p > 2
  934. if len(node) > 2
  935. call remove(node,index(node,minnode))
  936. call insert(slots,minnode,index)
  937. call insert(slots,node,index)
  938. endif
  939. endif
  940. unlet node
  941. if newline != onespace
  942. let newline = substitute(newline,'\s*$','','g') "remove trailing space.
  943. call insert(out,newline,0)
  944. call insert(outmeta,newmeta,0)
  945. endif
  946. endwhile
  947. let self.asciitree = out
  948. let self.asciimeta = outmeta
  949. " revert index.
  950. let totallen = len(out)
  951. for i in keys(seq2index)
  952. let seq2index[i] = totallen - 1 - seq2index[i]
  953. endfor
  954. let self.seq2index = seq2index
  955. endfunction
  956. "=================================================
  957. "diff panel
  958. let s:diffpanel = s:new(s:panel)
  959. function! s:diffpanel.Update(seq,targetBufnr,targetid) abort
  960. call s:log('diffpanel.Update(),seq:'.a:seq.' bufname:'.bufname(a:targetBufnr))
  961. if !self.diffexecutable
  962. return
  963. endif
  964. let diffresult = []
  965. let self.changes.add = 0
  966. let self.changes.del = 0
  967. if a:seq == 0
  968. let diffresult = []
  969. else
  970. if has_key(self.cache,a:targetBufnr.'_'.a:seq)
  971. call s:log("diff cache hit.")
  972. let diffresult = self.cache[a:targetBufnr.'_'.a:seq]
  973. else
  974. " Double check the target winnr and bufnr
  975. let targetWinnr = -1
  976. for winnr in range(1, winnr('$')) "winnr starts from 1
  977. if (getwinvar(winnr,'undotree_id') == a:targetid)
  978. \&& winbufnr(winnr) == a:targetBufnr
  979. let targetWinnr = winnr
  980. endif
  981. endfor
  982. if targetWinnr == -1
  983. return
  984. endif
  985. let ei_bak = &eventignore
  986. set eventignore=all
  987. call s:exec_silent(targetWinnr." wincmd w")
  988. " remember and restore cursor and window position.
  989. let savedview = winsaveview()
  990. let new = getbufline(a:targetBufnr,'^','$')
  991. silent undo
  992. let old = getbufline(a:targetBufnr,'^','$')
  993. silent redo
  994. call winrestview(savedview)
  995. " diff files.
  996. let tempfile1 = tempname()
  997. let tempfile2 = tempname()
  998. if writefile(old,tempfile1) == -1
  999. echoerr "Can not write to temp file:".tempfile1
  1000. endif
  1001. if writefile(new,tempfile2) == -1
  1002. echoerr "Can not write to temp file:".tempfile2
  1003. endif
  1004. let diffresult = split(system(g:undotree_DiffCommand.' '.tempfile1.' '.tempfile2),"\n")
  1005. call s:log("diffresult: ".string(diffresult))
  1006. if delete(tempfile1) != 0
  1007. echoerr "Can not delete temp file:".tempfile1
  1008. endif
  1009. if delete(tempfile2) != 0
  1010. echoerr "Can not delete temp file:".tempfile2
  1011. endif
  1012. let &eventignore = ei_bak
  1013. "Update cache
  1014. let self.cache[a:targetBufnr.'_'.a:seq] = diffresult
  1015. endif
  1016. endif
  1017. call self.ParseDiff(diffresult, a:targetBufnr)
  1018. call self.SetFocus()
  1019. setlocal modifiable
  1020. call s:exec('1,$ d _')
  1021. call append(0,diffresult)
  1022. call append(0,'- seq: '.a:seq.' -')
  1023. "remove the last empty line
  1024. if getline("$") == ""
  1025. call s:exec('$d _')
  1026. endif
  1027. call s:exec('norm! gg') "move cursor to line 1.
  1028. setlocal nomodifiable
  1029. call t:undotree.SetFocus()
  1030. endfunction
  1031. function! s:diffpanel.ParseDiff(diffresult, targetBufnr) abort
  1032. " set target focus first.
  1033. call t:undotree.SetTargetFocus()
  1034. " If 'a:diffresult' is empty then there are no new signs to place. However,
  1035. " we need to ensure any old signs are removed. This is especially important
  1036. " if we are at the very first sequence, otherwise signs get left
  1037. if (exists("w:undotree_diffsigns"))
  1038. while w:undotree_diffsigns > 0
  1039. exe 'sign unplace '.s:signId
  1040. let w:undotree_diffsigns -= 1
  1041. endwhile
  1042. endif
  1043. if empty(a:diffresult)
  1044. return
  1045. endif
  1046. " clear previous highlighted syntax
  1047. " matchadd associates with windows.
  1048. if exists("w:undotree_diffmatches")
  1049. for i in w:undotree_diffmatches
  1050. silent! call matchdelete(i)
  1051. endfor
  1052. endif
  1053. let w:undotree_diffmatches = []
  1054. let w:undotree_diffsigns = 0
  1055. let lineNr = 0
  1056. let l:lastLine = line('$')
  1057. for line in a:diffresult
  1058. let matchnum = matchstr(line,'^[0-9,\,]*[acd]\zs\d*\ze')
  1059. if !empty(matchnum)
  1060. let lineNr = str2nr(matchnum)
  1061. let matchwhat = matchstr(line,'^[0-9,\,]*\zs[acd]\ze\d*')
  1062. if matchwhat ==# 'd'
  1063. if g:undotree_HighlightChangedWithSign
  1064. " Normally, for a 'delete' change, the line number we have is always 1 less than the line we
  1065. " need to place the sign at, hence '+ 1'
  1066. " However, if the very end of the buffer has been deleted then this is not possible (because
  1067. " that bit of the buffer no longer exists), so we place a 'special' version of the 'delete'
  1068. " sign on what is the last available line)
  1069. exe 'sign place '.s:signId.' line='.((lineNr < l:lastLine) ? lineNr + 1 : l:lastLine).' name='.((lineNr < l:lastLine) ? 'UndotreeDel' : 'UndotreeDelEnd').' buffer='.a:targetBufnr
  1070. let w:undotree_diffsigns += 1
  1071. endif
  1072. let matchnum = 0
  1073. let matchwhat = ''
  1074. endif
  1075. continue
  1076. endif
  1077. if matchstr(line,'^<.*$') != ''
  1078. let self.changes.del += 1
  1079. endif
  1080. let matchtext = matchstr(line,'^>\zs .*$')
  1081. if empty(matchtext)
  1082. continue
  1083. endif
  1084. let self.changes.add += 1
  1085. if g:undotree_HighlightChangedText
  1086. if matchtext != ' '
  1087. let matchtext = '\%'.lineNr.'l\V'.escape(matchtext[1:],'"\') "remove beginning space.
  1088. call s:log("matchadd(".matchwhat.") -> ".matchtext)
  1089. call add(w:undotree_diffmatches,matchadd((matchwhat ==# 'a' ? g:undotree_HighlightSyntaxAdd : g:undotree_HighlightSyntaxChange),matchtext))
  1090. endif
  1091. endif
  1092. if g:undotree_HighlightChangedWithSign
  1093. exe 'sign place '.s:signId.' line='.lineNr.' name='.(matchwhat ==# 'a' ? 'UndotreeAdd' : 'UndotreeChg').' buffer='.a:targetBufnr
  1094. let w:undotree_diffsigns += 1
  1095. endif
  1096. let lineNr = lineNr+1
  1097. endfor
  1098. endfunction
  1099. function! s:diffpanel.GetStatusLine() abort
  1100. let max = winwidth(0) - 4
  1101. let sum = self.changes.add + self.changes.del
  1102. if sum > max
  1103. let add = self.changes.add * max / sum + 1
  1104. let del = self.changes.del * max / sum + 1
  1105. else
  1106. let add = self.changes.add
  1107. let del = self.changes.del
  1108. endif
  1109. return string(sum).' '.repeat('+',add).repeat('-',del)
  1110. endfunction
  1111. function! s:diffpanel.Init() abort
  1112. let self.bufname = "diffpanel_".s:getUniqueID()
  1113. let self.cache = {}
  1114. let self.changes = {'add':0, 'del':0}
  1115. let self.diffexecutable = executable(g:undotree_DiffCommand)
  1116. if !self.diffexecutable
  1117. " If the command contains parameters, strip out the executable itself
  1118. let cmd = matchstr(g:undotree_DiffCommand.' ', '.\{-}\ze\s.*')
  1119. let self.diffexecutable = executable(cmd)
  1120. if !self.diffexecutable
  1121. echoerr '"'.cmd.'" is not executable.'
  1122. endif
  1123. endif
  1124. endfunction
  1125. function! s:diffpanel.Toggle() abort
  1126. call s:log(self.bufname." Toggle()")
  1127. if self.IsVisible()
  1128. call self.Hide()
  1129. else
  1130. call self.Show()
  1131. endif
  1132. endfunction
  1133. function! s:diffpanel.Show() abort
  1134. call s:log("diffpanel.Show()")
  1135. if self.IsVisible()
  1136. return
  1137. endif
  1138. " Create diffpanel window.
  1139. call t:undotree.SetFocus() "can not exist without undotree
  1140. " remember and restore cursor and window position.
  1141. let savedview = winsaveview()
  1142. let ei_bak= &eventignore
  1143. set eventignore=all
  1144. if exists("g:undotree_CustomDiffpanelCmd")
  1145. let cmd = g:undotree_CustomDiffpanelCmd.' '.self.bufname
  1146. elseif g:undotree_WindowLayout == 1 || g:undotree_WindowLayout == 3
  1147. let cmd = 'belowright '.g:undotree_DiffpanelHeight.'new '.self.bufname
  1148. else
  1149. let cmd = 'botright '.g:undotree_DiffpanelHeight.'new '.self.bufname
  1150. endif
  1151. call s:exec_silent(cmd)
  1152. let b:isUndotreeBuffer = 1
  1153. setlocal winfixwidth
  1154. setlocal winfixheight
  1155. setlocal noswapfile
  1156. setlocal buftype=nowrite
  1157. setlocal bufhidden=delete
  1158. setlocal nowrap
  1159. setlocal nolist
  1160. setlocal nobuflisted
  1161. setlocal nospell
  1162. setlocal nonumber
  1163. setlocal norelativenumber
  1164. setlocal nocursorline
  1165. setlocal nomodifiable
  1166. setlocal statusline=%!t:diffpanel.GetStatusLine()
  1167. let &eventignore = ei_bak
  1168. " syntax need filetype autocommand
  1169. setfiletype diff
  1170. setlocal foldcolumn=0
  1171. setlocal nofoldenable
  1172. call self.BindAu()
  1173. call t:undotree.SetFocus()
  1174. call winrestview(savedview)
  1175. endfunction
  1176. function! s:diffpanel.BindAu() abort
  1177. " Auto exit if it's the last window or undotree closed.
  1178. augroup Undotree_Diff
  1179. au!
  1180. au BufEnter <buffer> call s:exitIfLast()
  1181. au BufEnter <buffer> if !t:undotree.IsVisible()
  1182. \|call t:diffpanel.Hide() |endif
  1183. augroup end
  1184. endfunction
  1185. function! s:diffpanel.CleanUpHighlight() abort
  1186. call s:log("CleanUpHighlight()")
  1187. " save current position
  1188. let curwinnr = winnr()
  1189. let savedview = winsaveview()
  1190. " clear w:undotree_diffmatches in all windows.
  1191. let winnum = winnr('$')
  1192. for i in range(1,winnum)
  1193. call s:exec_silent("norm! ".i."\<c-w>\<c-w>")
  1194. if exists("w:undotree_diffmatches")
  1195. for j in w:undotree_diffmatches
  1196. silent! call matchdelete(j)
  1197. endfor
  1198. let w:undotree_diffmatches = []
  1199. endif
  1200. if (exists("w:undotree_diffsigns"))
  1201. while w:undotree_diffsigns > 0
  1202. exe 'sign unplace '.s:signId
  1203. let w:undotree_diffsigns -= 1
  1204. endwhile
  1205. endif
  1206. endfor
  1207. "restore position
  1208. call s:exec_silent("norm! ".curwinnr."\<c-w>\<c-w>")
  1209. call winrestview(savedview)
  1210. endfunction
  1211. function! s:diffpanel.Hide() abort
  1212. call s:log(self.bufname." Hide()")
  1213. if !self.IsVisible()
  1214. return
  1215. endif
  1216. call self.SetFocus()
  1217. call s:exec("quit")
  1218. call self.CleanUpHighlight()
  1219. endfunction
  1220. "=================================================
  1221. " It will set the target of undotree window to the current editing buffer.
  1222. function! s:undotreeAction(action) abort
  1223. call s:log("undotreeAction()")
  1224. if !exists('t:undotree')
  1225. echoerr "Fatal: t:undotree does not exist!"
  1226. return
  1227. endif
  1228. call t:undotree.Action(a:action)
  1229. endfunction
  1230. function! s:exitIfLast() abort
  1231. let num = 0
  1232. if exists('t:undotree') && t:undotree.IsVisible()
  1233. let num = num + 1
  1234. endif
  1235. if exists('t:diffpanel') && t:diffpanel.IsVisible()
  1236. let num = num + 1
  1237. endif
  1238. if winnr('$') == num
  1239. if exists('t:undotree')
  1240. call t:undotree.Hide()
  1241. endif
  1242. if exists('t:diffpanel')
  1243. call t:diffpanel.Hide()
  1244. endif
  1245. endif
  1246. endfunction
  1247. "=================================================
  1248. " User command functions
  1249. "called outside undotree window
  1250. function! undotree#UndotreeUpdate() abort
  1251. if !exists('t:undotree')
  1252. return
  1253. endif
  1254. if !exists('w:undotree_id')
  1255. let w:undotree_id = 'id_'.s:getUniqueID()
  1256. call s:log("Unique window id assigned: ".w:undotree_id)
  1257. endif
  1258. " assume window layout won't change during updating.
  1259. let thiswinnr = winnr()
  1260. call t:undotree.Update()
  1261. " focus moved
  1262. if winnr() != thiswinnr
  1263. call s:exec("norm! ".thiswinnr."\<c-w>\<c-w>")
  1264. endif
  1265. endfunction
  1266. function! undotree#UndotreeToggle() abort
  1267. try
  1268. call s:log(">>> UndotreeToggle()")
  1269. if !exists('w:undotree_id')
  1270. let w:undotree_id = 'id_'.s:getUniqueID()
  1271. call s:log("Unique window id assigned: ".w:undotree_id)
  1272. endif
  1273. if !exists('t:undotree')
  1274. let t:undotree = s:new(s:undotree)
  1275. endif
  1276. if !exists('t:diffpanel')
  1277. let t:diffpanel = s:new(s:diffpanel)
  1278. endif
  1279. call t:undotree.Toggle()
  1280. call s:log("<<< UndotreeToggle() leave")
  1281. catch /^Vim\%((\a\+)\)\?:E11/
  1282. echohl ErrorMsg
  1283. echom v:exception
  1284. echohl NONE
  1285. endtry
  1286. endfunction
  1287. function! undotree#UndotreeIsVisible() abort
  1288. return (exists('t:undotree') && t:undotree.IsVisible())
  1289. endfunction
  1290. function! undotree#UndotreeHide() abort
  1291. if undotree#UndotreeIsVisible()
  1292. try
  1293. call undotree#UndotreeToggle()
  1294. catch /^Vim\%((\a\+)\)\?:E11/
  1295. echohl ErrorMsg
  1296. echom v:exception
  1297. echohl NONE
  1298. endtry
  1299. endif
  1300. endfunction
  1301. function! undotree#UndotreeShow() abort
  1302. try
  1303. if ! undotree#UndotreeIsVisible()
  1304. call undotree#UndotreeToggle()
  1305. else
  1306. call t:undotree.SetFocus()
  1307. endif
  1308. catch /^Vim\%((\a\+)\)\?:E11/
  1309. echohl ErrorMsg
  1310. echom v:exception
  1311. echohl NONE
  1312. endtry
  1313. endfunction
  1314. function! undotree#UndotreeFocus() abort
  1315. if undotree#UndotreeIsVisible()
  1316. try
  1317. call t:undotree.SetFocus()
  1318. catch /^Vim\%((\a\+)\)\?:E11/
  1319. echohl ErrorMsg
  1320. echom v:exception
  1321. echohl NONE
  1322. endtry
  1323. endif
  1324. endfunction
  1325. function! undotree#UndotreePersistUndo(goSetUndofile) abort
  1326. call s:log("undotree#UndotreePersistUndo(" . a:goSetUndofile . ")")
  1327. if ! &undofile
  1328. if !isdirectory(g:undotree_UndoDir)
  1329. call mkdir(g:undotree_UndoDir, 'p', 0700)
  1330. call s:log(" > [Dir " . g:undotree_UndoDir . "] created.")
  1331. endif
  1332. exe "set undodir=" . fnameescape(g:undotree_UndoDir)
  1333. call s:log(" > [set undodir=" . g:undotree_UndoDir . "] executed.")
  1334. if filereadable(undofile(expand('%'))) || a:goSetUndofile
  1335. setlocal undofile
  1336. call s:log(" > [setlocal undofile] executed")
  1337. endif
  1338. if a:goSetUndofile
  1339. silent! write
  1340. echo "A persistence undo file has been created."
  1341. endif
  1342. else
  1343. call s:log(" > Undofile has been set. Do nothing.")
  1344. endif
  1345. endfunction
  1346. " vim: set et fdm=marker sts=4 sw=4: