" Vimball Archiver by Charles E. Campbell, Jr., Ph.D. UseVimball finish doc/tmru.txt [[[1 301 *tmru.txt* Most Recently Used Files Author: Tom Link, micathom at gmail com This plugin allows users to open recently used files. Users can filter the file list by typing a pattern. Users can open multiple files at once. The list of recently used files is synchronized across multiple instances of (g)vim running simultaneously. By default, tmru will remember 500 files (see |g:tmruSize|) accessible via the :TRecentlyUsedFiles command. The list can be easily filtered. Users can optionally use fuzzy patterns, which is not enabled by default though (see |g:tlib#input#filter_mode|). Files can be grouped in sessions: - Use named sessions to group files that are frequently edited together - Use numbered sessions to open files you edited during one of the latest editing sessions Files can be marked as "sticky" -- they will never be removed from the list. :TRecentlyUsedFiles ... open one or more recently used file(s) :TRecentlyUsedFilesEdit ... edit the mru list If viminfo contains "!", the data is stored as global variable. Otherwise, tlib#cache is used -- which saves the data in ${vimfiles}/cache/tmru/files by default. By default tmru matches the search pattern on the full filename. If you want to match on the basename, add the following to your |.vimrc| file: > let g:tmru_world = {} let g:tmru_world.filter_format = 'fnamemodify(%s, ":t")' If you want to retain the last filter between calls of :TRecentlyUsedFiles, you could also add the following lines: > let g:tmru_world.cache_var = 'g:tmru_cache' let g:tmru_world.restore_from_cache = ['filter'] ----------------------------------------------------------------------- Install~ Edit the vba file and type: > :so % See :help vimball for details. If you have difficulties or use vim 7.0, please make sure, you have the current version of vimball (vimscript #1502) installed or update your runtime. This script requires tlib (vimscript #1863) to be installed. Suggested maps (to be set in ~/.vimrc): > noremap :TRecentlyUsedFiles ======================================================================== Contents~ g:tmruMenu ......................... |g:tmruMenu| g:tmruMenuSize ..................... |g:tmruMenuSize| g:tmru_sessions .................... |g:tmru_sessions| g:tmru#display_relative_filename ... |g:tmru#display_relative_filename| g:tmru_single_instance_mode ........ |g:tmru_single_instance_mode| g:tmru_update_viminfo .............. |g:tmru_update_viminfo| g:tmru_events ...................... |g:tmru_events| g:tmru_file ........................ |g:tmru_file| g:tmruSize ......................... |g:tmruSize| g:tmruExclude ...................... |g:tmruExclude| g:tmru_ignorecase .................. |g:tmru_ignorecase| g:tmru_check_disk .................. |g:tmru_check_disk| TmruObj ............................ |TmruObj()| TmruGetItem ........................ |TmruGetItem()| TmruInsert ......................... |TmruInsert()| :TRecentlyUsedFiles ................ |:TRecentlyUsedFiles| :TMRU .............................. |:TMRU| :TRecentlyUsedFilesEdit ............ |:TRecentlyUsedFilesEdit| :TRecentlyUsedFilesSessions ........ |:TRecentlyUsedFilesSessions| g:tmru#set_filename_indicators ..... |g:tmru#set_filename_indicators| g:tmru#sessions_len ................ |g:tmru#sessions_len| g:tmru_select_filter ............... |g:tmru_select_filter| g:tmru#drop ........................ |g:tmru#drop| g:tmru#auto_remove_unreadable ...... |g:tmru#auto_remove_unreadable| tmru#SelectMRU ..................... |tmru#SelectMRU()| tmru#EditMRU ....................... |tmru#EditMRU()| tmru#EditFiles ..................... |tmru#EditFiles()| tmru#Session ....................... |tmru#Session()| tmru#Leave ......................... |tmru#Leave()| tmru#DisplayUnreadableFiles ........ |tmru#DisplayUnreadableFiles()| tmru#CheckFilenames ................ |tmru#CheckFilenames()| tmru#RemoveItem .................... |tmru#RemoveItem()| tmru#Drop .......................... |tmru#Drop()| tmru#UnsetPersistent ............... |tmru#UnsetPersistent()| tmru#TogglePersistent .............. |tmru#TogglePersistent()| tmru#PreviousSession ............... |tmru#PreviousSession()| tmru#SelectNamedSession ............ |tmru#SelectNamedSession()| tmru#OpenNamedSession .............. |tmru#OpenNamedSession()| tmru#SessionNames .................. |tmru#SessionNames()| tmru#AddNamedSession ............... |tmru#AddNamedSession()| tmru#RemoveNamedSession ............ |tmru#RemoveNamedSession()| tmru#EditNamedSessions ............. |tmru#EditNamedSessions()| tmru#SetFilenameIndicators ......... |tmru#SetFilenameIndicators()| ======================================================================== plugin/tmru.vim~ *g:tmruMenu* g:tmruMenu (default: 'File.M&RU.') The menu's prefix. If the value is "", the menu will be disabled. *g:tmruMenuSize* g:tmruMenuSize (default: 20) The number of recently edited files that are displayed in the menu. *g:tmru_sessions* g:tmru_sessions (default: 9) If greater than zero, make tmru to save the file list opened when closing vim. Save at most information for the N latest sessions. Setting this variable to 0, disables this feature. This variable must be set before starting vim. *g:tmru#display_relative_filename* g:tmru#display_relative_filename (default: 0) If true, display the relative filename. If this options is used with |g:tlib#input#format_filename| set to "l", |g:tlib_inputlist_filename_indicators| doesn't work. *g:tmru_single_instance_mode* g:tmru_single_instance_mode (default: 0) If true, work as if only one instance of vim is running. This results in reading and writing the mru list less frequently from/to disk. The list won't be synchronized across multiple instances of vim running in parallel. *g:tmru_update_viminfo* g:tmru_update_viminfo (default: !g:tmru_single_instance_mode) If true, load and save the viminfo file on certain events -- see |g:tmru_events|. This is useful if 'viminfo' includes '!' and |g:tmru_file| is empty and you run multiple instances of vim. *g:tmru_events* g:tmru_events (default: {...}) A dictionary of {EVENT: ACTION = BOOL, ...}, where ACTION is one of the following: load ....... Load the external representation from disk register ... Register the current buffer save ....... Save mru list to disk (currently ignored) *g:tmru_file* g:tmru_file (default: tlib#persistent#Filename('tmru', 'files', 1)) Where to save the file list. The default value is only effective, if 'viminfo' doesn't contain '!' -- in which case the 'viminfo' will be used. *g:tmruSize* g:tmruSize (default: empty(g:tmru_file) ? 50 : 500) The number of recently edited files that are registered. The size is smaller if viminfo is used (see |g:tmru_file|). *g:tmruExclude* g:tmruExclude (default: '/te\?mp/\|vim.\{-}/\(doc\|cache\)/\|__.\{-}__$') Ignore files matching this regexp. *g:tmru_ignorecase* g:tmru_ignorecase (default: !has('fname_case')) If true, ignore case when comparing filenames. *g:tmru_check_disk* g:tmru_check_disk (default: 1) If TRUE, allow disk checks when adding files to the list by means of a registered event (see |g:tmru_events|). This may cause annoying slow-downs in certain settings. In this case, set this variable to 0 in your |vimrc| file. *TmruObj()* TmruObj(...) *TmruGetItem()* TmruGetItem(tmruobj, filename) *TmruInsert()* TmruInsert(tmruobj, oldpos, item) *:TRecentlyUsedFiles* :TRecentlyUsedFiles Display the MRU list. *:TMRU* :TMRU Alias for |:TRecentlyUsedFiles|. *:TRecentlyUsedFilesEdit* :TRecentlyUsedFilesEdit Edit the MRU list. *:TRecentlyUsedFilesSessions* :TRecentlyUsedFilesSessions Open files from a previous session (see |g:tmru_sessions|). This command is only available if g:tmru_sessions > 0. ======================================================================== autoload/tmru.vim~ *g:tmru#set_filename_indicators* g:tmru#set_filename_indicators (default: 1) *g:tmru#sessions_len* g:tmru#sessions_len (default: 3) Remember at most N sessions per file. *g:tmru_select_filter* g:tmru_select_filter (default: '') If non-empty, an expression to |filter()| the list of files. Can also be buffer-local. *g:tmru#drop* g:tmru#drop (default: has('gui')) If true, use |:drop| to edit loaded buffers (only available with GUI). *g:tmru#auto_remove_unreadable* g:tmru#auto_remove_unreadable (default: 1) If true, automatically remove unreadable files from the mru list, when trying to edit them. *tmru#SelectMRU()* tmru#SelectMRU() *tmru#EditMRU()* tmru#EditMRU() *tmru#EditFiles()* tmru#EditFiles(filenames, ...) *tmru#Session()* tmru#Session(session_no, mru) *tmru#Leave()* tmru#Leave() *tmru#DisplayUnreadableFiles()* tmru#DisplayUnreadableFiles(mru) *tmru#CheckFilenames()* tmru#CheckFilenames(world, selected) Validate list of filenames in mru list. This checks that files are readable and removes any (canonicalized) duplicates. *tmru#RemoveItem()* tmru#RemoveItem(world, selected) *tmru#Drop()* tmru#Drop(world, selected) *tmru#UnsetPersistent()* tmru#UnsetPersistent(world, selected) *tmru#TogglePersistent()* tmru#TogglePersistent(world, selected) *tmru#PreviousSession()* tmru#PreviousSession(world, selected) *tmru#SelectNamedSession()* tmru#SelectNamedSession(world, selected) *tmru#OpenNamedSession()* tmru#OpenNamedSession(world, selected) *tmru#SessionNames()* tmru#SessionNames(...) *tmru#AddNamedSession()* tmru#AddNamedSession(world, selected) *tmru#RemoveNamedSession()* tmru#RemoveNamedSession(world, selected) *tmru#EditNamedSessions()* tmru#EditNamedSessions(world, selected) *tmru#SetFilenameIndicators()* tmru#SetFilenameIndicators(world, mru) vim:tw=78:fo=w2croql:isk=!-~,^*,^|,^":ts=8:ft=help:norl: plugin/tmru.vim [[[1 481 " tmru.vim -- Most Recently Used Files " @Author: Tom Link (micathom AT gmail com?subject=vim-tlib-mru) " @Website: http://www.vim.org/account/profile.php?user_id=4037 " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Created: 2007-04-13. " @Last Change: 2014-02-05. " @Revision: 911 " GetLatestVimScripts: 1864 1 tmru.vim if &cp || exists("loaded_tmru") finish endif if !exists('loaded_tlib') || loaded_tlib < 106 echoerr "tlib >= 1.06 is required" finish endif let loaded_tmru = 103 let s:save_cpo = &cpo set cpo&vim if !exists("g:tmruMenu") " The menu's prefix. If the value is "", the menu will be disabled. let g:tmruMenu = 'File.M&RU.' "{{{2 endif if !exists("g:tmruMenuSize") " The number of recently edited files that are displayed in the " menu. let g:tmruMenuSize = 20 "{{{2 endif if !exists('g:tmru_sessions') " If greater than zero, make tmru to save the file list opened when " closing vim. Save at most information for the N latest sessions. " " Setting this variable to 0, disables this feature. " " This variable must be set before starting vim. let g:tmru_sessions = 9 "{{{2 endif if !exists('g:tmru#display_relative_filename') " If true, display the relative filename. " " If this options is used with |g:tlib#input#format_filename| set to " "l", |g:tlib_inputlist_filename_indicators| doesn't work. let g:tmru#display_relative_filename = 0 "{{{2 endif if !exists('g:tmru_single_instance_mode') " If true, work as if only one instance of vim is running. This " results in reading and writing the mru list less frequently " from/to disk. The list won't be synchronized across multiple " instances of vim running in parallel. let g:tmru_single_instance_mode = 0 "{{{2 endif if !exists('g:tmru_update_viminfo') " If true, load and save the viminfo file on certain events -- see " |g:tmru_events|. " This is useful if 'viminfo' includes '!' and |g:tmru_file| is " empty and you run multiple instances of vim. let g:tmru_update_viminfo = !g:tmru_single_instance_mode "{{{2 endif if !exists("g:tmru_events") " A dictionary of {EVENT: ACTION = BOOL, ...}, where ACTION is one " of the following: " " load ....... Load the external representation from disk " register ... Register the current buffer " save ....... Save mru list to disk (currently ignored) " " :read: let g:tmru_events = {...} "{{{2 if exists('g:tmruEvents') " backwards compatibility if type(g:tmruEvents) == 1 let g:tmru_events = {} for s:ev in g:tmruEvents let g:tmru_events[s:ev] = {'load': 0, 'register': 1, 'save': 1} endfor unlet s:ev else let g:tmru_events = map(g:tmruEvents, "{'load': 0, 'register': 1, 'save': v:val}") endif unlet g:tmruEvents else let g:tmru_events = { \ 'VimLeave': {'load': 0, 'register': 0, 'save': 1, 'exit': 1}, \ 'FocusGained': {'load': 1, 'register': 0, 'save': !g:tmru_single_instance_mode}, \ 'FocusLost': {'load': 0, 'register': 0, 'save': !g:tmru_single_instance_mode}, \ 'BufWritePost': {'load': 0, 'register': 1, 'save': !g:tmru_single_instance_mode}, \ 'BufReadPost': {'load': 0, 'register': 1, 'save': !g:tmru_single_instance_mode}, \ 'BufWinEnter': {'load': 0, 'register': 1, 'save': !g:tmru_single_instance_mode}, \ 'BufEnter': {'load': 0, 'register': 1, 'save': !g:tmru_single_instance_mode}, \ 'BufDelete': {'load': 0, 'register': 1, 'save': !g:tmru_single_instance_mode} \ } endif endif if !exists("g:tmru_file") if stridx(&viminfo, '!') == -1 " Where to save the file list. The default value is only " effective, if 'viminfo' doesn't contain '!' -- in which case " the 'viminfo' will be used. let g:tmru_file = tlib#persistent#Filename('tmru', 'files', 1) "{{{2 else let g:tmru_file = '' endif endif if !exists("g:tmruSize") " The number of recently edited files that are registered. " The size is smaller if viminfo is used (see |g:tmru_file|). let g:tmruSize = empty(g:tmru_file) ? 50 : 500 "{{{2 endif if !exists("g:tmruExclude") "{{{2 if exists('+shellslash') let s:PS = &shellslash ? '/' : '\\' else let s:PS = "/" endif " Ignore files matching this regexp. " :read: let g:tmruExclude = '/te\?mp/\|vim.\{-}/\(doc\|cache\)/\|__.\{-}__$' "{{{2 let g:tmruExclude = s:PS . '[Tt]e\?mp' . s:PS \ . '\|' . s:PS . '\(vimfiles\|\.vim\)' . s:PS . '\(doc\|cache\)' . s:PS \ . '\|\.tmp$' \ . '\|'. s:PS .'.git'. s:PS .'\(COMMIT_EDITMSG\|git-rebase-todo\)$' \ . '\|'. s:PS .'quickfix$' \ . '\|__.\{-}__$' \ . '\|^fugitive:' \ . '\|/truecrypt\d\+/' \ . '\|' . substitute(escape(&suffixes, '~.*$^'), '\\\@= 1 ? a:1 : 0 " TLogVAR read_data if empty(g:tmru_file) if read_data && exists("g:TMRU") if g:tmru_update_viminfo " TLogVAR read_data, g:tmru_update_viminfo rviminfo endif endif if !exists("g:TMRU") let g:TMRU = '' endif let s:tmru_list = map(split(g:TMRU, '\n'), '[v:val, {}]') else if read_data if exists('s:tmru_mtime') && getftime(g:tmru_file) == s:tmru_mtime let read_data = 0 endif elseif !exists('s:tmru_mtime') || getftime(g:tmru_file) != s:tmru_mtime let read_data = 1 endif if read_data " TLogVAR read_data, g:tmru_file let data = tlib#persistent#Get(g:tmru_file) let s:tmru_mtime = getftime(g:tmru_file) if get(data, 'version', 0) == 0 let s:tmru_list = map(split(get(data, 'tmru', ''), '\n'), '[v:val, {}]') else let s:tmru_list = get(data, 'tmru', []) endif endif endif if read_data let s:last_auto_filename = '' endif return s:tmru_list endf function! s:BuildMenu(initial) "{{{3 if !empty(g:tmruMenu) if !a:initial silent! exec 'aunmenu '. g:tmruMenu endif let tmruobj = TmruObj() let mru = tmruobj.mru if g:tmruMenuSize > 0 && len(mru) > g:tmruMenuSize let mru = mru[0 : g:tmruMenuSize - 1] endif for item in mru let e = item[0] let me = escape(e, '.\ ') exec 'amenu '. g:tmruMenu . me .' :call Edit('. string(e) .')' endfor endif endf function! s:NormalizeFilename(filename) "{{{3 let filename = fnamemodify(a:filename, ':p') if exists('+shellslash') if &shellslash let filename = substitute(filename, '\\', '/', 'g') else let filename = substitute(filename, '/', '\\', 'g') endif endif return filename endf function! s:MruStore(mru, ...) " TLogVAR g:tmru_file let props = a:0 >= 1 ? a:1 : {} let tmru_list = a:mru if get(props, 'exit', 0) let tmru_list = s:MruSort(tmru_list) " echom "DBG tmru_list != s:tmru_list" (tmru_list != s:tmru_list) " echom "DBG tmru_list != s:tmru_list" (string(tmru_list) != string(s:tmru_list)) " echom "DBG tmru_list" string(filter(copy(tmru_list), 'has_key(v:val[1], "sessions")')) endif let tmru_list = tmru_list[0 : g:tmruSize] if tmru_list != s:tmru_list let s:tmru_list = deepcopy(tmru_list) if !get(props, 'exit', 0) call s:BuildMenu(0) endif " TLogVAR g:tmru_file if empty(g:tmru_file) " TLogVAR g:TMRU if g:tmru_update_viminfo let g:TMRU = join(map(s:tmru_list, 'v:val[0]'), "\n") wviminfo endif else call tlib#persistent#Save(g:tmru_file, {'version': 1, 'tmru': s:tmru_list}) let s:tmru_mtime = getftime(g:tmru_file) endif endif endf function! s:MruSort(mru) "{{{3 let s:mru_pos = 0 call map(a:mru, 's:SetPos(v:val)') unlet s:mru_pos " TLogVAR a:mru let mru = sort(a:mru, 's:MruSorter') " TLogVAR mru return mru endf function! s:SetPos(item) "{{{3 let a:item[1].pos = s:mru_pos " TLogVAR a:item let s:mru_pos += 1 return a:item endf function! s:MruSorter(i1, i2) "{{{3 let i11 = a:i1[1] let i21 = a:i2[1] let s1 = get(i11, 'sticky', 0) let s2 = get(i21, 'sticky', 0) if s1 == s2 let p1 = get(i11, 'pos') let p2 = get(i21, 'pos') return p1 == p2 ? 0 : p1 > p2 ? 1 : -1 else return s1 > s2 ? -1 : 1 endif endf let s:last_auto_filename = '' function! s:RegisterFile(filename, event, props) "{{{3 " TLogVAR a:filename, a:event, a:props, &buftype if !empty(a:filename) && get(a:props, 'register', 1) && s:last_auto_filename != a:filename " TLogVAR "Consider", a:filename if getbufvar(a:filename, '&buflisted') && \ getbufvar(a:filename, '&buftype') !~ 'nofile' && \ (g:tmru_check_disk ? \ (filereadable(a:filename) && !isdirectory(a:filename)) : \ fnamemodify(a:filename, ":t") != '') let s:last_auto_filename = a:filename call s:MruRegister(a:filename, a:props) endif endif endf function! s:MruRegister(filename, props) " TLogVAR a:filename let filename = s:NormalizeFilename(a:filename) if g:tmruExclude != '' && filename =~ g:tmruExclude if &verbose | echom "tmru: ignore file" filename | end return endif if exists('b:tmruExclude') && b:tmruExclude return endif let tmruobj = TmruObj(get(a:props, 'load', 0)) let [oldpos, item] = TmruGetItem(tmruobj, filename) let [must_update, mru] = TmruInsert(tmruobj, oldpos, item) if must_update let tmruobj.mru = mru call tmruobj.Save(a:props) endif endf function! TmruGetItem(tmruobj, filename) "{{{3 " TLogVAR a:filename let filenames = a:tmruobj.GetFilenames() let imru = a:tmruobj.FilenameIndex(filenames, a:filename) " TLogVAR imru if imru == -1 let item = [a:filename, {}] else let item = get(a:tmruobj.mru, imru) endif " TLogVAR imru, item return [imru, item] endf function! TmruInsert(tmruobj, oldpos, item) "{{{3 " TLogVAR a:oldpos, a:item " echom "DBG" get(a:item[1], "sticky", 0) let newpos = 0 if !get(a:item[1], 'sticky', 0) for mruitem in a:tmruobj.mru " TLogVAR mruitem if get(mruitem[1], 'sticky', 0) let newpos += 1 elseif mruitem[0] == a:item[0] else break endif endfor endif " TLogVAR newpos if a:oldpos == newpos return [0, a:tmruobj.mru] else let mru = copy(a:tmruobj.mru) if a:oldpos != -1 if mru[a:oldpos] != a:item throw 'TMRU: Inconsistent state' endif call remove(mru, a:oldpos) endif call insert(mru, a:item, newpos) " TLogVAR imru return [mru != a:tmruobj.mru, mru] endif endf augroup tmru autocmd! if has('vim_starting') autocmd VimEnter * call s:BuildMenu(1) else call s:BuildMenu(1) endif for [s:event, s:props] in items(g:tmru_events) exec 'autocmd '. s:event .' * call s:RegisterFile(expand(":p"), '. string(s:event) .', '. string(s:props) .')' endfor unlet! s:event s:props augroup END for s:i in range(1, bufnr('$')) call s:RegisterFile(bufname(s:i), 'vimstarting', {'register': 1}) endfor unlet! s:i " Display the MRU list. command! TRecentlyUsedFiles call tmru#SelectMRU() " Alias for |:TRecentlyUsedFiles|. command! TMRU TRecentlyUsedFiles " Edit the MRU list. command! TRecentlyUsedFilesEdit call tmru#EditMRU() if g:tmru_sessions > 0 " Open files from a previous session (see |g:tmru_sessions|). " This command is only available if g:tmru_sessions > 0. command! -nargs=? TRecentlyUsedFilesSessions call tmru#Session(, TmruObj().mru) autocmd tmru VimLeave * call tmru#Leave() endif let &cpo = s:save_cpo unlet s:save_cpo autoload/tmru.vim [[[1 654 " tmru.vim " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Created: 2011-04-10. " @Last Change: 2014-07-07. " @Revision: 320 if !exists('g:tmru#set_filename_indicators') let g:tmru#set_filename_indicators = 1 "{{{2 endif if !exists('g:tmru#sessions_len') " Remember at most N sessions per file. let g:tmru#sessions_len = 3 "{{{2 endif if !exists('g:tmru#world') "{{{2 " *g:tmru_world* *b:tmru_world* " If the variables b:tmru_world or g:tmru_world exist, they are used " to extend the value of g:tmru#world. let g:tmru#world = { \ 'type': 'm', \ 'scratch': '__TMRU__', \ 'key_handlers': [ \ {'key': 3, 'agent': 'tlib#agent#CopyItems', 'key_name': '', 'submenu': 'Edit', 'help': 'Copy file name(s)'}, \ {'key': 6, 'agent': 'tmru#CheckFilenames', 'key_name': '', 'submenu': 'Edit', 'help': 'Check file name(s)'}, \ {'key': "\", 'agent': 'tmru#RemoveItem', 'key_name': '', 'submenu': 'Edit', 'help': 'Remove file name(s)'}, \ {'key': "\", 'agent': 'tmru#Drop', 'key_name': '', 'help': 'Drop to file name'}, \ {'key': 9, 'agent': 'tlib#agent#ShowInfo', 'key_name': '', 'help': 'Show info'}, \ {'key': 19, 'agent': 'tlib#agent#EditFileInSplit', 'key_name': '', 'help': 'Edit files (split)'}, \ {'key': 22, 'agent': 'tlib#agent#EditFileInVSplit', 'key_name': '', 'help': 'Edit files (vertical split)'}, \ {'key': 20, 'agent': 'tlib#agent#EditFileInTab', 'key_name': '', 'help': 'Edit files (new tab)'}, \ {'key': 23, 'agent': 'tlib#agent#ViewFile', 'key_name': '', 'help': 'View file in window'}, \ ], \ 'allow_suspend': 0, \ 'query': 'Select file', \ } " \ 'filter_format': 'fnamemodify(%s, ":t")', if !empty(g:tmru_file) if g:tmru_sessions > 0 let g:tmru#world.key_handlers += [ \ {'key': 12, 'agent': 'tmru#PreviousSession', 'key_name': '', 'submenu': 'Sessions', 'help': 'Open files from a session'}, \ {'key': '<2-12>', 'agent': 'tmru#OpenNamedSession', 'key_name': '', 'submenu': 'Sessions', 'help': 'Open a named session'}, \ {'key': 28, 'agent': 'tmru#SelectNamedSession', 'key_name': '', 'submenu': 'Sessions', 'help': '(Un-)Select a named session'}, \ {'key': 29, 'agent': 'tmru#AddNamedSession', 'key_name': '', 'submenu': 'Sessions', 'help': 'Add files to a session'}, \ {'key': 31, 'agent': 'tmru#RemoveNamedSession', 'key_name': '', 'submenu': 'Sessions', 'help': 'Remove files from a session'}, \ {'key': 5, 'agent': 'tmru#EditNamedSessions', 'key_name': '', 'submenu': 'Sessions', 'help': 'Edit named sessions'}, \ ] endif call add(g:tmru#world.key_handlers, \ {'key': 16, 'agent': 'tmru#TogglePersistent', 'key_name': '', 'submenu': 'Sticky', 'help': 'Toggle a file''s persistent mark'}) call add(g:tmru#world.key_handlers, \ {'key': 21, 'agent': 'tmru#UnsetPersistent', 'key_name': '', 'submenu': 'Sticky', 'help': 'Unset a file''s persistent mark'}) endif if exists('g:tmru_world') let g:tmru#world = extend(g:tmru#world, g:tmru_world) endif endif if !exists('g:tmru_select_filter') " If non-empty, an expression to |filter()| the list of files. " Can also be buffer-local. let g:tmru_select_filter = '' "{{{2 endif if !exists('g:tmru#drop') " If true, use |:drop| to edit loaded buffers (only available with GUI). let g:tmru#drop = has('gui') "{{{2 endif if !exists('g:tmru#auto_remove_unreadable') " If true, automatically remove unreadable files from the mru list, " when trying to edit them. let g:tmru#auto_remove_unreadable = 1 "{{{2 endif function! tmru#SelectMRU() " TLogDBG "SelectMRU#1" let tmruobj = TmruObj() if !empty(tmruobj.mru) " TLogDBG "SelectMRU#2" let w0 = exists('b:tmru_world') ? extend(copy(g:tmru#world), b:tmru_world) : g:tmru#world let world = tlib#World#New(w0) call world.Set_display_format('filename') " TLogDBG "SelectMRU#3" call tmruobj.SetBase(world) let select_filter = tlib#var#Get('tmru_select_filter', 'bg') if !empty(select_filter) let world.base = filter(world.base, select_filter) endif let stickyn = len(filter(copy(tmruobj.mru), 'get(v:val[1], "sticky", 0)')) if stickyn < len(tmruobj.mru) let stickyn += 1 endif let world.initial_index = stickyn " TLogDBG "SelectMRU#4" let bs = tlib#input#ListW(world) " TLogDBG "SelectMRU#5" " TLogVAR bs call tmru#EditFiles(bs, tmruobj) endif return 0 endf function! tmru#EditMRU() let tmruobj = TmruObj() let filenames0 = tmruobj.GetFilenames() let properties = s:AList2Dict(tmruobj.mru) let filenames1 = tlib#input#EditList('Edit MRU', filenames0) if filenames0 != filenames1 let tmruobj.mru = map(filenames1, '[v:val, get(properties, v:val, {})]') call tmruobj.Save() endif endf function! s:AList2Dict(mru) let props = {} for item in a:mru let props[item[0]] = item[1] endfor return props endf function! tmru#EditFiles(filenames, ...) "{{{3 if !empty(a:filenames) let tmruobj = a:0 >= 1 ? a:1 : TmruObj() let remove_files = [] for bf in a:filenames " TLogVAR bf if !s:Edit(bf) call add(remove_files, bf) endif endfor if g:tmru#auto_remove_unreadable && !empty(remove_files) return !s:RemoveItems(remove_files, tmruobj) endif endif return 1 endf " Return 0 if the file isn't readable/doesn't exist. " Otherwise return 1. function! s:Edit(filename) "{{{3 let filename = fnamemodify(a:filename, ':p') if filename == expand('%:p') return 1 else let bn = bufnr(filename) " TLogVAR bn if bn != -1 && buflisted(bn) if g:tmru#drop exec 'drop' fnameescape(filename) else exec 'buffer' bn endif return 1 elseif filereadable(filename) try let file = tlib#arg#Ex(filename) " TLogVAR file exec 'edit' file catch /E325/ " swap file exists, let the user handle it catch echohl error echom v:exception echohl NONE endtry return 1 else echom "TMRU: File not readable: " . filename if filename != a:filename echom "TMRU: original filename: " . a:filename endif endif endif return 0 endf function! s:RemoveItems(filenames, ...) "{{{3 let modified_list = 0 if !empty(a:filenames) let tmruobj = a:0 >= 1 ? a:1 : TmruObj() call tmruobj.Update() let filenames = tmruobj.GetFilenames() for bf in a:filenames let bi = tmruobj.FilenameIndex(filenames, bf) " TLogVAR bi if bi != -1 call remove(tmruobj.mru, bi) let modified_list = 1 endif endfor if modified_list call tmruobj.Save() endif endif return modified_list endf function! s:IsNamedSession(session) "{{{3 return a:session =~ '\D' endf function! tmru#Session(session_no, mru) "{{{3 " TLogVAR a:session_no if empty(a:session_no) let session = 1 let opt = 'sessions' elseif s:IsNamedSession(a:session_no) let session = a:session_no let opt = 'sessionnames' else let session = str2nr(a:session_no) let opt = 'sessions' endif " TLogVAR a:session_no, session, opt if !empty(session) let filenames = [] for [filename, props] in a:mru " TLogVAR filename, props if index(get(props, opt, []), session) != -1 call add(filenames, filename) endif endfor " TLogVAR filenames call tmru#EditFiles(filenames) endif endf function! tmru#Leave() "{{{3 let tmruobj = TmruObj() let filenames = tmruobj.GetFilenames() let mru = deepcopy(tmruobj.mru) let modified = [] for bufnr in range(1, bufnr('$')) if buflisted(bufnr) let bufname = fnamemodify(bufname(bufnr), ':p') let [idx, item] = tmruobj.Find(bufname) if idx != -1 let item1 = s:SetSessions(item, 1) let mru[idx] = item1 " TLogVAR item1 call add(modified, idx) endif endif endfor " TLogVAR modified for idx in range(len(filenames)) if index(modified, idx) == -1 let mru[idx] = s:SetSessions(mru[idx], 0) endif endfor let tmruobj.mru = mru call tmruobj.Save({'exit': 1}) endf function! s:SetSessions(item, buflisted) "{{{3 let [filename, props] = a:item let sessions = get(props, 'sessions', []) if !empty(sessions) let sessions = map(sessions, 'v:val + 1') let sessions = filter(sessions, 'v:val <= g:tmru_sessions') endif if a:buflisted let sessions = insert(sessions, 1) endif if g:tmru#sessions_len > 0 let sessions = sessions[0 : g:tmru#sessions_len - 1] endif if !empty(sessions) let a:item[1].sessions = sessions elseif has_key(props, 'sessions') call remove(a:item[1], 'sessions') endif return a:item endf function! tmru#DisplayUnreadableFiles(mru) "{{{3 " TLogVAR a:mru for file in a:mru if !filereadable(file) echohl WarningMsg " echom "DBG TMRU: unreadable file:" file echohl NONE endif endfor endf " Validate list of filenames in mru list. " This checks that files are readable and removes any (canonicalized) " duplicates. function! tmru#CheckFilenames(world, selected) "{{{3 let tmruobj = TmruObj() let filenames = tmruobj.GetFilenames() let idx = len(tmruobj.mru) - 1 let uniqdict = {} " used to remove duplicates let unreadable = 0 let dupes = 0 let normalized = 0 while idx > 0 let file_p = fnamemodify(filenames[idx], ':p') let file = substitute(substitute(file_p, '\\\+', '\', 'g'), '/\+', '/', 'g') if !filereadable(file) " TLogVAR file call remove(tmruobj.mru, idx) let unreadable += 1 elseif get(uniqdict, file) " file is a dupe let dupes += 1 call remove(tmruobj.mru, idx) else " file is OK, add it to dictionary for dupe checking let uniqdict[file] = 1 if file_p != file let normalized += 1 let tmruobj.mru[idx][0] = file endif endif let idx -= 1 endwh if unreadable > 0 || dupes > 0 || normalized > 0 call tmruobj.Save() echom "TMRU: Removed" unreadable "unreadable and" dupes "duplicate" \ "files from mru list, and normalized" normalized "entries." endif call tmruobj.SetBase(a:world) let a:world.state = 'reset' return a:world endf function! tmru#RemoveItem(world, selected) "{{{3 let tmruobj = TmruObj() let filenames = tmruobj.GetFilenames() " TLogVAR a:selected let idx = -1 for filename in a:selected let fidx = tmruobj.FilenameIndex(filenames, filename) if idx < 0 let idx = fidx endif " TLogVAR filename, fidx if fidx >= 0 call remove(tmruobj.mru, fidx) endif endfor call tmruobj.Save() call a:world.ResetSelected() let a:world.base = tmruobj.GetFilenames() if idx > len(tmruobj.mru) let a:world.idx = len(tmruobj.mru) elseif idx >= 0 let a:world.idx = idx endif " TLogVAR a:world.idx let a:world.state = 'display' return a:world endf function! tmru#Drop(world, selected) "{{{3 let filename = a:selected[0] if bufnr(filename) != -1 exec 'drop' fnameescape(filename) else call tmru#EditFiles([filename]) endif let a:world.state = 'exit' return a:world endf function! tmru#UnsetPersistent(world, selected) "{{{3 let tmruobj = TmruObj() let mru = tmruobj.mru let filenames = tmruobj.GetFilenames() for filename in a:selected let [oldpos, item] = TmruGetItem(tmruobj, filename) let item[1]['sticky'] = 0 let [must_update, tmruobj.mru] = TmruInsert(tmruobj, oldpos, item) endfor call tmruobj.Save() call tmruobj.SetBase(a:world) let a:world.state = 'reset' return a:world endf function! tmru#TogglePersistent(world, selected) "{{{3 let tmruobj = TmruObj() let filenames = tmruobj.GetFilenames() let msgs = [] for filename in a:selected let [oldpos, item] = TmruGetItem(tmruobj, filename) let item[1]['sticky'] = !get(item[1], 'sticky', 0) call add(msgs, printf('Mark %ssticky: %s', item[1]['sticky'] ? '' : 'not ', filename)) let [must_update, tmruobj.mru] = TmruInsert(tmruobj, oldpos, item) let fidx = tmruobj.FilenameIndex(filenames, filename) " TLogVAR filename, fidx endfor if !empty(msgs) echom join(msgs, "\n") echohl MoreMsg call input("Press ENTER to continue") echohl NONE endif call tmruobj.Save() call tmruobj.SetBase(a:world) let a:world.state = 'reset' return a:world endf function! tmru#PreviousSession(world, selected) "{{{3 let sessions_done = [] let tmruobj = TmruObj() let filenames = tmruobj.GetFilenames() for filename in a:selected let fidx = tmruobj.FilenameIndex(filenames, filename) if fidx >= 0 let props = tmruobj.mru[fidx][1] if has_key(props, 'sessions') || has_key(props, 'sessionnames') let sessions = get(props, 'sessions', []) + get(props, 'sessionnames', []) " TLogVAR sessions let sessions = filter(sessions, 'index(sessions_done, v:val) == -1') " TLogVAR sessions if empty(sessions) let session = 0 elseif len(sessions) == 1 let session = sessions[0] else let session = tlib#input#List('s', 'Select session:', sessions) endif " TLogVAR session if !empty(session) if empty(sessions_done) call a:world.CloseScratch() endif call add(sessions_done, session) exec 'TRecentlyUsedFilesSessions' session endif endif endif endfor if empty(sessions_done) let a:world.state = 'redisplay' else let a:world.state = 'exit' endif return a:world endf function! tmru#SelectNamedSession(world, selected) "{{{3 let sessionnames = tmru#SessionNames() let session = tlib#input#List('s', 'Select session:', sessionnames) if !empty(session) let tmruobj = TmruObj() let filenames = [] for item in tmruobj.mru let names = get(get(item, 1, {}), 'sessionnames', []) if index(names, session) != -1 call add(filenames, item[0]) endif endfor " TLogVAR filenames if !empty(filenames) call a:world.SelectItemsByNames('toggle', filenames) endif let a:world.state = 'display' else let a:world.state = 'redisplay' endif return a:world endf function! tmru#OpenNamedSession(world, selected) "{{{3 let sessionnames = tmru#SessionNames() let session = tlib#input#List('s', 'Select session:', sessionnames) if !empty(session) call a:world.CloseScratch() exec 'TRecentlyUsedFilesSessions' session let a:world.state = 'exit' else let a:world.state = 'redisplay' endif return a:world endf function! tmru#SessionNames(...) "{{{3 if a:0 == 3 let [ArgLead, CmdLine, CursorPos] = a:000 else let [ArgLead, CmdLine, CursorPos] = ['', '', 0] endif let tmruobj = TmruObj() let filenames = exists('s:sessionnames_filenames') ? s:sessionnames_filenames : [] let mru = tmruobj.mru if empty(mru) let sessionnames = [] else if empty(filenames) let sessionnames = map(range(len(mru)), 'get(get(mru[v:val], 1, {}), "sessionnames", [])') else let items = filter(copy(mru), 'index(filenames, v:val[0]) != -1') let sessionnames = map(items, 'get(get(v:val, 1, {}), "sessionnames", [])') endif let sessionnames = tlib#list#Flatten(sessionnames) let sessionnames = tlib#list#Uniq(sessionnames) if !empty(ArgLead) let sessionnames = filter(sessionnames, 'stridx(v:val, ArgLead) != -1') endif endif return sessionnames endf function! tmru#AddNamedSession(world, selected) "{{{3 let sessionname = input('Add session name(s): ', '', 'customlist,tmru#SessionNames') if !empty(sessionname) let tmruobj = TmruObj() let add_sessionnames = split(sessionname, '\s*,\s*') let tmruobj.mru = map(tmruobj.mru, 's:AddOrRemoveNamedSession(v:val, a:selected, add_sessionnames, [])') call tmruobj.Save() call tmruobj.SetBase(a:world) let a:world.state = 'reset' else let a:world.state = 'redisplay' endif return a:world endf function! tmru#RemoveNamedSession(world, selected) "{{{3 let s:sessionnames_filenames = a:selected try let sessionname = input('Remove session name(s): ', '', 'customlist,tmru#SessionNames') finally unlet s:sessionnames_filenames endtry if !empty(sessionname) let tmruobj = TmruObj() let remove_sessionnames = split(sessionname, '\s*,\s*') let tmruobj.mru = map(tmruobj.mru, 's:AddOrRemoveNamedSession(v:val, a:selected, [], remove_sessionnames)') call tmruobj.Save() call tmruobj.SetBase(a:world) let a:world.state = 'reset' else let a:world.state = 'redisplay' endif return a:world endf function! s:AddOrRemoveNamedSession(item, filenames, add_sessionnames, remove_sessionnames) "{{{3 if index(a:filenames, a:item[0]) != -1 let props = get(a:item, 1, {}) let sessionnames0 = get(props, 'sessionnames', []) let sessionnames1 = tlib#list#Uniq(sessionnames0 + a:add_sessionnames) for name in a:remove_sessionnames let idx = index(sessionnames1, name) if idx != -1 call remove(sessionnames1, idx) endif endfor if empty(sessionnames1) if has_key(props, 'sessionnames') call remove(props, 'sessionnames') let a:item[1] = props endif elseif sessionnames1 != sessionnames0 let props.sessionnames = sessionnames1 let a:item[1] = props endif endif return a:item endf function! tmru#EditNamedSessions(world, selected) "{{{3 let tmruobj = TmruObj() let tmruobj.mru = map(tmruobj.mru, 's:EditNamedSessions(v:val, a:selected)') call tmruobj.Save() call tmruobj.SetBase(a:world) let a:world.state = 'reset' return a:world endfun function! s:EditNamedSessions(item, filenames) "{{{3 if index(a:filenames, a:item[0]) != -1 let filename = fnamemodify(a:item[0], ':t') let props = get(a:item, 1, {}) let sessionnames0 = get(props, 'sessionnames', []) let sessionnames = join(sessionnames0, ', ') let prompt = printf("%s session names: ", filename) let sessionnames = input(prompt, sessionnames) if empty(sessionnames) call remove(props, 'sessionnames') else let props.sessionnames = split(sessionnames, '\s*,\s*') endif let a:item[1] = props endif return a:item endf function! tmru#SetFilenameIndicators(world, mru) "{{{3 if g:tmru#set_filename_indicators let a:world.filename_indicators = {} let idx = 0 for item in a:mru let [filename, props] = item let indicators = [] if get(props, 'sticky', 0) call add(indicators, "s") endif let sessions = get(props, 'sessions', []) + get(props, 'sessionnames', []) if !empty(sessions) call add(indicators, '-'. join(sessions, g:tmru_sessions < 10 ? '' : '-')) endif if !empty(indicators) let fname = g:tmru#display_relative_filename ? a:world.base[idx] : filename " TLogVAR fname, indicators let a:world.filename_indicators[fname] = join(indicators, '') endif let idx += 1 endfor endif endf