
This way you'll see as much as possible from a potential header. The reverse (making the current line the top line in the window) could be done via.. autocmd User Startified normal! zt ..or centerered via.. autocmd User Startified normal! zz Fixes https://github.com/mhinz/vim-startify/issues/391
1109 lines
31 KiB
VimL
1109 lines
31 KiB
VimL
" vim: et sw=2 sts=2
|
|
|
|
" Plugin: https://github.com/mhinz/vim-startify
|
|
" Description: A fancy start screen for Vim.
|
|
" Maintainer: Marco Hinz <http://github.com/mhinz>
|
|
|
|
if exists('g:autoloaded_startify') || &compatible
|
|
finish
|
|
endif
|
|
let g:autoloaded_startify = 1
|
|
|
|
" Function: #get_lastline {{{1
|
|
function! startify#get_lastline() abort
|
|
return b:startify.lastline + 1
|
|
endfunction
|
|
|
|
" Function: #get_separator {{{1
|
|
function! startify#get_separator() abort
|
|
return !exists('+shellslash') || &shellslash ? '/' : '\'
|
|
endfunction
|
|
|
|
" Function: #get_session_path {{{1
|
|
function! startify#get_session_path() abort
|
|
if exists('g:startify_session_dir')
|
|
let path = g:startify_session_dir
|
|
elseif has('nvim')
|
|
let path = has('nvim-0.3.1')
|
|
\ ? stdpath('data').'/session'
|
|
\ : has('win32')
|
|
\ ? '~/AppData/Local/nvim-data/session'
|
|
\ : '~/.local/share/nvim/session'
|
|
else " Vim
|
|
let path = has('win32')
|
|
\ ? '~/vimfiles/session'
|
|
\ : '~/.vim/session'
|
|
endif
|
|
|
|
return resolve(expand(path))
|
|
endfunction
|
|
|
|
" Function: #insane_in_the_membrane {{{1
|
|
function! startify#insane_in_the_membrane(on_vimenter) abort
|
|
" Handle vim -y, vim -M.
|
|
if a:on_vimenter && (&insertmode || !&modifiable)
|
|
return
|
|
endif
|
|
|
|
if !&hidden && &modified
|
|
call s:warn('Save your changes first.')
|
|
return
|
|
endif
|
|
|
|
if !empty(v:servername) && exists('g:startify_skiplist_server')
|
|
for servname in g:startify_skiplist_server
|
|
if servname == v:servername
|
|
return
|
|
endif
|
|
endfor
|
|
endif
|
|
|
|
if line2byte('$') != -1
|
|
noautocmd enew
|
|
endif
|
|
|
|
silent! setlocal
|
|
\ bufhidden=wipe
|
|
\ colorcolumn=
|
|
\ foldcolumn=0
|
|
\ matchpairs=
|
|
\ nobuflisted
|
|
\ nocursorcolumn
|
|
\ nocursorline
|
|
\ nolist
|
|
\ nonumber
|
|
\ norelativenumber
|
|
\ nospell
|
|
\ noswapfile
|
|
\ signcolumn=no
|
|
if empty(&statusline)
|
|
setlocal statusline=\ startify
|
|
endif
|
|
|
|
" Must be global so that it can be read by syntax/startify.vim.
|
|
let g:startify_header = exists('g:startify_custom_header')
|
|
\ ? s:set_custom_section(g:startify_custom_header)
|
|
\ : (exists('*strwidth') ? startify#fortune#cowsay() : [])
|
|
if !empty(g:startify_header)
|
|
let g:startify_header += [''] " add blank line
|
|
endif
|
|
call append('$', g:startify_header)
|
|
|
|
let b:startify = { 'tick': 0, 'entries': {}, 'indices': [] }
|
|
|
|
if s:show_special
|
|
call append('$', [s:padding_left .'[e] <empty buffer>', ''])
|
|
endif
|
|
call s:register(line('$')-1, 'e', 'special', 'enew', '')
|
|
|
|
let b:startify.entry_number = 0
|
|
if filereadable('Session.vim')
|
|
call append('$', [s:padding_left .'[0] '. getcwd() . s:sep .'Session.vim', ''])
|
|
call s:register(line('$')-1, '0', 'session',
|
|
\ 'call startify#session_delete_buffers() | source', 'Session.vim')
|
|
let b:startify.entry_number = 1
|
|
let l:show_session = 1
|
|
endif
|
|
|
|
if empty(v:oldfiles)
|
|
call s:warn("Can't read viminfo file. Read :help startify-faq-02")
|
|
endif
|
|
|
|
let b:startify.section_header_lines = []
|
|
|
|
let lists = s:get_lists()
|
|
call s:show_lists(lists)
|
|
|
|
silent $delete _
|
|
|
|
if s:show_special
|
|
call append('$', ['', s:padding_left .'[q] <quit>'])
|
|
call s:register(line('$'), 'q', 'special', 'call s:close()', '')
|
|
else
|
|
" Don't overwrite the last regular entry, thus +1
|
|
call s:register(line('$')+1, 'q', 'special', 'call s:close()', '')
|
|
endif
|
|
|
|
" compute first line offset
|
|
let b:startify.firstline = 2
|
|
let b:startify.firstline += len(g:startify_header)
|
|
" no special, no local Session.vim, but a section header
|
|
if !s:show_special && !exists('l:show_session') && has_key(lists[0], 'header')
|
|
let b:startify.firstline += len(lists[0].header) + 1
|
|
endif
|
|
|
|
let b:startify.lastline = line('$')
|
|
|
|
let footer = exists('g:startify_custom_footer')
|
|
\ ? s:set_custom_section(g:startify_custom_footer)
|
|
\ : []
|
|
if !empty(footer)
|
|
let footer = [''] + footer
|
|
endif
|
|
call append('$', footer)
|
|
|
|
setlocal nomodifiable nomodified
|
|
|
|
call s:set_mappings()
|
|
call cursor(b:startify.firstline, 5)
|
|
autocmd startify CursorMoved <buffer> call s:set_cursor()
|
|
|
|
silent! %foldopen!
|
|
normal! zb
|
|
set filetype=startify
|
|
|
|
if exists('##DirChanged')
|
|
let b:startify.cwd = getcwd()
|
|
autocmd startify DirChanged <buffer> if getcwd() !=# get(get(b:, 'startify', {}), 'cwd') | Startify | endif
|
|
endif
|
|
if exists('#User#Startified')
|
|
doautocmd <nomodeline> User Startified
|
|
endif
|
|
if exists('#User#StartifyReady')
|
|
doautocmd <nomodeline> User StartifyReady
|
|
endif
|
|
endfunction
|
|
|
|
" Function: #session_load {{{1
|
|
function! startify#session_load(source_last_session, ...) abort
|
|
if !isdirectory(s:session_dir)
|
|
echomsg 'The session directory does not exist: '. s:session_dir
|
|
return
|
|
elseif empty(startify#session_list_as_string(''))
|
|
echomsg 'There are no sessions...'
|
|
return
|
|
endif
|
|
|
|
let session_path = s:session_dir . s:sep
|
|
|
|
if a:0
|
|
let session_path .= a:1
|
|
elseif a:source_last_session && !has('win32')
|
|
let session_path .= '__LAST__'
|
|
else
|
|
call inputsave()
|
|
let session_path .= input(
|
|
\ 'Load this session: ',
|
|
\ fnamemodify(v:this_session, ':t'),
|
|
\ 'custom,startify#session_list_as_string') | redraw
|
|
call inputrestore()
|
|
endif
|
|
|
|
if filereadable(session_path)
|
|
if get(g:, 'startify_session_persistence') && filewritable(v:this_session)
|
|
call startify#session_write(fnameescape(v:this_session))
|
|
endif
|
|
call startify#session_delete_buffers()
|
|
execute 'source '. fnameescape(session_path)
|
|
call s:create_last_session_link(session_path)
|
|
else
|
|
echo 'No such file: '. session_path
|
|
endif
|
|
endfunction
|
|
|
|
" Function: #session_save {{{1
|
|
function! startify#session_save(bang, ...) abort
|
|
if !isdirectory(s:session_dir)
|
|
if exists('*mkdir')
|
|
echo 'The session directory does not exist: '. s:session_dir .'. Create it? [y/n]'
|
|
if (nr2char(getchar()) == 'y')
|
|
call mkdir(s:session_dir, 'p')
|
|
else
|
|
echo
|
|
return
|
|
endif
|
|
else
|
|
echo 'The session directory does not exist: '. s:session_dir
|
|
return
|
|
endif
|
|
endif
|
|
|
|
call inputsave()
|
|
let this_session = fnamemodify(v:this_session, ':t')
|
|
if this_session ==# '__LAST__'
|
|
let this_session = ''
|
|
endif
|
|
let session_name = exists('a:1')
|
|
\ ? a:1
|
|
\ : input('Save under this session name: ', this_session, 'custom,startify#session_list_as_string') | redraw
|
|
call inputrestore()
|
|
|
|
if empty(session_name)
|
|
echo 'You gave an empty name!'
|
|
return
|
|
endif
|
|
|
|
let session_path = s:session_dir . s:sep . session_name
|
|
if !filereadable(session_path)
|
|
call startify#session_write(fnameescape(session_path))
|
|
echo 'Session saved under: '. session_path
|
|
return
|
|
endif
|
|
|
|
echo 'Session already exists. Overwrite? [y/n]' | redraw
|
|
if a:bang || nr2char(getchar()) == 'y'
|
|
call startify#session_write(fnameescape(session_path))
|
|
echo 'Session saved under: '. session_path
|
|
else
|
|
echo 'Did NOT save the session!'
|
|
endif
|
|
endfunction
|
|
|
|
" Function: #session_close {{{1
|
|
function! startify#session_close() abort
|
|
if exists('v:this_session') && filewritable(v:this_session)
|
|
call startify#session_write(fnameescape(v:this_session))
|
|
let v:this_session = ''
|
|
endif
|
|
call startify#session_delete_buffers()
|
|
Startify
|
|
endfunction
|
|
|
|
" Function: #session_write {{{1
|
|
function! startify#session_write(session_path)
|
|
" preserve existing variables from savevars
|
|
if exists('g:startify_session_savevars')
|
|
let savevars = map(filter(copy(g:startify_session_savevars), 'exists(v:val)'), '"let ". v:val ." = ". strtrans(string(eval(v:val)))')
|
|
endif
|
|
|
|
" if this function is called while being in the Startify buffer
|
|
" (by loading another session or running :SSave/:SLoad directly)
|
|
" switch back to the previous buffer before saving the session
|
|
if &filetype == 'startify'
|
|
let callingbuffer = bufnr('#')
|
|
if callingbuffer > 0
|
|
execute 'buffer' callingbuffer
|
|
endif
|
|
endif
|
|
" prevent saving already deleted buffers that were in the arglist
|
|
for arg in argv()
|
|
if !buflisted(arg)
|
|
execute 'silent! argdelete' fnameescape(arg)
|
|
endif
|
|
endfor
|
|
" clean up session before saving it
|
|
for cmd in get(g:, 'startify_session_before_save', [])
|
|
execute cmd
|
|
endfor
|
|
|
|
let ssop = &sessionoptions
|
|
set sessionoptions-=options
|
|
try
|
|
execute 'mksession!' a:session_path
|
|
catch
|
|
echohl ErrorMsg
|
|
echomsg v:exception
|
|
echohl NONE
|
|
return
|
|
finally
|
|
let &sessionoptions = ssop
|
|
endtry
|
|
|
|
if exists('g:startify_session_remove_lines')
|
|
\ || exists('g:startify_session_savevars')
|
|
\ || exists('g:startify_session_savecmds')
|
|
silent execute 'split' a:session_path
|
|
|
|
" remove lines from the session file
|
|
if exists('g:startify_session_remove_lines')
|
|
for pattern in g:startify_session_remove_lines
|
|
execute 'silent global/'. pattern .'/delete _'
|
|
endfor
|
|
endif
|
|
|
|
" put variables from savevars into session file
|
|
if exists('savevars') && !empty(savevars)
|
|
call append(line('$')-3, savevars)
|
|
endif
|
|
|
|
" put commands from savecmds into session file
|
|
if exists('g:startify_session_savecmds')
|
|
call append(line('$')-3, g:startify_session_savecmds)
|
|
endif
|
|
|
|
setlocal bufhidden=delete
|
|
silent update
|
|
silent hide
|
|
endif
|
|
|
|
call s:create_last_session_link(a:session_path)
|
|
endfunction
|
|
|
|
" Function: #session_delete {{{1
|
|
function! startify#session_delete(bang, ...) abort
|
|
if !isdirectory(s:session_dir)
|
|
echo 'The session directory does not exist: '. s:session_dir
|
|
return
|
|
elseif empty(startify#session_list_as_string(''))
|
|
echo 'There are no sessions...'
|
|
return
|
|
endif
|
|
|
|
call inputsave()
|
|
let session_path = s:session_dir . s:sep . (exists('a:1')
|
|
\ ? a:1
|
|
\ : input('Delete this session: ', fnamemodify(v:this_session, ':t'), 'custom,startify#session_list_as_string'))
|
|
call inputrestore()
|
|
|
|
if !filereadable(session_path)
|
|
echomsg 'No such session: '. session_path
|
|
return
|
|
endif
|
|
|
|
echo 'Really delete '. session_path .'? [y/n]'
|
|
if a:bang || nr2char(getchar()) == 'y'
|
|
if delete(session_path) == 0
|
|
echo 'Deleted session '. session_path .'!'
|
|
else
|
|
echo 'Deletion failed!'
|
|
endif
|
|
else
|
|
echo 'Deletion aborted!'
|
|
endif
|
|
endfunction
|
|
|
|
" Function: #session_delete_buffers {{{1
|
|
function! startify#session_delete_buffers()
|
|
if get(g:, 'startify_session_delete_buffers', 1)
|
|
silent! %bdelete!
|
|
endif
|
|
endfunction
|
|
|
|
" Function: #session_list {{{1
|
|
function! startify#session_list(lead, ...) abort
|
|
return filter(map(split(globpath(s:session_dir, '*'.a:lead.'*'), '\n'), 'fnamemodify(v:val, ":t")'), 'v:val !=# "__LAST__"')
|
|
endfunction
|
|
|
|
" Function: #session_list_as_string {{{1
|
|
function! startify#session_list_as_string(lead, ...) abort
|
|
return join(filter(map(split(globpath(s:session_dir, '*'.a:lead.'*'), '\n'), 'fnamemodify(v:val, ":t")'), 'v:val !=# "__LAST__"'), "\n")
|
|
endfunction
|
|
|
|
" Function: #debug {{{1
|
|
function! startify#debug()
|
|
if exists('b:startify.entries')
|
|
for k in sort(keys(b:startify.entries))
|
|
echomsg '['. k .'] = '. string(b:startify.entries[k])
|
|
endfor
|
|
else
|
|
call s:warn('This is no Startify buffer!')
|
|
endif
|
|
endfunction
|
|
|
|
" Function: #open_buffers {{{1
|
|
function! startify#open_buffers(...) abort
|
|
if exists('a:1') " used in mappings
|
|
let entry = b:startify.entries[a:1]
|
|
if !empty(s:batchmode) && entry.type == 'file'
|
|
call s:set_mark(s:batchmode, a:1)
|
|
else
|
|
call s:open_buffer(entry)
|
|
endif
|
|
return
|
|
endif
|
|
|
|
let marked = filter(copy(b:startify.entries), 'v:val.marked')
|
|
if empty(marked) " open current entry
|
|
call s:open_buffer(b:startify.entries[line('.')])
|
|
return
|
|
endif
|
|
|
|
enew
|
|
setlocal nobuflisted
|
|
|
|
" Open all marked entries.
|
|
for entry in sort(values(marked), 's:sort_by_tick')
|
|
call s:open_buffer(entry)
|
|
endfor
|
|
|
|
wincmd =
|
|
|
|
if exists('#User#StartifyAllBuffersOpened')
|
|
doautocmd <nomodeline> User StartifyAllBuffersOpened
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:get_lists {{{1
|
|
function! s:get_lists() abort
|
|
if exists('g:startify_lists')
|
|
return g:startify_lists
|
|
elseif exists('g:startify_list_order')
|
|
" Convert old g:startify_list_order format to newer g:startify_lists format.
|
|
let lists = []
|
|
for item in g:startify_list_order
|
|
if type(item) == type([])
|
|
let header = item
|
|
else
|
|
if exists('header')
|
|
let lists += [{ 'type': item, 'header': header }]
|
|
unlet header
|
|
else
|
|
let lists += [{ 'type': item }]
|
|
endif
|
|
endif
|
|
unlet item
|
|
endfor
|
|
return lists
|
|
else
|
|
return [
|
|
\ { 'header': [s:padding_left .'MRU'], 'type': 'files' },
|
|
\ { 'header': [s:padding_left .'MRU '. getcwd()], 'type': 'dir' },
|
|
\ { 'header': [s:padding_left .'Sessions'], 'type': 'sessions' },
|
|
\ { 'header': [s:padding_left .'Bookmarks'], 'type': 'bookmarks' },
|
|
\ { 'header': [s:padding_left .'Commands'], 'type': 'commands' },
|
|
\ ]
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:show_lists {{{1
|
|
function! s:show_lists(lists) abort
|
|
for list in a:lists
|
|
if !has_key(list, 'type')
|
|
continue
|
|
endif
|
|
|
|
let b:startify.indices = copy(get(list, 'indices', []))
|
|
|
|
if type(list.type) == type('')
|
|
if has_key(list, 'header')
|
|
let s:last_message = list.header
|
|
endif
|
|
call s:show_{list.type}()
|
|
elseif type(list.type) == type(function('tr'))
|
|
try
|
|
let entries = list.type()
|
|
catch
|
|
call s:warn(v:exception)
|
|
continue
|
|
endtry
|
|
if empty(entries)
|
|
unlet! s:last_message
|
|
continue
|
|
endif
|
|
|
|
if has_key(list, 'header')
|
|
let s:last_message = list.header
|
|
call s:print_section_header()
|
|
endif
|
|
|
|
for entry in entries
|
|
let cmd = get(entry, 'cmd', 'edit')
|
|
let path = get(entry, 'path', '')
|
|
let type = get(entry, 'type', empty(path) ? 'special' : 'file')
|
|
let index = s:get_index_as_string()
|
|
call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . entry.line)
|
|
call s:register(line('$'), index, type, cmd, path)
|
|
endfor
|
|
call append('$', '')
|
|
else
|
|
call s:warn('Wrong format for g:startify_lists: '. string(list))
|
|
endif
|
|
endfor
|
|
endfunction
|
|
|
|
" Function: s:open_buffer {{{1
|
|
function! s:open_buffer(entry)
|
|
if a:entry.type == 'special'
|
|
execute a:entry.cmd
|
|
elseif a:entry.type == 'session'
|
|
execute a:entry.cmd a:entry.path
|
|
elseif a:entry.type == 'file'
|
|
if line2byte('$') == -1
|
|
execute 'edit' a:entry.path
|
|
else
|
|
if a:entry.cmd == 'tabnew'
|
|
wincmd =
|
|
endif
|
|
execute a:entry.cmd a:entry.path
|
|
endif
|
|
call s:check_user_options(a:entry.path)
|
|
endif
|
|
if exists('#User#StartifyBufferOpened')
|
|
doautocmd <nomodeline> User StartifyBufferOpened
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:set_custom_section {{{1
|
|
function! s:set_custom_section(section) abort
|
|
if type(a:section) == type([])
|
|
return copy(a:section)
|
|
elseif type(a:section) == type('')
|
|
return empty(a:section) ? [] : eval(a:section)
|
|
endif
|
|
return []
|
|
endfunction
|
|
|
|
" Function: s:display_by_path {{{1
|
|
function! s:display_by_path(path_prefix, path_format, use_env) abort
|
|
let oldfiles = call(get(g:, 'startify_enable_unsafe') ? 's:filter_oldfiles_unsafe' : 's:filter_oldfiles',
|
|
\ [a:path_prefix, a:path_format, a:use_env])
|
|
|
|
let entry_format = "s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) ."
|
|
if exists('*StartifyEntryFormat')
|
|
let entry_format .= StartifyEntryFormat()
|
|
else
|
|
let entry_format .= 'entry_path'
|
|
endif
|
|
|
|
if !empty(oldfiles)
|
|
if exists('s:last_message')
|
|
call s:print_section_header()
|
|
endif
|
|
|
|
for [absolute_path, entry_path] in oldfiles
|
|
let index = s:get_index_as_string()
|
|
call append('$', eval(entry_format))
|
|
if has('win32')
|
|
let absolute_path = substitute(absolute_path, '\[', '\[[]', 'g')
|
|
endif
|
|
call s:register(line('$'), index, 'file', 'edit', absolute_path)
|
|
endfor
|
|
|
|
call append('$', '')
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:filter_oldfiles {{{1
|
|
function! s:filter_oldfiles(path_prefix, path_format, use_env) abort
|
|
let path_prefix = '\V'. escape(a:path_prefix, '\')
|
|
let counter = s:numfiles
|
|
let entries = {}
|
|
let oldfiles = []
|
|
|
|
for fname in v:oldfiles
|
|
if counter <= 0
|
|
break
|
|
endif
|
|
|
|
if s:is_in_skiplist(fname)
|
|
" https://github.com/mhinz/vim-startify/issues/353
|
|
continue
|
|
endif
|
|
|
|
let absolute_path = fnamemodify(resolve(fname), ":p")
|
|
" filter duplicates, bookmarks and entries from the skiplist
|
|
if has_key(entries, absolute_path)
|
|
\ || !filereadable(absolute_path)
|
|
\ || s:is_in_skiplist(absolute_path)
|
|
\ || match(absolute_path, path_prefix)
|
|
continue
|
|
endif
|
|
|
|
let entry_path = ''
|
|
if s:tf
|
|
let entry_path = s:transform(absolute_path)
|
|
endif
|
|
if empty(entry_path)
|
|
let entry_path = fnamemodify(absolute_path, a:path_format)
|
|
endif
|
|
|
|
let entries[absolute_path] = 1
|
|
let counter -= 1
|
|
let oldfiles += [[fnameescape(absolute_path), entry_path]]
|
|
endfor
|
|
|
|
if a:use_env
|
|
call s:init_env()
|
|
for i in range(len(oldfiles))
|
|
for [k,v] in s:env
|
|
let p = oldfiles[i][0]
|
|
if !stridx(tolower(p), tolower(v))
|
|
let oldfiles[i][1] = printf('$%s%s', k, p[len(v):])
|
|
break
|
|
endif
|
|
endfor
|
|
endfor
|
|
endif
|
|
|
|
return oldfiles
|
|
endfunction
|
|
|
|
" Function: s:filter_oldfiles_unsafe {{{1
|
|
function! s:filter_oldfiles_unsafe(path_prefix, path_format, use_env) abort
|
|
let path_prefix = '\V'. escape(a:path_prefix, '\')
|
|
let counter = s:numfiles
|
|
let entries = {}
|
|
let oldfiles = []
|
|
let is_dir = escape(s:sep, '\') . '$'
|
|
|
|
for fname in v:oldfiles
|
|
if counter <= 0
|
|
break
|
|
endif
|
|
|
|
if s:is_in_skiplist(fname)
|
|
" https://github.com/mhinz/vim-startify/issues/353
|
|
continue
|
|
endif
|
|
|
|
let absolute_path = glob(fnamemodify(fname, ":p"))
|
|
if empty(absolute_path)
|
|
\ || has_key(entries, absolute_path)
|
|
\ || (absolute_path =~ is_dir)
|
|
\ || match(absolute_path, path_prefix)
|
|
\ || s:is_in_skiplist(absolute_path)
|
|
continue
|
|
endif
|
|
|
|
let entry_path = fnamemodify(absolute_path, a:path_format)
|
|
let entries[absolute_path] = 1
|
|
let counter -= 1
|
|
let oldfiles += [[fnameescape(absolute_path), entry_path]]
|
|
endfor
|
|
|
|
return oldfiles
|
|
endfunction
|
|
|
|
" Function: s:show_dir {{{1
|
|
function! s:show_dir() abort
|
|
return s:display_by_path(getcwd() . s:sep, ':.', 0)
|
|
endfunction
|
|
|
|
" Function: s:show_files {{{1
|
|
function! s:show_files() abort
|
|
return s:display_by_path('', s:relative_path, get(g:, 'startify_use_env'))
|
|
endfunction
|
|
|
|
" Function: s:show_sessions {{{1
|
|
function! s:show_sessions() abort
|
|
let limit = get(g:, 'startify_session_number', 999) - 1
|
|
if limit <= -1
|
|
return
|
|
endif
|
|
|
|
let sfiles = split(globpath(s:session_dir, '*'), '\n')
|
|
let sfiles = filter(sfiles, 'v:val !~# "__LAST__$"')
|
|
let sfiles = filter(sfiles,
|
|
\ '!(v:val =~# "x\.vim$" && index(sfiles, v:val[:-6].".vim") >= 0)')
|
|
if empty(sfiles)
|
|
if exists('s:last_message')
|
|
unlet s:last_message
|
|
endif
|
|
return
|
|
endif
|
|
|
|
if exists('s:last_message')
|
|
call s:print_section_header()
|
|
endif
|
|
|
|
if get(g:, 'startify_session_sort')
|
|
function! s:sort_by_mtime(foo, bar)
|
|
let foo = getftime(a:foo)
|
|
let bar = getftime(a:bar)
|
|
return foo == bar ? 0 : (foo < bar ? 1 : -1)
|
|
endfunction
|
|
call sort(sfiles, 's:sort_by_mtime')
|
|
endif
|
|
|
|
for i in range(len(sfiles))
|
|
let index = s:get_index_as_string()
|
|
let fname = fnamemodify(sfiles[i], ':t')
|
|
let dname = sfiles[i] ==# v:this_session ? fname.' (*)' : fname
|
|
call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . dname)
|
|
if has('win32')
|
|
let fname = substitute(fname, '\[', '\[[]', 'g')
|
|
endif
|
|
call s:register(line('$'), index, 'session', 'SLoad', fname)
|
|
if i == limit
|
|
break
|
|
endif
|
|
endfor
|
|
|
|
call append('$', '')
|
|
endfunction
|
|
|
|
" Function: s:show_bookmarks {{{1
|
|
function! s:show_bookmarks() abort
|
|
if !exists('g:startify_bookmarks') || empty(g:startify_bookmarks)
|
|
return
|
|
endif
|
|
|
|
if exists('s:last_message')
|
|
call s:print_section_header()
|
|
endif
|
|
|
|
for bookmark in g:startify_bookmarks
|
|
if type(bookmark) == type({})
|
|
let [index, path] = items(bookmark)[0]
|
|
else " string
|
|
let [index, path] = [s:get_index_as_string(), bookmark]
|
|
endif
|
|
|
|
let entry_path = ''
|
|
if s:tf
|
|
let entry_path = s:transform(fnamemodify(resolve(expand(path)), ':p'))
|
|
endif
|
|
if empty(entry_path)
|
|
let entry_path = path
|
|
endif
|
|
call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . entry_path)
|
|
|
|
if has('win32')
|
|
let path = substitute(path, '\[', '\[[]', 'g')
|
|
endif
|
|
call s:register(line('$'), index, 'file', 'edit', fnameescape(expand(path)))
|
|
|
|
unlet bookmark " avoid type mismatch for heterogeneous lists
|
|
endfor
|
|
|
|
call append('$', '')
|
|
endfunction
|
|
|
|
" Function: s:show_commands {{{1
|
|
function! s:show_commands() abort
|
|
if !exists('g:startify_commands') || empty(g:startify_commands)
|
|
return
|
|
endif
|
|
|
|
if exists('s:last_message')
|
|
call s:print_section_header()
|
|
endif
|
|
|
|
for entry in g:startify_commands
|
|
if type(entry) == type({}) " with custom index
|
|
let [index, command] = items(entry)[0]
|
|
else
|
|
let command = entry
|
|
let index = s:get_index_as_string()
|
|
endif
|
|
" If no list is given, the description is the command itself.
|
|
let [desc, cmd] = type(command) == type([]) ? command : [command, command]
|
|
|
|
call append('$', s:padding_left .'['. index .']'. repeat(' ', (3 - strlen(index))) . desc)
|
|
call s:register(line('$'), index, 'special', cmd, '')
|
|
|
|
unlet entry command
|
|
endfor
|
|
|
|
call append('$', '')
|
|
endfunction
|
|
|
|
" Function: s:is_in_skiplist {{{1
|
|
function! s:is_in_skiplist(arg) abort
|
|
for regexp in s:skiplist
|
|
try
|
|
if a:arg =~# regexp
|
|
return 1
|
|
endif
|
|
catch
|
|
call s:warn('Pattern '. string(regexp) .' threw an exception. Read :help g:startify_skiplist')
|
|
endtry
|
|
endfor
|
|
endfunction
|
|
|
|
" Function: s:set_cursor {{{1
|
|
function! s:set_cursor() abort
|
|
let b:startify.oldline = exists('b:startify.newline') ? b:startify.newline : 2 + len(s:padding_left)
|
|
let b:startify.newline = line('.')
|
|
|
|
" going up (-1) or down (1)
|
|
if b:startify.oldline == b:startify.newline
|
|
\ && col('.') != s:fixed_column
|
|
\ && !b:startify.leftmouse
|
|
let movement = 2 * (col('.') > s:fixed_column) - 1
|
|
let b:startify.newline += movement
|
|
else
|
|
let movement = 2 * (b:startify.newline > b:startify.oldline) - 1
|
|
let b:startify.leftmouse = 0
|
|
endif
|
|
|
|
" skip section headers lines until an entry is found
|
|
while index(b:startify.section_header_lines, b:startify.newline) != -1
|
|
let b:startify.newline += movement
|
|
endwhile
|
|
|
|
" skip blank lines between lists
|
|
if empty(getline(b:startify.newline))
|
|
let b:startify.newline += movement
|
|
endif
|
|
|
|
" don't go beyond first or last entry
|
|
let b:startify.newline = max([b:startify.firstline, min([b:startify.lastline, b:startify.newline])])
|
|
|
|
call cursor(b:startify.newline, s:fixed_column)
|
|
endfunction
|
|
|
|
" Function: s:set_mappings {{{1
|
|
function! s:set_mappings() abort
|
|
nnoremap <buffer><nowait><silent> i :enew <bar> startinsert<cr>
|
|
nnoremap <buffer><nowait><silent> <insert> :enew <bar> startinsert<cr>
|
|
nnoremap <buffer><nowait><silent> b :call <sid>set_mark('B')<cr>
|
|
nnoremap <buffer><nowait><silent> s :call <sid>set_mark('S')<cr>
|
|
nnoremap <buffer><nowait><silent> t :call <sid>set_mark('T')<cr>
|
|
nnoremap <buffer><nowait><silent> v :call <sid>set_mark('V')<cr>
|
|
nnoremap <buffer><nowait><silent> B :call startify#set_batchmode('B')<cr>
|
|
nnoremap <buffer><nowait><silent> S :call startify#set_batchmode('S')<cr>
|
|
nnoremap <buffer><nowait><silent> T :call startify#set_batchmode('T')<cr>
|
|
nnoremap <buffer><nowait><silent> V :call startify#set_batchmode('V')<cr>
|
|
nnoremap <buffer><nowait><silent> <cr> :call startify#open_buffers()<cr>
|
|
nnoremap <buffer><nowait><silent> <LeftMouse> :call <sid>leftmouse()<cr>
|
|
nnoremap <buffer><nowait><silent> <2-LeftMouse> :call startify#open_buffers()<cr>
|
|
nnoremap <buffer><nowait><silent> <MiddleMouse> :enew <bar> execute 'normal! "'.(v:register=='"'?'*':v:register).'gp'<cr>
|
|
|
|
" Without these mappings n/N wouldn't work properly, since autocmds always
|
|
" force the cursor back on the index.
|
|
nnoremap <buffer><expr> n ' j'[v:searchforward].'n'
|
|
nnoremap <buffer><expr> N 'j '[v:searchforward].'N'
|
|
|
|
function! s:leftmouse()
|
|
" feedkeys() triggers CursorMoved which calls s:set_cursor() which checks
|
|
" .leftmouse.
|
|
let b:startify.leftmouse = 1
|
|
call feedkeys("\<LeftMouse>", 'nt')
|
|
endfunction
|
|
|
|
function! s:compare_by_index(foo, bar)
|
|
return a:foo.index - a:bar.index
|
|
endfunction
|
|
|
|
for entry in sort(values(b:startify.entries), 's:compare_by_index')
|
|
execute 'nnoremap <buffer><silent><nowait>' entry.index
|
|
\ ':call startify#open_buffers('. string(entry.line) .')<cr>'
|
|
endfor
|
|
endfunction
|
|
|
|
" Function: #set_batchmode {{{1
|
|
function! startify#set_batchmode(batchmode) abort
|
|
let s:batchmode = (a:batchmode == s:batchmode) ? '' : a:batchmode
|
|
echo empty(s:batchmode) ? 'Batchmode off' : 'Batchmode: '. s:batchmode
|
|
endfunction
|
|
|
|
" Function: s:set_mark {{{1
|
|
function! s:set_mark(type, ...) abort
|
|
if a:0
|
|
let entryline = a:1
|
|
else
|
|
call startify#set_batchmode('')
|
|
let entryline = line('.')
|
|
endif
|
|
let entry = b:startify.entries[entryline]
|
|
|
|
if entry.type != 'file'
|
|
return
|
|
endif
|
|
|
|
let default_cmds = {
|
|
\ 'B': 'edit',
|
|
\ 'S': 'split',
|
|
\ 'V': 'vsplit',
|
|
\ 'T': 'tabnew',
|
|
\ }
|
|
|
|
let origline = line('.')
|
|
execute entryline
|
|
let index = expand('<cword>')
|
|
setlocal modifiable
|
|
|
|
if entry.marked && index[0] == a:type
|
|
let entry.cmd = 'edit'
|
|
let entry.marked = 0
|
|
execute 'normal! ci]'. entry.index
|
|
else
|
|
let entry.cmd = default_cmds[a:type]
|
|
let entry.marked = 1
|
|
let entry.tick = b:startify.tick
|
|
let b:startify.tick += 1
|
|
execute 'normal! ci]'. repeat(a:type, len(index))
|
|
endif
|
|
|
|
setlocal nomodifiable nomodified
|
|
" Reset cursor to fixed column, which is important for s:set_cursor().
|
|
call cursor(origline, s:fixed_column)
|
|
endfunction
|
|
|
|
" Function: s:sort_by_tick {{{1
|
|
function! s:sort_by_tick(one, two)
|
|
return a:one.tick - a:two.tick
|
|
endfunction
|
|
|
|
" Function: s:check_user_options {{{1
|
|
function! s:check_user_options(path) abort
|
|
let session = a:path . s:sep .'Session.vim'
|
|
|
|
if get(g:, 'startify_session_autoload') && filereadable(glob(session))
|
|
execute 'silent bwipeout' a:path
|
|
call startify#session_delete_buffers()
|
|
execute 'source' session
|
|
return
|
|
endif
|
|
|
|
if get(g:, 'startify_change_to_vcs_root') && s:cd_to_vcs_root(a:path)
|
|
return
|
|
endif
|
|
|
|
if get(g:, 'startify_change_to_dir', 1)
|
|
if isdirectory(a:path)
|
|
execute 'lcd' a:path
|
|
else
|
|
let dir = fnamemodify(a:path, ':h')
|
|
if isdirectory(dir)
|
|
execute 'lcd' dir
|
|
else
|
|
" Do nothing. E.g. a:path == `scp://foo/bar`
|
|
endif
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:cd_to_vcs_root {{{1
|
|
function! s:cd_to_vcs_root(path) abort
|
|
let dir = fnamemodify(a:path, ':p:h')
|
|
for vcs in [ '.git', '.hg', '.bzr', '.svn' ]
|
|
let root = finddir(vcs, dir .';')
|
|
if !empty(root)
|
|
execute 'lcd' fnameescape(fnamemodify(root, ':h'))
|
|
return 1
|
|
endif
|
|
endfor
|
|
return 0
|
|
endfunction
|
|
|
|
" Function: s:close {{{1
|
|
function! s:close() abort
|
|
if len(filter(range(0, bufnr('$')), 'buflisted(v:val)')) - &buflisted
|
|
if bufloaded(bufnr('#')) && bufnr('#') != bufnr('%')
|
|
buffer #
|
|
else
|
|
bnext
|
|
endif
|
|
else
|
|
quit
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:get_index_as_string {{{1
|
|
function! s:get_index_as_string() abort
|
|
if !empty(b:startify.indices)
|
|
return remove(b:startify.indices, 0)
|
|
elseif exists('g:startify_custom_indices')
|
|
let listlen = len(g:startify_custom_indices)
|
|
if b:startify.entry_number < listlen
|
|
let idx = g:startify_custom_indices[b:startify.entry_number]
|
|
else
|
|
let idx = string(b:startify.entry_number - listlen)
|
|
endif
|
|
else
|
|
let idx = string(b:startify.entry_number)
|
|
endif
|
|
|
|
let b:startify.entry_number += 1
|
|
|
|
return idx
|
|
endfunction
|
|
|
|
" Function: s:print_section_header {{{1
|
|
function! s:print_section_header() abort
|
|
$
|
|
let curline = line('.')
|
|
|
|
for lnum in range(curline, curline + len(s:last_message) + 1)
|
|
call add(b:startify.section_header_lines, lnum)
|
|
endfor
|
|
|
|
call append('$', s:last_message + [''])
|
|
unlet s:last_message
|
|
endfunction
|
|
|
|
" Function: s:register {{{1
|
|
function! s:register(line, index, type, cmd, path)
|
|
let b:startify.entries[a:line] = {
|
|
\ 'index': a:index,
|
|
\ 'type': a:type,
|
|
\ 'line': a:line,
|
|
\ 'cmd': a:cmd,
|
|
\ 'path': a:path,
|
|
\ 'marked': 0,
|
|
\ }
|
|
endfunction
|
|
|
|
" Function: s:create_last_session_link {{{1
|
|
function! s:create_last_session_link(session_path)
|
|
if !has('win32') && a:session_path !~# '__LAST__$'
|
|
let cmd = printf('ln -sf %s %s',
|
|
\ shellescape(fnamemodify(a:session_path, ':t')),
|
|
\ shellescape(s:session_dir .'/__LAST__'))
|
|
call system(cmd)
|
|
if v:shell_error
|
|
call s:warn("Can't create 'last used session' symlink.")
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
" Function: s:init_env {{{1
|
|
function! s:init_env()
|
|
let s:env = []
|
|
let ignore = {
|
|
\ 'HOME': 1,
|
|
\ 'OLDPWD': 1,
|
|
\ 'PWD': 1,
|
|
\ }
|
|
|
|
function! s:get_env()
|
|
redir => s
|
|
silent! execute "norm!:ec$\<c-a>'\<c-b>\<right>\<right>\<del>'\<cr>"
|
|
redir END
|
|
redraw
|
|
return split(s)
|
|
endfunction
|
|
|
|
function! s:compare_by_key_len(foo, bar)
|
|
return len(a:foo[0]) - len(a:bar[0])
|
|
endfunction
|
|
function! s:compare_by_val_len(foo, bar)
|
|
return len(a:bar[1]) - len(a:foo[1])
|
|
endfunction
|
|
|
|
for k in s:get_env()
|
|
silent! execute "let v = eval('$'.k)"
|
|
if has('win32') ? (v[1] != ':') : (v[0] != '/')
|
|
\ || has_key(ignore, k)
|
|
\ || len(k) > len(v)
|
|
continue
|
|
endif
|
|
call insert(s:env, [k,v], 0)
|
|
endfor
|
|
|
|
let s:env = sort(s:env, 's:compare_by_key_len')
|
|
let s:env = sort(s:env, 's:compare_by_val_len')
|
|
endfunction
|
|
|
|
" Function: s:transform {{{1
|
|
function s:transform(absolute_path)
|
|
for [k,V] in g:startify_transformations
|
|
if a:absolute_path =~ k
|
|
return type(V) == type('') ? V : V(a:absolute_path)
|
|
endif
|
|
unlet V
|
|
endfor
|
|
return ''
|
|
endfunction
|
|
|
|
" Function: s:warn {{{1
|
|
function! s:warn(msg) abort
|
|
echohl WarningMsg
|
|
echomsg 'startify: '. a:msg
|
|
echohl NONE
|
|
endfunction
|
|
|
|
" Init: values {{{1
|
|
let s:sep = startify#get_separator()
|
|
|
|
let s:numfiles = get(g:, 'startify_files_number', 10)
|
|
let s:show_special = get(g:, 'startify_enable_special', 1)
|
|
let s:relative_path = get(g:, 'startify_relative_path') ? ':~:.' : ':p:~'
|
|
let s:tf = exists('g:startify_transformations')
|
|
let s:session_dir = startify#get_session_path()
|
|
|
|
let s:skiplist = get(g:, 'startify_skiplist', [
|
|
\ 'runtime/doc/.*\.txt',
|
|
\ 'bundle/.*/doc/.*\.txt',
|
|
\ 'plugged/.*/doc/.*\.txt',
|
|
\ '/.git/',
|
|
\ 'fugitiveblame$',
|
|
\ escape(fnamemodify(resolve($VIMRUNTIME), ':p'), '\') .'doc/.*\.txt',
|
|
\ ])
|
|
|
|
let s:padding_left = repeat(' ', get(g:, 'startify_padding_left', 3))
|
|
let s:fixed_column = len(s:padding_left) + 2
|
|
let s:batchmode = ''
|