startify.vim 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. " vim: et sw=2 sts=2
  2. " Plugin: https://github.com/mhinz/vim-startify
  3. " Description: A fancy start screen for Vim.
  4. " Maintainer: Marco Hinz <http://github.com/mhinz>
  5. if exists('g:autoloaded_startify') || &compatible
  6. finish
  7. endif
  8. let g:autoloaded_startify = 1
  9. " Function: #get_lastline {{{1
  10. function! startify#get_lastline() abort
  11. return b:startify.lastline + 1
  12. endfunction
  13. " Function: #get_separator {{{1
  14. function! startify#get_separator() abort
  15. return !exists('+shellslash') || &shellslash ? '/' : '\'
  16. endfunction
  17. " Function: #get_session_path {{{1
  18. function! startify#get_session_path() abort
  19. if exists('g:startify_session_dir')
  20. let path = g:startify_session_dir
  21. elseif has('nvim')
  22. let path = has('nvim-0.3.1')
  23. \ ? stdpath('data').'/session'
  24. \ : has('win32')
  25. \ ? '~/AppData/Local/nvim-data/session'
  26. \ : '~/.local/share/nvim/session'
  27. else " Vim
  28. let path = has('win32')
  29. \ ? '~/vimfiles/session'
  30. \ : '~/.vim/session'
  31. endif
  32. return resolve(expand(path))
  33. endfunction
  34. " Function: #insane_in_the_membrane {{{1
  35. function! startify#insane_in_the_membrane(on_vimenter) abort
  36. " Handle vim -y, vim -M.
  37. if a:on_vimenter && (&insertmode || !&modifiable)
  38. return
  39. endif
  40. if !&hidden && &modified
  41. call s:warn('Save your changes first.')
  42. return
  43. endif
  44. if !empty(v:servername) && exists('g:startify_skiplist_server')
  45. for servname in g:startify_skiplist_server
  46. if servname == v:servername
  47. return
  48. endif
  49. endfor
  50. endif
  51. if line2byte('$') != -1
  52. noautocmd enew
  53. endif
  54. silent! setlocal
  55. \ bufhidden=wipe
  56. \ colorcolumn=
  57. \ foldcolumn=0
  58. \ matchpairs=
  59. \ nobuflisted
  60. \ nocursorcolumn
  61. \ nocursorline
  62. \ nolist
  63. \ nonumber
  64. \ norelativenumber
  65. \ nospell
  66. \ noswapfile
  67. \ signcolumn=no
  68. if empty(&statusline)
  69. setlocal statusline=\ startify
  70. endif
  71. " Must be global so that it can be read by syntax/startify.vim.
  72. let g:startify_header = exists('g:startify_custom_header')
  73. \ ? s:set_custom_section(g:startify_custom_header)
  74. \ : (exists('*strwidth') ? startify#fortune#cowsay() : [])
  75. if !empty(g:startify_header)
  76. let g:startify_header += [''] " add blank line
  77. endif
  78. call append('$', g:startify_header)
  79. let b:startify = { 'tick': 0, 'entries': {}, 'indices': [] }
  80. if s:show_special
  81. call append('$', [s:padding_left .'[e] <empty buffer>', ''])
  82. endif
  83. call s:register(line('$')-1, 'e', 'special', 'enew', '')
  84. let b:startify.entry_number = 0
  85. if filereadable('Session.vim')
  86. call append('$', [s:padding_left .'[0] '. getcwd() . s:sep .'Session.vim', ''])
  87. call s:register(line('$')-1, '0', 'session',
  88. \ 'call startify#session_delete_buffers() | source', 'Session.vim')
  89. let b:startify.entry_number = 1
  90. let l:show_session = 1
  91. endif
  92. if empty(v:oldfiles)
  93. call s:warn("Can't read viminfo file. Read :help startify-faq-02")
  94. endif
  95. let b:startify.section_header_lines = []
  96. let lists = s:get_lists()
  97. call s:show_lists(lists)
  98. silent $delete _
  99. if s:show_special
  100. call append('$', ['', s:padding_left .'[q] <quit>'])
  101. call s:register(line('$'), 'q', 'special', 'call s:close()', '')
  102. else
  103. " Don't overwrite the last regular entry, thus +1
  104. call s:register(line('$')+1, 'q', 'special', 'call s:close()', '')
  105. endif
  106. " compute first line offset
  107. let b:startify.firstline = 2
  108. let b:startify.firstline += len(g:startify_header)
  109. " no special, no local Session.vim, but a section header
  110. if !s:show_special && !exists('l:show_session') && has_key(lists[0], 'header')
  111. let b:startify.firstline += len(lists[0].header) + 1
  112. endif
  113. let b:startify.lastline = line('$')
  114. let footer = exists('g:startify_custom_footer')
  115. \ ? s:set_custom_section(g:startify_custom_footer)
  116. \ : []
  117. if !empty(footer)
  118. let footer = [''] + footer
  119. endif
  120. call append('$', footer)
  121. setlocal nomodifiable nomodified
  122. call s:set_mappings()
  123. call cursor(b:startify.firstline, 5)
  124. autocmd startify CursorMoved <buffer> call s:set_cursor()
  125. silent! %foldopen!
  126. normal! zb
  127. set filetype=startify
  128. if exists('##DirChanged')
  129. let b:startify.cwd = getcwd()
  130. autocmd startify DirChanged <buffer> if getcwd() !=# get(get(b:, 'startify', {}), 'cwd') | Startify | endif
  131. endif
  132. if exists('#User#Startified')
  133. doautocmd <nomodeline> User Startified
  134. endif
  135. if exists('#User#StartifyReady')
  136. doautocmd <nomodeline> User StartifyReady
  137. endif
  138. endfunction
  139. " Function: #session_load {{{1
  140. function! startify#session_load(source_last_session, ...) abort
  141. if !isdirectory(s:session_dir)
  142. echomsg 'The session directory does not exist: '. s:session_dir
  143. return
  144. elseif empty(startify#session_list_as_string(''))
  145. echomsg 'There are no sessions...'
  146. return
  147. endif
  148. let session_path = s:session_dir . s:sep
  149. if a:0
  150. let session_path .= a:1
  151. elseif a:source_last_session && !has('win32')
  152. let session_path .= '__LAST__'
  153. else
  154. call inputsave()
  155. let session_path .= input(
  156. \ 'Load this session: ',
  157. \ fnamemodify(v:this_session, ':t'),
  158. \ 'custom,startify#session_list_as_string') | redraw
  159. call inputrestore()
  160. endif
  161. if filereadable(session_path)
  162. if get(g:, 'startify_session_persistence') && filewritable(v:this_session)
  163. call startify#session_write(fnameescape(v:this_session))
  164. endif
  165. call startify#session_delete_buffers()
  166. execute 'source '. fnameescape(session_path)
  167. call s:create_last_session_link(session_path)
  168. else
  169. echo 'No such file: '. session_path
  170. endif
  171. endfunction
  172. " Function: #session_save {{{1
  173. function! startify#session_save(bang, ...) abort
  174. if !isdirectory(s:session_dir)
  175. if exists('*mkdir')
  176. echo 'The session directory does not exist: '. s:session_dir .'. Create it? [y/n]'
  177. if (nr2char(getchar()) == 'y')
  178. call mkdir(s:session_dir, 'p')
  179. else
  180. echo
  181. return
  182. endif
  183. else
  184. echo 'The session directory does not exist: '. s:session_dir
  185. return
  186. endif
  187. endif
  188. call inputsave()
  189. let this_session = fnamemodify(v:this_session, ':t')
  190. if this_session ==# '__LAST__'
  191. let this_session = ''
  192. endif
  193. let session_name = exists('a:1')
  194. \ ? a:1
  195. \ : input('Save under this session name: ', this_session, 'custom,startify#session_list_as_string') | redraw
  196. call inputrestore()
  197. if empty(session_name)
  198. echo 'You gave an empty name!'
  199. return
  200. endif
  201. let session_path = s:session_dir . s:sep . session_name
  202. if !filereadable(session_path)
  203. call startify#session_write(fnameescape(session_path))
  204. echo 'Session saved under: '. session_path
  205. return
  206. endif
  207. echo 'Session already exists. Overwrite? [y/n]' | redraw
  208. if a:bang || nr2char(getchar()) == 'y'
  209. call startify#session_write(fnameescape(session_path))
  210. echo 'Session saved under: '. session_path
  211. else
  212. echo 'Did NOT save the session!'
  213. endif
  214. endfunction
  215. " Function: #session_close {{{1
  216. function! startify#session_close() abort
  217. if exists('v:this_session') && filewritable(v:this_session)
  218. call startify#session_write(fnameescape(v:this_session))
  219. let v:this_session = ''
  220. endif
  221. call startify#session_delete_buffers()
  222. Startify
  223. endfunction
  224. " Function: #session_write {{{1
  225. function! startify#session_write(session_path)
  226. " preserve existing variables from savevars
  227. if exists('g:startify_session_savevars')
  228. let savevars = map(filter(copy(g:startify_session_savevars), 'exists(v:val)'), '"let ". v:val ." = ". strtrans(string(eval(v:val)))')
  229. endif
  230. " if this function is called while being in the Startify buffer
  231. " (by loading another session or running :SSave/:SLoad directly)
  232. " switch back to the previous buffer before saving the session
  233. if &filetype == 'startify'
  234. let callingbuffer = bufnr('#')
  235. if callingbuffer > 0
  236. execute 'buffer' callingbuffer
  237. endif
  238. endif
  239. " prevent saving already deleted buffers that were in the arglist
  240. for arg in argv()
  241. if !buflisted(arg)
  242. execute 'silent! argdelete' fnameescape(arg)
  243. endif
  244. endfor
  245. " clean up session before saving it
  246. for cmd in get(g:, 'startify_session_before_save', [])
  247. execute cmd
  248. endfor
  249. let ssop = &sessionoptions
  250. set sessionoptions-=options
  251. try
  252. execute 'mksession!' a:session_path
  253. catch
  254. echohl ErrorMsg
  255. echomsg v:exception
  256. echohl NONE
  257. return
  258. finally
  259. let &sessionoptions = ssop
  260. endtry
  261. if exists('g:startify_session_remove_lines')
  262. \ || exists('g:startify_session_savevars')
  263. \ || exists('g:startify_session_savecmds')
  264. silent execute 'split' a:session_path
  265. " remove lines from the session file
  266. if exists('g:startify_session_remove_lines')
  267. for pattern in g:startify_session_remove_lines
  268. execute 'silent global/'. pattern .'/delete _'
  269. endfor
  270. endif
  271. " put variables from savevars into session file
  272. if exists('savevars') && !empty(savevars)
  273. call append(line('$')-3, savevars)
  274. endif
  275. " put commands from savecmds into session file
  276. if exists('g:startify_session_savecmds')
  277. call append(line('$')-3, g:startify_session_savecmds)
  278. endif
  279. setlocal bufhidden=delete
  280. silent update
  281. silent hide
  282. endif
  283. call s:create_last_session_link(a:session_path)
  284. endfunction
  285. " Function: #session_delete {{{1
  286. function! startify#session_delete(bang, ...) abort
  287. if !isdirectory(s:session_dir)
  288. echo 'The session directory does not exist: '. s:session_dir
  289. return
  290. elseif empty(startify#session_list_as_string(''))
  291. echo 'There are no sessions...'
  292. return
  293. endif
  294. call inputsave()
  295. let session_path = s:session_dir . s:sep . (exists('a:1')
  296. \ ? a:1
  297. \ : input('Delete this session: ', fnamemodify(v:this_session, ':t'), 'custom,startify#session_list_as_string'))
  298. call inputrestore()
  299. if !filereadable(session_path)
  300. echomsg 'No such session: '. session_path
  301. return
  302. endif
  303. echo 'Really delete '. session_path .'? [y/n]'
  304. if a:bang || nr2char(getchar()) == 'y'
  305. if delete(session_path) == 0
  306. echo 'Deleted session '. session_path .'!'
  307. else
  308. echo 'Deletion failed!'
  309. endif
  310. else
  311. echo 'Deletion aborted!'
  312. endif
  313. endfunction
  314. " Function: #session_delete_buffers {{{1
  315. function! startify#session_delete_buffers()
  316. if get(g:, 'startify_session_delete_buffers', 1)
  317. silent! %bdelete!
  318. endif
  319. endfunction
  320. " Function: #session_list {{{1
  321. function! startify#session_list(lead, ...) abort
  322. return filter(map(split(globpath(s:session_dir, '*'.a:lead.'*'), '\n'), 'fnamemodify(v:val, ":t")'), 'v:val !=# "__LAST__"')
  323. endfunction
  324. " Function: #session_list_as_string {{{1
  325. function! startify#session_list_as_string(lead, ...) abort
  326. return join(filter(map(split(globpath(s:session_dir, '*'.a:lead.'*'), '\n'), 'fnamemodify(v:val, ":t")'), 'v:val !=# "__LAST__"'), "\n")
  327. endfunction
  328. " Function: #debug {{{1
  329. function! startify#debug()
  330. if exists('b:startify.entries')
  331. for k in sort(keys(b:startify.entries))
  332. echomsg '['. k .'] = '. string(b:startify.entries[k])
  333. endfor
  334. else
  335. call s:warn('This is no Startify buffer!')
  336. endif
  337. endfunction
  338. " Function: #open_buffers {{{1
  339. function! startify#open_buffers(...) abort
  340. if exists('a:1') " used in mappings
  341. let entry = b:startify.entries[a:1]
  342. if !empty(s:batchmode) && entry.type == 'file'
  343. call s:set_mark(s:batchmode, a:1)
  344. else
  345. call s:open_buffer(entry)
  346. endif
  347. return
  348. endif
  349. let marked = filter(copy(b:startify.entries), 'v:val.marked')
  350. if empty(marked) " open current entry
  351. call s:open_buffer(b:startify.entries[line('.')])
  352. return
  353. endif
  354. enew
  355. setlocal nobuflisted
  356. " Open all marked entries.
  357. for entry in sort(values(marked), 's:sort_by_tick')
  358. call s:open_buffer(entry)
  359. endfor
  360. wincmd =
  361. if exists('#User#StartifyAllBuffersOpened')
  362. doautocmd <nomodeline> User StartifyAllBuffersOpened
  363. endif
  364. endfunction
  365. " Function: s:get_lists {{{1
  366. function! s:get_lists() abort
  367. if exists('g:startify_lists')
  368. return g:startify_lists
  369. elseif exists('g:startify_list_order')
  370. " Convert old g:startify_list_order format to newer g:startify_lists format.
  371. let lists = []
  372. for item in g:startify_list_order
  373. if type(item) == type([])
  374. let header = item
  375. else
  376. if exists('header')
  377. let lists += [{ 'type': item, 'header': header }]
  378. unlet header
  379. else
  380. let lists += [{ 'type': item }]
  381. endif
  382. endif
  383. unlet item
  384. endfor
  385. return lists
  386. else
  387. return [
  388. \ { 'header': [s:padding_left .'MRU'], 'type': 'files' },
  389. \ { 'header': [s:padding_left .'MRU '. getcwd()], 'type': 'dir' },
  390. \ { 'header': [s:padding_left .'Sessions'], 'type': 'sessions' },
  391. \ { 'header': [s:padding_left .'Bookmarks'], 'type': 'bookmarks' },
  392. \ { 'header': [s:padding_left .'Commands'], 'type': 'commands' },
  393. \ ]
  394. endif
  395. endfunction
  396. " Function: s:show_lists {{{1
  397. function! s:show_lists(lists) abort
  398. for list in a:lists
  399. if !has_key(list, 'type')
  400. continue
  401. endif
  402. let b:startify.indices = copy(get(list, 'indices', []))
  403. if type(list.type) == type('')
  404. if has_key(list, 'header')
  405. let s:last_message = list.header
  406. endif
  407. call s:show_{list.type}()
  408. elseif type(list.type) == type(function('tr'))
  409. try
  410. let entries = list.type()
  411. catch
  412. call s:warn(v:exception)
  413. continue
  414. endtry
  415. if empty(entries)
  416. unlet! s:last_message
  417. continue
  418. endif
  419. if has_key(list, 'header')
  420. let s:last_message = list.header
  421. call s:print_section_header()
  422. endif
  423. for entry in entries
  424. let cmd = get(entry, 'cmd', 'edit')
  425. let path = get(entry, 'path', '')
  426. let type = get(entry, 'type', empty(path) ? 'special' : 'file')
  427. let index = s:get_index_as_string()
  428. call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . entry.line)
  429. call s:register(line('$'), index, type, cmd, path)
  430. endfor
  431. call append('$', '')
  432. else
  433. call s:warn('Wrong format for g:startify_lists: '. string(list))
  434. endif
  435. endfor
  436. endfunction
  437. " Function: s:open_buffer {{{1
  438. function! s:open_buffer(entry)
  439. if a:entry.type == 'special'
  440. execute a:entry.cmd
  441. elseif a:entry.type == 'session'
  442. execute a:entry.cmd a:entry.path
  443. elseif a:entry.type == 'file'
  444. if line2byte('$') == -1
  445. execute 'edit' a:entry.path
  446. else
  447. if a:entry.cmd == 'tabnew'
  448. wincmd =
  449. endif
  450. execute a:entry.cmd a:entry.path
  451. endif
  452. call s:check_user_options(a:entry.path)
  453. endif
  454. if exists('#User#StartifyBufferOpened')
  455. doautocmd <nomodeline> User StartifyBufferOpened
  456. endif
  457. endfunction
  458. " Function: s:set_custom_section {{{1
  459. function! s:set_custom_section(section) abort
  460. if type(a:section) == type([])
  461. return copy(a:section)
  462. elseif type(a:section) == type('')
  463. return empty(a:section) ? [] : eval(a:section)
  464. endif
  465. return []
  466. endfunction
  467. " Function: s:display_by_path {{{1
  468. function! s:display_by_path(path_prefix, path_format, use_env) abort
  469. let oldfiles = call(get(g:, 'startify_enable_unsafe') ? 's:filter_oldfiles_unsafe' : 's:filter_oldfiles',
  470. \ [a:path_prefix, a:path_format, a:use_env])
  471. let entry_format = "s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) ."
  472. if exists('*StartifyEntryFormat')
  473. let entry_format .= StartifyEntryFormat()
  474. else
  475. let entry_format .= 'entry_path'
  476. endif
  477. if !empty(oldfiles)
  478. if exists('s:last_message')
  479. call s:print_section_header()
  480. endif
  481. for [absolute_path, entry_path] in oldfiles
  482. let index = s:get_index_as_string()
  483. call append('$', eval(entry_format))
  484. if has('win32')
  485. let absolute_path = substitute(absolute_path, '\[', '\[[]', 'g')
  486. endif
  487. call s:register(line('$'), index, 'file', 'edit', absolute_path)
  488. endfor
  489. call append('$', '')
  490. endif
  491. endfunction
  492. " Function: s:filter_oldfiles {{{1
  493. function! s:filter_oldfiles(path_prefix, path_format, use_env) abort
  494. let path_prefix = '\V'. escape(a:path_prefix, '\')
  495. let counter = s:numfiles
  496. let entries = {}
  497. let oldfiles = []
  498. for fname in v:oldfiles
  499. if counter <= 0
  500. break
  501. endif
  502. if s:is_in_skiplist(fname)
  503. " https://github.com/mhinz/vim-startify/issues/353
  504. continue
  505. endif
  506. let absolute_path = fnamemodify(resolve(fname), ":p")
  507. " filter duplicates, bookmarks and entries from the skiplist
  508. if has_key(entries, absolute_path)
  509. \ || !filereadable(absolute_path)
  510. \ || s:is_in_skiplist(absolute_path)
  511. \ || match(absolute_path, path_prefix)
  512. continue
  513. endif
  514. let entry_path = ''
  515. if s:tf
  516. let entry_path = s:transform(absolute_path)
  517. endif
  518. if empty(entry_path)
  519. let entry_path = fnamemodify(absolute_path, a:path_format)
  520. endif
  521. let entries[absolute_path] = 1
  522. let counter -= 1
  523. let oldfiles += [[fnameescape(absolute_path), entry_path]]
  524. endfor
  525. if a:use_env
  526. call s:init_env()
  527. for i in range(len(oldfiles))
  528. for [k,v] in s:env
  529. let p = oldfiles[i][0]
  530. if !stridx(tolower(p), tolower(v))
  531. let oldfiles[i][1] = printf('$%s%s', k, p[len(v):])
  532. break
  533. endif
  534. endfor
  535. endfor
  536. endif
  537. return oldfiles
  538. endfunction
  539. " Function: s:filter_oldfiles_unsafe {{{1
  540. function! s:filter_oldfiles_unsafe(path_prefix, path_format, use_env) abort
  541. let path_prefix = '\V'. escape(a:path_prefix, '\')
  542. let counter = s:numfiles
  543. let entries = {}
  544. let oldfiles = []
  545. let is_dir = escape(s:sep, '\') . '$'
  546. for fname in v:oldfiles
  547. if counter <= 0
  548. break
  549. endif
  550. if s:is_in_skiplist(fname)
  551. " https://github.com/mhinz/vim-startify/issues/353
  552. continue
  553. endif
  554. let absolute_path = glob(fnamemodify(fname, ":p"))
  555. if empty(absolute_path)
  556. \ || has_key(entries, absolute_path)
  557. \ || (absolute_path =~ is_dir)
  558. \ || match(absolute_path, path_prefix)
  559. \ || s:is_in_skiplist(absolute_path)
  560. continue
  561. endif
  562. let entry_path = fnamemodify(absolute_path, a:path_format)
  563. let entries[absolute_path] = 1
  564. let counter -= 1
  565. let oldfiles += [[fnameescape(absolute_path), entry_path]]
  566. endfor
  567. return oldfiles
  568. endfunction
  569. " Function: s:show_dir {{{1
  570. function! s:show_dir() abort
  571. return s:display_by_path(getcwd() . s:sep, ':.', 0)
  572. endfunction
  573. " Function: s:show_files {{{1
  574. function! s:show_files() abort
  575. return s:display_by_path('', s:relative_path, get(g:, 'startify_use_env'))
  576. endfunction
  577. " Function: s:show_sessions {{{1
  578. function! s:show_sessions() abort
  579. let limit = get(g:, 'startify_session_number', 999) - 1
  580. if limit <= -1
  581. return
  582. endif
  583. let sfiles = split(globpath(s:session_dir, '*'), '\n')
  584. let sfiles = filter(sfiles, 'v:val !~# "__LAST__$"')
  585. let sfiles = filter(sfiles,
  586. \ '!(v:val =~# "x\.vim$" && index(sfiles, v:val[:-6].".vim") >= 0)')
  587. if empty(sfiles)
  588. if exists('s:last_message')
  589. unlet s:last_message
  590. endif
  591. return
  592. endif
  593. if exists('s:last_message')
  594. call s:print_section_header()
  595. endif
  596. if get(g:, 'startify_session_sort')
  597. function! s:sort_by_mtime(foo, bar)
  598. let foo = getftime(a:foo)
  599. let bar = getftime(a:bar)
  600. return foo == bar ? 0 : (foo < bar ? 1 : -1)
  601. endfunction
  602. call sort(sfiles, 's:sort_by_mtime')
  603. endif
  604. for i in range(len(sfiles))
  605. let index = s:get_index_as_string()
  606. let fname = fnamemodify(sfiles[i], ':t')
  607. let dname = sfiles[i] ==# v:this_session ? fname.' (*)' : fname
  608. call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . dname)
  609. if has('win32')
  610. let fname = substitute(fname, '\[', '\[[]', 'g')
  611. endif
  612. call s:register(line('$'), index, 'session', 'SLoad', fname)
  613. if i == limit
  614. break
  615. endif
  616. endfor
  617. call append('$', '')
  618. endfunction
  619. " Function: s:show_bookmarks {{{1
  620. function! s:show_bookmarks() abort
  621. if !exists('g:startify_bookmarks') || empty(g:startify_bookmarks)
  622. return
  623. endif
  624. if exists('s:last_message')
  625. call s:print_section_header()
  626. endif
  627. for bookmark in g:startify_bookmarks
  628. if type(bookmark) == type({})
  629. let [index, path] = items(bookmark)[0]
  630. else " string
  631. let [index, path] = [s:get_index_as_string(), bookmark]
  632. endif
  633. let entry_path = ''
  634. if s:tf
  635. let entry_path = s:transform(fnamemodify(resolve(expand(path)), ':p'))
  636. endif
  637. if empty(entry_path)
  638. let entry_path = path
  639. endif
  640. call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . entry_path)
  641. if has('win32')
  642. let path = substitute(path, '\[', '\[[]', 'g')
  643. endif
  644. call s:register(line('$'), index, 'file', 'edit', fnameescape(expand(path)))
  645. unlet bookmark " avoid type mismatch for heterogeneous lists
  646. endfor
  647. call append('$', '')
  648. endfunction
  649. " Function: s:show_commands {{{1
  650. function! s:show_commands() abort
  651. if !exists('g:startify_commands') || empty(g:startify_commands)
  652. return
  653. endif
  654. if exists('s:last_message')
  655. call s:print_section_header()
  656. endif
  657. for entry in g:startify_commands
  658. if type(entry) == type({}) " with custom index
  659. let [index, command] = items(entry)[0]
  660. else
  661. let command = entry
  662. let index = s:get_index_as_string()
  663. endif
  664. " If no list is given, the description is the command itself.
  665. let [desc, cmd] = type(command) == type([]) ? command : [command, command]
  666. call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . desc)
  667. call s:register(line('$'), index, 'special', cmd, '')
  668. unlet entry command
  669. endfor
  670. call append('$', '')
  671. endfunction
  672. " Function: s:is_in_skiplist {{{1
  673. function! s:is_in_skiplist(arg) abort
  674. for regexp in s:skiplist
  675. try
  676. if a:arg =~# regexp
  677. return 1
  678. endif
  679. catch
  680. call s:warn('Pattern '. string(regexp) .' threw an exception. Read :help g:startify_skiplist')
  681. endtry
  682. endfor
  683. endfunction
  684. " Function: s:set_cursor {{{1
  685. function! s:set_cursor() abort
  686. let b:startify.oldline = exists('b:startify.newline') ? b:startify.newline : 2 + len(s:padding_left)
  687. let b:startify.newline = line('.')
  688. " going up (-1) or down (1)
  689. if b:startify.oldline == b:startify.newline
  690. \ && col('.') != s:fixed_column
  691. \ && !b:startify.leftmouse
  692. let movement = 2 * (col('.') > s:fixed_column) - 1
  693. let b:startify.newline += movement
  694. else
  695. let movement = 2 * (b:startify.newline > b:startify.oldline) - 1
  696. let b:startify.leftmouse = 0
  697. endif
  698. " skip section headers lines until an entry is found
  699. while index(b:startify.section_header_lines, b:startify.newline) != -1
  700. let b:startify.newline += movement
  701. endwhile
  702. " skip blank lines between lists
  703. if empty(getline(b:startify.newline))
  704. let b:startify.newline += movement
  705. endif
  706. " don't go beyond first or last entry
  707. let b:startify.newline = max([b:startify.firstline, min([b:startify.lastline, b:startify.newline])])
  708. call cursor(b:startify.newline, s:fixed_column)
  709. endfunction
  710. " Function: s:set_mappings {{{1
  711. function! s:set_mappings() abort
  712. nnoremap <buffer><nowait><silent> i :enew <bar> startinsert<cr>
  713. nnoremap <buffer><nowait><silent> <insert> :enew <bar> startinsert<cr>
  714. nnoremap <buffer><nowait><silent> b :call startify#set_mark('B')<cr>
  715. nnoremap <buffer><nowait><silent> s :call startify#set_mark('S')<cr>
  716. nnoremap <buffer><nowait><silent> t :call startify#set_mark('T')<cr>
  717. nnoremap <buffer><nowait><silent> v :call startify#set_mark('V')<cr>
  718. nnoremap <buffer><nowait><silent> B :call startify#set_batchmode('B')<cr>
  719. nnoremap <buffer><nowait><silent> S :call startify#set_batchmode('S')<cr>
  720. nnoremap <buffer><nowait><silent> T :call startify#set_batchmode('T')<cr>
  721. nnoremap <buffer><nowait><silent> V :call startify#set_batchmode('V')<cr>
  722. nnoremap <buffer><nowait><silent> <cr> :call startify#open_buffers()<cr>
  723. nnoremap <buffer><nowait><silent> <LeftMouse> :call <sid>leftmouse()<cr>
  724. nnoremap <buffer><nowait><silent> <2-LeftMouse> :call startify#open_buffers()<cr>
  725. nnoremap <buffer><nowait><silent> <MiddleMouse> :enew <bar> execute 'normal! "'.(v:register=='"'?'*':v:register).'gp'<cr>
  726. " Without these mappings n/N wouldn't work properly, since autocmds always
  727. " force the cursor back on the index.
  728. nnoremap <buffer><expr> n ' j'[v:searchforward].'n'
  729. nnoremap <buffer><expr> N 'j '[v:searchforward].'N'
  730. function! s:leftmouse()
  731. " feedkeys() triggers CursorMoved which calls s:set_cursor() which checks
  732. " .leftmouse.
  733. let b:startify.leftmouse = 1
  734. call feedkeys("\<LeftMouse>", 'nt')
  735. endfunction
  736. function! s:compare_by_index(foo, bar)
  737. return a:foo.index - a:bar.index
  738. endfunction
  739. for entry in sort(values(b:startify.entries), 's:compare_by_index')
  740. execute 'nnoremap <buffer><silent><nowait>' entry.index
  741. \ ':call startify#open_buffers('. string(entry.line) .')<cr>'
  742. endfor
  743. endfunction
  744. " Function: #set_batchmode {{{1
  745. function! startify#set_batchmode(batchmode) abort
  746. let s:batchmode = (a:batchmode == s:batchmode) ? '' : a:batchmode
  747. echo empty(s:batchmode) ? 'Batchmode off' : 'Batchmode: '. s:batchmode
  748. endfunction
  749. " Function: #set_mark {{{1
  750. function! startify#set_mark(type, ...) abort
  751. if a:0
  752. let entryline = a:1
  753. else
  754. call startify#set_batchmode('')
  755. let entryline = line('.')
  756. endif
  757. let entry = b:startify.entries[entryline]
  758. if entry.type != 'file'
  759. return
  760. endif
  761. let default_cmds = {
  762. \ 'B': 'edit',
  763. \ 'S': 'split',
  764. \ 'V': 'vsplit',
  765. \ 'T': 'tabnew',
  766. \ }
  767. let origline = line('.')
  768. execute entryline
  769. let index = expand('<cword>')
  770. setlocal modifiable
  771. if entry.marked && index[0] == a:type
  772. let entry.cmd = 'edit'
  773. let entry.marked = 0
  774. execute 'normal! ci]'. entry.index
  775. else
  776. let entry.cmd = default_cmds[a:type]
  777. let entry.marked = 1
  778. let entry.tick = b:startify.tick
  779. let b:startify.tick += 1
  780. execute 'normal! ci]'. repeat(a:type, len(index))
  781. endif
  782. setlocal nomodifiable nomodified
  783. " Reset cursor to fixed column, which is important for s:set_cursor().
  784. call cursor(origline, s:fixed_column)
  785. endfunction
  786. " Function: s:sort_by_tick {{{1
  787. function! s:sort_by_tick(one, two)
  788. return a:one.tick - a:two.tick
  789. endfunction
  790. " Function: s:check_user_options {{{1
  791. function! s:check_user_options(path) abort
  792. let session = a:path . s:sep .'Session.vim'
  793. if get(g:, 'startify_session_autoload') && filereadable(glob(session))
  794. execute 'silent bwipeout' a:path
  795. call startify#session_delete_buffers()
  796. execute 'source' session
  797. return
  798. endif
  799. if get(g:, 'startify_change_to_vcs_root') && s:cd_to_vcs_root(a:path)
  800. return
  801. endif
  802. if get(g:, 'startify_change_to_dir', 1)
  803. if isdirectory(a:path)
  804. execute 'lcd' a:path
  805. else
  806. let dir = fnamemodify(a:path, ':h')
  807. if isdirectory(dir)
  808. execute 'lcd' dir
  809. else
  810. " Do nothing. E.g. a:path == `scp://foo/bar`
  811. endif
  812. endif
  813. endif
  814. endfunction
  815. " Function: s:cd_to_vcs_root {{{1
  816. function! s:cd_to_vcs_root(path) abort
  817. let dir = fnamemodify(a:path, ':p:h')
  818. for vcs in [ '.git', '.hg', '.bzr', '.svn' ]
  819. let root = finddir(vcs, dir .';')
  820. if !empty(root)
  821. execute 'lcd' fnameescape(fnamemodify(root, ':h'))
  822. return 1
  823. endif
  824. endfor
  825. return 0
  826. endfunction
  827. " Function: s:close {{{1
  828. function! s:close() abort
  829. if len(filter(range(0, bufnr('$')), 'buflisted(v:val)')) - &buflisted
  830. if bufloaded(bufnr('#')) && bufnr('#') != bufnr('%')
  831. buffer #
  832. else
  833. bnext
  834. endif
  835. else
  836. quit
  837. endif
  838. endfunction
  839. " Function: s:get_index_as_string {{{1
  840. function! s:get_index_as_string() abort
  841. if !empty(b:startify.indices)
  842. return remove(b:startify.indices, 0)
  843. elseif exists('g:startify_custom_indices')
  844. let listlen = len(g:startify_custom_indices)
  845. if b:startify.entry_number < listlen
  846. let idx = g:startify_custom_indices[b:startify.entry_number]
  847. else
  848. let idx = string(b:startify.entry_number - listlen)
  849. endif
  850. else
  851. let idx = string(b:startify.entry_number)
  852. endif
  853. let b:startify.entry_number += 1
  854. return idx
  855. endfunction
  856. " Function: s:print_section_header {{{1
  857. function! s:print_section_header() abort
  858. $
  859. let curline = line('.')
  860. for lnum in range(curline, curline + len(s:last_message) + 1)
  861. call add(b:startify.section_header_lines, lnum)
  862. endfor
  863. call append('$', s:last_message + [''])
  864. unlet s:last_message
  865. endfunction
  866. " Function: s:register {{{1
  867. function! s:register(line, index, type, cmd, path)
  868. let b:startify.entries[a:line] = {
  869. \ 'index': a:index,
  870. \ 'type': a:type,
  871. \ 'line': a:line,
  872. \ 'cmd': a:cmd,
  873. \ 'path': a:path,
  874. \ 'marked': 0,
  875. \ }
  876. endfunction
  877. " Function: s:create_last_session_link {{{1
  878. function! s:create_last_session_link(session_path)
  879. if !has('win32') && a:session_path !~# '__LAST__$'
  880. let cmd = printf('ln -sf %s %s',
  881. \ shellescape(fnamemodify(a:session_path, ':t')),
  882. \ shellescape(s:session_dir .'/__LAST__'))
  883. call system(cmd)
  884. if v:shell_error
  885. call s:warn("Can't create 'last used session' symlink.")
  886. endif
  887. endif
  888. endfunction
  889. " Function: s:init_env {{{1
  890. function! s:init_env()
  891. let s:env = []
  892. let ignore = {
  893. \ 'HOME': 1,
  894. \ 'OLDPWD': 1,
  895. \ 'PWD': 1,
  896. \ }
  897. function! s:get_env()
  898. redir => s
  899. silent! execute "norm!:ec$\<c-a>'\<c-b>\<right>\<right>\<del>'\<cr>"
  900. redir END
  901. redraw
  902. return split(s)
  903. endfunction
  904. function! s:compare_by_key_len(foo, bar)
  905. return len(a:foo[0]) - len(a:bar[0])
  906. endfunction
  907. function! s:compare_by_val_len(foo, bar)
  908. return len(a:bar[1]) - len(a:foo[1])
  909. endfunction
  910. for k in s:get_env()
  911. silent! execute "let v = eval('$'.k)"
  912. if has('win32') ? (v[1] != ':') : (v[0] != '/')
  913. \ || has_key(ignore, k)
  914. \ || len(k) > len(v)
  915. continue
  916. endif
  917. call insert(s:env, [k,v], 0)
  918. endfor
  919. let s:env = sort(s:env, 's:compare_by_key_len')
  920. let s:env = sort(s:env, 's:compare_by_val_len')
  921. endfunction
  922. " Function: s:transform {{{1
  923. function s:transform(absolute_path)
  924. for [k,V] in g:startify_transformations
  925. if a:absolute_path =~ k
  926. return type(V) == type('') ? V : V(a:absolute_path)
  927. endif
  928. unlet V
  929. endfor
  930. return ''
  931. endfunction
  932. " Function: s:warn {{{1
  933. function! s:warn(msg) abort
  934. echohl WarningMsg
  935. echomsg 'startify: '. a:msg
  936. echohl NONE
  937. endfunction
  938. " Init: values {{{1
  939. let s:sep = startify#get_separator()
  940. let s:numfiles = get(g:, 'startify_files_number', 10)
  941. let s:show_special = get(g:, 'startify_enable_special', 1)
  942. let s:relative_path = get(g:, 'startify_relative_path') ? ':~:.' : ':p:~'
  943. let s:tf = exists('g:startify_transformations')
  944. let s:session_dir = startify#get_session_path()
  945. let s:skiplist = get(g:, 'startify_skiplist', [
  946. \ 'runtime/doc/.*\.txt',
  947. \ 'bundle/.*/doc/.*\.txt',
  948. \ 'plugged/.*/doc/.*\.txt',
  949. \ '/.git/',
  950. \ 'fugitiveblame$',
  951. \ escape(fnamemodify(resolve($VIMRUNTIME), ':p'), '\') .'doc/.*\.txt',
  952. \ ])
  953. let s:padding_left = repeat(' ', get(g:, 'startify_padding_left', 3))
  954. let s:fixed_column = len(s:padding_left) + 2
  955. let s:batchmode = ''