mru.vim 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. " File: mru.vim
  2. " Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
  3. " Version: 3.10.2
  4. " Last Modified: August 14, 2021
  5. " Copyright: Copyright (C) 2003-2021 Yegappan Lakshmanan
  6. " License: Permission is hereby granted to use and distribute this code,
  7. " with or without modifications, provided that this copyright
  8. " notice is copied with it. Like anything else that's free,
  9. " mru.vim is provided *as is* and comes with no warranty of any
  10. " kind, either expressed or implied. In no event will the copyright
  11. " holder be liable for any damages resulting from the use of this
  12. " software.
  13. "
  14. " ****************** Do not modify after this line ************************
  15. if exists('loaded_mru')
  16. finish
  17. endif
  18. let loaded_mru=1
  19. if v:version < 700
  20. finish
  21. endif
  22. " Line continuation used here
  23. let s:cpo_save = &cpoptions
  24. set cpoptions&vim
  25. " MRU configuration variables {{{1
  26. " Maximum number of entries allowed in the MRU list
  27. if !exists('MRU_Max_Entries')
  28. let MRU_Max_Entries = 100
  29. endif
  30. " Files to exclude from the MRU list
  31. if !exists('MRU_Exclude_Files')
  32. let MRU_Exclude_Files = ''
  33. endif
  34. " Files to include in the MRU list
  35. if !exists('MRU_Include_Files')
  36. let MRU_Include_Files = ''
  37. endif
  38. " Height of the MRU window
  39. " Default height is 8
  40. if !exists('MRU_Window_Height')
  41. let MRU_Window_Height = 8
  42. endif
  43. if !exists('MRU_Use_Current_Window')
  44. let MRU_Use_Current_Window = 0
  45. endif
  46. if !exists('MRU_Auto_Close')
  47. let MRU_Auto_Close = 1
  48. endif
  49. if !exists('g:MRU_File')
  50. if has('unix') || has('macunix')
  51. let s:MRU_File = $HOME . '/.vim_mru_files'
  52. else
  53. let s:MRU_File = $VIM . '/_vim_mru_files'
  54. if has('win32')
  55. " MS-Windows
  56. if !empty($USERPROFILE)
  57. let s:MRU_File = $USERPROFILE . '\_vim_mru_files'
  58. endif
  59. endif
  60. endif
  61. else
  62. let s:MRU_File = expand(g:MRU_File)
  63. endif
  64. " Option for enabling or disabling the MRU menu
  65. if !exists('MRU_Add_Menu')
  66. let MRU_Add_Menu = 1
  67. endif
  68. " Maximum number of file names to show in the MRU menu. If too many files are
  69. " listed in the menu, then Vim becomes slow when updating the menu. So set
  70. " this to a low value.
  71. if !exists('MRU_Max_Menu_Entries')
  72. let MRU_Max_Menu_Entries = 10
  73. endif
  74. " Maximum number of file names to show in a MRU sub-menu. If the MRU list
  75. " contains more file names than this setting, then the MRU menu is split into
  76. " one or more sub-menus.
  77. if !exists('MRU_Max_Submenu_Entries')
  78. let MRU_Max_Submenu_Entries = 10
  79. endif
  80. " When only a single matching filename is found in the MRU list, the following
  81. " option controls whether the file name is displayed in the MRU window or the
  82. " file is directly opened. When this variable is set to 0 and a single
  83. " matching file name is found, then the file is directly opened.
  84. if !exists('MRU_Window_Open_Always')
  85. let MRU_Window_Open_Always = 0
  86. endif
  87. " When opening a file from the MRU list, the file is opened in the current
  88. " tab. If the selected file has to be opened in a tab always, then set the
  89. " following variable to 1. If the file is already opened in a tab, then the
  90. " cursor will be moved to that tab.
  91. if !exists('MRU_Open_File_Use_Tabs')
  92. let MRU_Open_File_Use_Tabs = 0
  93. endif
  94. " Controls whether fuzzy matching is used for matching a user supplied pattern
  95. " against the file names in the MRU list.
  96. if !exists('MRU_FuzzyMatch')
  97. if exists('*matchfuzzy')
  98. " Fuzzy matching is supported only when matchfuzzy() function is present
  99. let MRU_FuzzyMatch = 1
  100. else
  101. let MRU_FuzzyMatch = 0
  102. endif
  103. endif
  104. " Controls whether the alternate file (:help alternate-file) is set when the
  105. " plugin is loaded to the first file in the MRU list. Default is to set the
  106. " alternate file.
  107. if !exists('MRU_Set_Alternate_File')
  108. let MRU_Set_Alternate_File = 0
  109. endif
  110. " Format of the file names displayed in the MRU window.
  111. " The default is to display the filename followed by the complete path to the
  112. " file in parenthesis. This variable controls the expressions used to format
  113. " and parse the path. This can be changed to display the filenames in a
  114. " different format. The 'formatter' specifies how to split/format the filename
  115. " and 'parser' specifies how to read the filename back; 'syntax' matches the
  116. " part to be highlighted.
  117. if !exists('MRU_Filename_Format')
  118. let MRU_Filename_Format = {
  119. \ 'formatter': 'fnamemodify(v:val, ":t") . " (" . v:val . ")"',
  120. \ 'parser': '(\zs.*\ze)',
  121. \ 'syntax': '^.\{-}\ze('
  122. \}
  123. endif
  124. let s:MRU_buf_name = '-RecentFiles-'
  125. " Control to temporarily lock the MRU list. Used to prevent files from
  126. " getting added to the MRU list when the ':vimgrep' command is executed.
  127. let s:mru_list_locked = 0
  128. " MRU_LoadList {{{1
  129. " Loads the latest list of file names from the MRU file
  130. func! s:MRU_LoadList() abort
  131. " If the MRU file is present, then load the list of filenames. Otherwise
  132. " start with an empty list.
  133. if filereadable(s:MRU_File)
  134. let s:MRU_files = readfile(s:MRU_File)
  135. if empty(s:MRU_files)
  136. " empty MRU file
  137. let s:MRU_files = []
  138. elseif s:MRU_files[0] =~# '^\s*" Most recently edited files in Vim'
  139. " Generated by the previous version of the MRU plugin.
  140. " Discard the list.
  141. let s:MRU_files = []
  142. elseif s:MRU_files[0] =~# '^#'
  143. " Remove the comment line
  144. call remove(s:MRU_files, 0)
  145. else
  146. " Unsupported format
  147. let s:MRU_files = []
  148. endif
  149. else
  150. let s:MRU_files = []
  151. endif
  152. " Refresh the MRU menu with the latest list of filenames
  153. call s:MRU_Refresh_Menu()
  154. endfunc
  155. " MRU_SaveList {{{1
  156. " Saves the MRU file names to the MRU file
  157. func! s:MRU_SaveList() abort
  158. let l = []
  159. call add(l, '# Most recently edited files in Vim (version 3.0)')
  160. call extend(l, s:MRU_files)
  161. call writefile(l, s:MRU_File)
  162. endfunc
  163. " MRU_AddFile {{{1
  164. " Adds a file to the MRU file list
  165. " acmd_bufnr - Buffer number of the file to add
  166. func! s:MRU_AddFile(acmd_bufnr) abort
  167. if s:mru_list_locked
  168. " MRU list is currently locked
  169. return
  170. endif
  171. " Get the full path to the filename
  172. let fname = fnamemodify(bufname(a:acmd_bufnr + 0), ':p')
  173. if empty(fname)
  174. return
  175. endif
  176. " Skip temporary buffers with buftype set. The buftype is set for buffers
  177. " used by plugins.
  178. if !empty(&buftype)
  179. return
  180. endif
  181. if !empty(g:MRU_Include_Files)
  182. " If MRU_Include_Files is set, include only files matching the
  183. " specified pattern
  184. if fname !~# g:MRU_Include_Files
  185. return
  186. endif
  187. endif
  188. if !empty(g:MRU_Exclude_Files)
  189. " Do not add files matching the pattern specified in the
  190. " MRU_Exclude_Files to the MRU list
  191. if fname =~# g:MRU_Exclude_Files
  192. return
  193. endif
  194. endif
  195. " If the filename is not already present in the MRU list and is not
  196. " readable then ignore it
  197. let idx = index(s:MRU_files, fname)
  198. if idx == -1
  199. if !filereadable(fname)
  200. " File is not readable and is not in the MRU list
  201. return
  202. endif
  203. endif
  204. " Load the latest MRU file list
  205. call s:MRU_LoadList()
  206. " Remove the new file name from the existing MRU list (if already present)
  207. call filter(s:MRU_files, 'v:val !=# fname')
  208. " Add the new file list to the beginning of the updated old file list
  209. call insert(s:MRU_files, fname, 0)
  210. " Trim the list
  211. if len(s:MRU_files) > g:MRU_Max_Entries
  212. call remove(s:MRU_files, g:MRU_Max_Entries, -1)
  213. endif
  214. " Save the updated MRU list
  215. call s:MRU_SaveList()
  216. " Refresh the MRU menu
  217. call s:MRU_Refresh_Menu()
  218. " If the MRU window is open, update the displayed MRU list
  219. let bname = s:MRU_buf_name
  220. let winnum = bufwinnr(bname)
  221. if winnum != -1
  222. let cur_winnr = winnr()
  223. call s:MRU_Open_Window('', '', 0)
  224. if winnr() != cur_winnr
  225. exe cur_winnr . 'wincmd w'
  226. endif
  227. endif
  228. endfunc
  229. " MRU_escape_filename {{{1
  230. " Escape special characters in a filename. Special characters in file names
  231. " that should be escaped (for security reasons)
  232. let s:esc_filename_chars = ' *?[{`$%#"|!<>();&' . "'\t\n"
  233. func! s:MRU_escape_filename(fname) abort
  234. if exists('*fnameescape')
  235. return fnameescape(a:fname)
  236. else
  237. return escape(a:fname, s:esc_filename_chars)
  238. endif
  239. endfunc
  240. " MRU_Edit_File {{{1
  241. " Edit the specified file
  242. " filename - Name of the file to edit
  243. " sanitized - Specifies whether the filename is already escaped for special
  244. " characters or not.
  245. " splitdir - command modifier for a split (topleft, belowright, etc.)
  246. " Used by the :MRU command and the "Recent Files" menu item
  247. func! s:MRU_Edit_File(filename, sanitized, splitdir) abort
  248. if !a:sanitized
  249. let esc_fname = s:MRU_escape_filename(a:filename)
  250. else
  251. let esc_fname = a:filename
  252. endif
  253. " If the user wants to always open the file in a tab, then open the file
  254. " in a tab. If it is already opened in a tab, then the cursor will be
  255. " moved to that tab.
  256. if g:MRU_Open_File_Use_Tabs
  257. call s:MRU_Open_File_In_Tab(a:filename, esc_fname)
  258. return
  259. endif
  260. " If the file is already open in one of the windows, jump to it
  261. let winnum = bufwinnr('^' . a:filename . '$')
  262. if winnum != -1
  263. if winnum != winnr()
  264. exe winnum . 'wincmd w'
  265. endif
  266. else
  267. if !empty(a:splitdir) || (!&hidden && (&modified || !empty(&buftype)
  268. \ || &previewwindow))
  269. " If a split command modifier is specified, always open the file
  270. " in a new window.
  271. " Or if the current buffer has unsaved changes or is a special buffer or
  272. " is the preview window. The 'hidden' option is also not set. So open
  273. " the file in a new window.
  274. if bufexists(esc_fname)
  275. exe a:splitdir . ' sbuffer ' . esc_fname
  276. else
  277. exe a:splitdir . ' split ' . esc_fname
  278. endif
  279. else
  280. " The current file can be replaced with the selected file.
  281. if bufexists(esc_fname)
  282. exe 'buffer ' . esc_fname
  283. else
  284. exe 'edit ' . esc_fname
  285. endif
  286. endif
  287. " Make the buffer a listed buffer (in case it was deleted before)
  288. setlocal buflisted
  289. endif
  290. endfunc
  291. " MRU_Open_File_In_Tab
  292. " Open a file in a tab. If the file is already opened in a tab, jump to the
  293. " tab. Otherwise, create a new tab and open the file.
  294. " fname : Name of the file to open
  295. " esc_fname : File name with special characters escaped
  296. func! s:MRU_Open_File_In_Tab(fname, esc_fname) abort
  297. " If the selected file is already open in the current tab or in
  298. " another tab, jump to it. Otherwise open it in a new tab
  299. if bufwinnr('^' . a:fname . '$') == -1
  300. let tabnum = -1
  301. let i = 1
  302. let bnum = bufnr('^' . a:fname . '$')
  303. while i <= tabpagenr('$')
  304. if index(tabpagebuflist(i), bnum) != -1
  305. let tabnum = i
  306. break
  307. endif
  308. let i += 1
  309. endwhile
  310. if tabnum != -1
  311. " Goto the tab containing the file
  312. exe 'tabnext ' . i
  313. else
  314. if (winnr('$') == 1) && empty(@%) && !&modified
  315. " Reuse the current tab if it contains a single new unmodified
  316. " file.
  317. if bufexists(a:esc_fname)
  318. exe 'buffer ' . a:esc_fname
  319. else
  320. exe 'edit ' . a:esc_fname
  321. endif
  322. else
  323. " Open a new tab as the last tab page
  324. if v:version >= 800
  325. if bufexists(a:esc_fname)
  326. exe '$tab sbuffer ' . a:esc_fname
  327. else
  328. exe '$tabnew ' . a:esc_fname
  329. endif
  330. else
  331. if bufexists(a:esc_fname)
  332. exe '99999tab sbuffer ' . a:esc_fname
  333. else
  334. exe '99999tabnew ' . a:esc_fname
  335. endif
  336. endif
  337. endif
  338. endif
  339. endif
  340. " Jump to the window containing the file
  341. let winnum = bufwinnr('^' . a:fname . '$')
  342. if winnum != winnr()
  343. exe winnum . 'wincmd w'
  344. endif
  345. endfunc
  346. " MRU_Window_Edit_File {{{1
  347. " fname : Name of the file to edit. May specify single or multiple
  348. " files.
  349. " edit_type : Specifies how to edit the file. Can be one of 'edit' or 'view'.
  350. " 'view' - Open the file as a read-only file
  351. " 'edit' - Edit the file as a regular file
  352. " multi : Specifies whether a single file or multiple files need to be
  353. " opened.
  354. " open_type : Specifies where to open the file.
  355. " useopen - If the file is already present in a window, then
  356. " jump to that window. Otherwise, open the file in
  357. " the previous window.
  358. " newwin_horiz - Open the file in a new horizontal window.
  359. " newwin_vert - Open the file in a new vertical window.
  360. " newtab - Open the file in a new tab. If the file is already
  361. " opened in a tab, then jump to that tab.
  362. " preview - Open the file in the preview window
  363. func! s:MRU_Window_Edit_File(fname, multi, edit_type, open_type) abort
  364. let esc_fname = s:MRU_escape_filename(a:fname)
  365. if a:open_type ==# 'newwin_horiz'
  366. " Edit the file in a new horizontally split window below the previous
  367. " window
  368. wincmd p
  369. if bufexists(esc_fname)
  370. exe 'belowright sbuffer ' . esc_fname
  371. else
  372. exe 'belowright new ' . esc_fname
  373. endif
  374. elseif a:open_type ==# 'newwin_vert'
  375. " Edit the file in a new vertically split window right of the previous
  376. " window
  377. wincmd p
  378. if bufexists(esc_fname)
  379. exe 'vertical belowright sbuffer ' . esc_fname
  380. else
  381. exe 'belowright vnew ' . esc_fname
  382. endif
  383. elseif a:open_type ==# 'newtab' || g:MRU_Open_File_Use_Tabs
  384. call s:MRU_Open_File_In_Tab(a:fname, esc_fname)
  385. elseif a:open_type ==# 'preview'
  386. " Edit the file in the preview window
  387. exe 'topleft pedit ' . esc_fname
  388. else
  389. " If the selected file is already open in one of the windows,
  390. " jump to it
  391. let winnum = bufwinnr('^' . a:fname . '$')
  392. if winnum != -1 && g:MRU_Use_Current_Window == 0
  393. exe winnum . 'wincmd w'
  394. else
  395. if g:MRU_Auto_Close == 1 && g:MRU_Use_Current_Window == 0
  396. " Jump to the window from which the MRU window was opened
  397. if exists('s:MRU_last_buffer')
  398. let last_winnr = bufwinnr(s:MRU_last_buffer)
  399. if last_winnr != -1 && last_winnr != winnr()
  400. exe last_winnr . 'wincmd w'
  401. endif
  402. endif
  403. else
  404. if g:MRU_Use_Current_Window == 0
  405. " Goto the previous window
  406. " If MRU_Use_Current_Window is set to one, then the
  407. " current window is used to open the file
  408. wincmd p
  409. endif
  410. endif
  411. let split_window = 0
  412. if (!&hidden && (&modified || &previewwindow)) || a:multi
  413. " Current buffer has unsaved changes or is the preview window
  414. " or the user is opening multiple files
  415. " So open the file in a new window
  416. let split_window = 1
  417. endif
  418. if !empty(&buftype)
  419. " Current buffer is a special buffer (maybe used by a plugin)
  420. if g:MRU_Use_Current_Window == 0 ||
  421. \ bufnr('%') != bufnr(s:MRU_buf_name)
  422. let split_window = 1
  423. endif
  424. endif
  425. " Edit the file
  426. if split_window
  427. " Current buffer has unsaved changes or is a special buffer or
  428. " is the preview window. So open the file in a new window
  429. if a:edit_type ==# 'edit'
  430. if bufexists(esc_fname)
  431. exe 'sbuffer ' . esc_fname
  432. else
  433. exe 'split ' . esc_fname
  434. endif
  435. else
  436. exe 'sview ' . esc_fname
  437. endif
  438. else
  439. let mod = ''
  440. if g:MRU_Use_Current_Window
  441. let mod = 'keepalt '
  442. endif
  443. if a:edit_type ==# 'edit'
  444. if bufexists(esc_fname)
  445. exe mod . 'buffer ' . esc_fname
  446. else
  447. exe mod . 'edit ' . esc_fname
  448. endif
  449. else
  450. exe mod . 'view ' . esc_fname
  451. endif
  452. endif
  453. endif
  454. endif
  455. " Make the buffer a listed buffer (in case it was deleted before)
  456. setlocal buflisted
  457. endfunc
  458. " MRU_Select_File_Cmd {{{1
  459. " Open a file selected from the MRU window
  460. "
  461. " 'opt' has two values separated by comma. The first value specifies how to
  462. " edit the file and can be either 'edit' or 'view'. The second value
  463. " specifies where to open the file. It can take one of the following values:
  464. " 'useopen' to open file in the previous window
  465. " 'newwin_horiz' to open the file in a new horizontal split window
  466. " 'newwin_vert' to open the file in a new vertical split window.
  467. " 'newtab' to open the file in a new tab.
  468. " If multiple file names are selected using visual mode, then open multiple
  469. " files (either in split windows or tabs)
  470. func! s:MRU_Select_File_Cmd(opt) range abort
  471. let [edit_type, open_type] = split(a:opt, ',')
  472. let fnames = getline(a:firstline, a:lastline)
  473. if g:MRU_Auto_Close == 1 && g:MRU_Use_Current_Window == 0
  474. " Automatically close the window if the file window is
  475. " not used to display the MRU list.
  476. silent! close
  477. endif
  478. let multi = 0
  479. for f in fnames
  480. if empty(f)
  481. continue
  482. endif
  483. " The text in the MRU window contains the filename in parenthesis
  484. let file = matchstr(f, g:MRU_Filename_Format.parser)
  485. call s:MRU_Window_Edit_File(file, multi, edit_type, open_type)
  486. if a:firstline != a:lastline
  487. " Opening multiple files
  488. let multi = 1
  489. endif
  490. endfor
  491. endfunc
  492. " MRU_Warn_Msg {{{1
  493. " Display a warning message
  494. func! s:MRU_Warn_Msg(msg) abort
  495. echohl WarningMsg
  496. echo a:msg
  497. echohl None
  498. endfunc
  499. " MRU_Open_Window {{{1
  500. " Display the Most Recently Used file list in a temporary window.
  501. " If the 'pat' argument is not empty, then it specifies the pattern of files
  502. " to selectively display in the MRU window.
  503. " The 'splitdir' argument specifies the location (topleft, belowright, etc.)
  504. " of the MRU window.
  505. func! s:MRU_Open_Window(pat, splitdir, winsz) abort
  506. " Load the latest MRU file list
  507. call s:MRU_LoadList()
  508. " Check for empty MRU list
  509. if empty(s:MRU_files)
  510. call s:MRU_Warn_Msg('MRU file list is empty')
  511. return
  512. endif
  513. " Save the current buffer number. This is used later to open a file when a
  514. " entry is selected from the MRU window. The window number is not saved,
  515. " as the window number will change when new windows are opened.
  516. let s:MRU_last_buffer = bufnr('%')
  517. let bname = s:MRU_buf_name
  518. " If the window is already open, jump to it
  519. let winnum = bufwinnr(bname)
  520. if winnum != -1
  521. if winnr() != winnum
  522. " If not already in the window, jump to it
  523. exe winnum . 'wincmd w'
  524. endif
  525. setlocal modifiable
  526. " Delete the contents of the buffer to the black-hole register
  527. silent! %delete _
  528. else
  529. if g:MRU_Use_Current_Window
  530. " Reuse the current window
  531. " If the current buffer has unsaved changes or is a special buffer
  532. " or is the preview window and 'hidden' is not set, then open a
  533. " new window. Otherwise, open in the current window.
  534. if !&hidden && (&modified || !empty(&buftype) || &previewwindow)
  535. let split_window = 1
  536. else
  537. let split_window = 0
  538. endif
  539. " If the __MRU_Files__ buffer exists, then reuse it. Otherwise open
  540. " a new buffer
  541. let bufnum = bufnr(bname)
  542. if bufnum == -1
  543. if split_window
  544. let cmd = 'botright split ' . bname
  545. else
  546. let cmd = 'edit ' . bname
  547. endif
  548. else
  549. if split_window
  550. let cmd = 'botright sbuffer ' . bufnum
  551. else
  552. let cmd = 'buffer ' . bufnum
  553. endif
  554. endif
  555. exe cmd
  556. if bufnr('%') != bufnr(bname)
  557. " Failed to edit the MRU buffer
  558. return
  559. endif
  560. else
  561. " Open a new window at the bottom
  562. let cmd = 'silent! '
  563. if empty(a:splitdir)
  564. let cmd .= 'botright '
  565. else
  566. let cmd .= a:splitdir . ' '
  567. endif
  568. let sz = a:winsz
  569. if sz == 0
  570. let sz = g:MRU_Window_Height
  571. endif
  572. let cmd .= sz . 'split '
  573. " If the __MRU_Files__ buffer exists, then reuse it. Otherwise open
  574. " a new buffer
  575. let bufnum = bufnr(bname)
  576. if bufnum == -1
  577. let cmd .= bname
  578. else
  579. let cmd .= '+buffer' . bufnum
  580. endif
  581. exe cmd
  582. endif
  583. endif
  584. setlocal modifiable
  585. " Mark the buffer as scratch
  586. setlocal buftype=nofile
  587. if g:MRU_Use_Current_Window
  588. " avoid using mru buffer as alternate file
  589. setlocal bufhidden=wipe
  590. else
  591. setlocal bufhidden=delete
  592. endif
  593. setlocal noswapfile
  594. setlocal nobuflisted
  595. setlocal nowrap
  596. setlocal nonumber
  597. if exists('&relativenumber')
  598. setlocal norelativenumber
  599. endif
  600. if exists('&signcolumn')
  601. setlocal signcolumn=no
  602. endif
  603. setlocal foldcolumn=0
  604. " Set the 'filetype' to 'mru'. This allows the user to apply custom
  605. " syntax highlighting or other changes to the MRU bufer.
  606. setlocal filetype=mru
  607. " Use fixed height and width for the MRU window
  608. setlocal winfixheight winfixwidth
  609. " Setup the cpoptions properly for the maps to work
  610. let old_cpoptions = &cpoptions
  611. set cpoptions&vim
  612. " Create mappings to select and edit a file from the MRU list
  613. nnoremap <buffer> <silent> <CR>
  614. \ :call <SID>MRU_Select_File_Cmd('edit,useopen')<CR>
  615. vnoremap <buffer> <silent> <CR>
  616. \ :call <SID>MRU_Select_File_Cmd('edit,useopen')<CR>
  617. nnoremap <buffer> <silent> o
  618. \ :call <SID>MRU_Select_File_Cmd('edit,newwin_horiz')<CR>
  619. vnoremap <buffer> <silent> o
  620. \ :call <SID>MRU_Select_File_Cmd('edit,newwin_horiz')<CR>
  621. nnoremap <buffer> <silent> <S-CR>
  622. \ :call <SID>MRU_Select_File_Cmd('edit,newwin_horiz')<CR>
  623. vnoremap <buffer> <silent> <S-CR>
  624. \ :call <SID>MRU_Select_File_Cmd('edit,newwin_horiz')<CR>
  625. nnoremap <buffer> <silent> O
  626. \ :call <SID>MRU_Select_File_Cmd('edit,newwin_vert')<CR>
  627. vnoremap <buffer> <silent> O
  628. \ :call <SID>MRU_Select_File_Cmd('edit,newwin_vert')<CR>
  629. nnoremap <buffer> <silent> t
  630. \ :call <SID>MRU_Select_File_Cmd('edit,newtab')<CR>
  631. vnoremap <buffer> <silent> t
  632. \ :call <SID>MRU_Select_File_Cmd('edit,newtab')<CR>
  633. nnoremap <buffer> <silent> v
  634. \ :call <SID>MRU_Select_File_Cmd('view,useopen')<CR>
  635. nnoremap <buffer> <silent> p
  636. \ :call <SID>MRU_Select_File_Cmd('view,preview')<CR>
  637. vnoremap <buffer> <silent> p
  638. \ :<C-u>if line("'<") == line("'>")<Bar>
  639. \ call <SID>MRU_Select_File_Cmd('open,preview')<Bar>
  640. \ else<Bar>
  641. \ echoerr "Only a single file can be previewed"<Bar>
  642. \ endif<CR>
  643. nnoremap <buffer> <silent> u :MRU<CR>
  644. nnoremap <buffer> <silent> <2-LeftMouse>
  645. \ :call <SID>MRU_Select_File_Cmd('edit,useopen')<CR>
  646. nnoremap <buffer> <silent> d
  647. \ :<C-U>call <SID>MRU_Delete_From_List()<CR>
  648. nnoremap <buffer> <silent> q :close<CR>
  649. " Restore the previous cpoptions settings
  650. let &cpoptions = old_cpoptions
  651. " Display the MRU list
  652. if empty(a:pat)
  653. " No search pattern specified. Display the complete list
  654. let m = copy(s:MRU_files)
  655. else
  656. " Display only the entries matching the specified pattern. First try
  657. " fuzzy matching or as a literal pattern.
  658. if g:MRU_FuzzyMatch
  659. let m = matchfuzzy(s:MRU_files, a:pat)
  660. else
  661. let m = filter(copy(s:MRU_files), 'stridx(v:val, a:pat) != -1')
  662. endif
  663. if len(m) == 0
  664. " No match. Try using it as a regular expression
  665. let m = filter(copy(s:MRU_files), 'v:val =~# a:pat')
  666. endif
  667. endif
  668. " Get the tail part of the file name (without the directory) and display
  669. " it along with the full path in parenthesis.
  670. let output = map(m, g:MRU_Filename_Format.formatter)
  671. silent! 0put =output
  672. " Delete the empty line at the end of the buffer
  673. silent! $delete _
  674. " Move the cursor to the beginning of the file
  675. normal! gg
  676. " Add syntax highlighting for the file names
  677. if has_key(g:MRU_Filename_Format, 'syntax')
  678. exe "syntax match MRUFileName '" . g:MRU_Filename_Format.syntax . "'"
  679. highlight default link MRUFileName Identifier
  680. endif
  681. setlocal nomodifiable
  682. endfunc
  683. " MRU_Complete {{{1
  684. " Command-line completion function used by :MRU command
  685. func! s:MRU_Complete(ArgLead, CmdLine, CursorPos) abort
  686. if empty(a:ArgLead)
  687. " Return the complete list of MRU files
  688. return s:MRU_files
  689. else
  690. if g:MRU_FuzzyMatch
  691. " Return only the files fuzzy matching the specified pattern
  692. return matchfuzzy(s:MRU_files, a:ArgLead)
  693. else
  694. " Return only the files matching the specified pattern
  695. return filter(copy(s:MRU_files), 'v:val =~? a:ArgLead')
  696. endif
  697. endif
  698. endfunc
  699. " MRU_Cmd {{{1
  700. " Function to handle the MRU command
  701. " pat - File name pattern passed to the MRU command
  702. func! s:MRU_Cmd(pat, splitdir, winsz) abort
  703. if empty(a:pat)
  704. " No arguments specified. Open the MRU window
  705. call s:MRU_Open_Window('', a:splitdir, a:winsz)
  706. return
  707. endif
  708. " Load the latest MRU file
  709. call s:MRU_LoadList()
  710. " Empty MRU list
  711. if empty(s:MRU_files)
  712. call s:MRU_Warn_Msg('MRU file list is empty')
  713. return
  714. endif
  715. " If Vim supports fuzzy matching, then try fuzzy matching the pattern
  716. " against the file names. Otherwise, use the specified string as a literal
  717. " string and search for filenames containing the string. If only one
  718. " filename is found, then edit it (unless the user wants to open the MRU
  719. " window always)
  720. if g:MRU_FuzzyMatch
  721. let m = matchfuzzy(s:MRU_files, a:pat)
  722. else
  723. let m = filter(copy(s:MRU_files), 'stridx(v:val, a:pat) != -1')
  724. endif
  725. if len(m) > 0
  726. if len(m) == 1 && !g:MRU_Window_Open_Always
  727. call s:MRU_Edit_File(m[0], 0, a:splitdir)
  728. return
  729. endif
  730. " More than one file matches. Try to find an accurate match
  731. let new_m = filter(m, 'v:val ==# a:pat')
  732. if len(new_m) == 1 && !g:MRU_Window_Open_Always
  733. call s:MRU_Edit_File(new_m[0], 0, a:splitdir)
  734. return
  735. endif
  736. " Couldn't find an exact match, open the MRU window with all the
  737. " files matching the pattern.
  738. call s:MRU_Open_Window(a:pat, a:splitdir, a:winsz)
  739. return
  740. endif
  741. " Use the specified string as a regular expression pattern and search
  742. " for filenames matching the pattern
  743. let m = filter(copy(s:MRU_files), 'v:val =~? a:pat')
  744. if len(m) == 0
  745. " If an existing file (not present in the MRU list) is specified,
  746. " then open the file.
  747. if filereadable(a:pat)
  748. call s:MRU_Edit_File(a:pat, 0, a:splitdir)
  749. return
  750. endif
  751. " No filenames matching the specified pattern are found
  752. call s:MRU_Warn_Msg("MRU file list doesn't contain " .
  753. \ 'files matching ' . a:pat)
  754. return
  755. endif
  756. if len(m) == 1 && !g:MRU_Window_Open_Always
  757. call s:MRU_Edit_File(m[0], 0, a:splitdir)
  758. return
  759. endif
  760. call s:MRU_Open_Window(a:pat, a:splitdir, a:winsz)
  761. endfunc
  762. " MRU_Toggle {{{1
  763. " Toggle MRU
  764. " pat - File name pattern passed to the MRU command
  765. func! s:MRU_Toggle(pat, splitdir) abort
  766. " If the MRU window is open, close it
  767. let winnum = bufwinnr(s:MRU_buf_name)
  768. if winnum != -1
  769. exe winnum . 'wincmd w'
  770. if g:MRU_Use_Current_Window && !empty(expand('#'))
  771. silent! b #
  772. else
  773. silent! close
  774. endif
  775. else
  776. call s:MRU_Cmd(a:pat, a:splitdir, '')
  777. endif
  778. endfunction
  779. " MRU_add_files_to_menu {{{1
  780. " Adds a list of files to the "Recent Files" sub menu under the "File" menu.
  781. " prefix - Prefix to use for each of the menu entries
  782. " file_list - List of file names to add to the menu
  783. func! s:MRU_add_files_to_menu(prefix, file_list) abort
  784. for fname in a:file_list
  785. " Escape special characters in the filename
  786. let esc_fname = escape(fnamemodify(fname, ':t'), ".\\" .
  787. \ s:esc_filename_chars)
  788. let esc_fname = substitute(esc_fname, '&', '&&', 'g')
  789. " Truncate the directory name if it is long
  790. let dir_name = fnamemodify(fname, ':h')
  791. if v:version >= 800 || has('patch-7.4.1730')
  792. let len = strchars(dir_name)
  793. " Shorten long file names by adding only few characters from
  794. " the beginning and end.
  795. if len > 30
  796. let dir_name = strcharpart(dir_name, 0, 10) .
  797. \ '...' .
  798. \ strcharpart(dir_name, len - 20)
  799. endif
  800. else
  801. let len = strlen(dir_name)
  802. " Shorten long file names by adding only few characters from
  803. " the beginning and end.
  804. if len > 30
  805. let dir_name = strpart(dir_name, 0, 10) .
  806. \ '...' .
  807. \ strpart(dir_name, len - 20)
  808. endif
  809. endif
  810. let esc_dir_name = escape(dir_name, ".\\" . s:esc_filename_chars)
  811. let esc_dir_name = substitute(esc_dir_name, '&', '&&', 'g')
  812. let menu_path = '&File.&Recent\ Files.' . a:prefix . esc_fname .
  813. \ '\ (' . esc_dir_name . ')'
  814. let esc_mfname = s:MRU_escape_filename(fname)
  815. exe 'anoremenu <silent> ' . menu_path .
  816. \ " :call <SID>MRU_Edit_File('" . esc_mfname . "', 1, '')<CR>"
  817. exe 'tmenu ' . menu_path . ' Edit file ' . esc_mfname
  818. endfor
  819. endfunc
  820. " MRU_Refresh_Menu {{{1
  821. " Refresh the MRU menu
  822. func! s:MRU_Refresh_Menu() abort
  823. if !has('menu') || !g:MRU_Add_Menu
  824. " No support for menus
  825. return
  826. endif
  827. " Setup the cpoptions properly for the maps to work
  828. let old_cpoptions = &cpoptions
  829. set cpoptions&vim
  830. " Remove the MRU menu
  831. " To retain the teared-off MRU menu, we need to add a dummy entry
  832. silent! unmenu &File.&Recent\ Files
  833. " The menu priority of the File menu is 10. If the MRU plugin runs
  834. " first before menu.vim, the File menu order may not be correct.
  835. " So specify the priority of the File menu here.
  836. 10noremenu &File.&Recent\ Files.Dummy <Nop>
  837. silent! unmenu! &File.&Recent\ Files
  838. anoremenu <silent> &File.&Recent\ Files.Refresh\ list
  839. \ :call <SID>MRU_LoadList()<CR>
  840. exe 'tmenu File.&Recent\ Files.Refresh\ list Reload the MRU file list from '
  841. \ . s:MRU_escape_filename(s:MRU_File)
  842. anoremenu File.&Recent\ Files.-SEP1- :
  843. " Add the filenames in the MRU list to the menu
  844. let entry_cnt = len(s:MRU_files)
  845. if entry_cnt > g:MRU_Max_Menu_Entries
  846. " Show only MRU_Max_Menu_Entries file names in the menu
  847. let mru_list = s:MRU_files[0 : g:MRU_Max_Menu_Entries - 1]
  848. let entry_cnt = g:MRU_Max_Menu_Entries
  849. else
  850. let mru_list = s:MRU_files
  851. endif
  852. if entry_cnt > g:MRU_Max_Submenu_Entries
  853. " Split the MRU menu into sub-menus
  854. for start_idx in range(0, entry_cnt, g:MRU_Max_Submenu_Entries)
  855. let last_idx = start_idx + g:MRU_Max_Submenu_Entries - 1
  856. if last_idx >= entry_cnt
  857. let last_idx = entry_cnt - 1
  858. endif
  859. let prefix = 'Files\ (' . (start_idx + 1) . '\.\.\.' .
  860. \ (last_idx + 1) . ').'
  861. call s:MRU_add_files_to_menu(prefix,
  862. \ mru_list[start_idx : last_idx])
  863. endfor
  864. else
  865. call s:MRU_add_files_to_menu('', mru_list)
  866. endif
  867. " Remove the dummy menu entry
  868. unmenu &File.&Recent\ Files.Dummy
  869. " Restore the previous cpoptions settings
  870. let &cpoptions = old_cpoptions
  871. endfunc
  872. " MRU_Refresh {{{1
  873. " Remove non-existing files from the MRU list
  874. func s:MRU_Refresh()
  875. call filter(s:MRU_files, 'filereadable(v:val)')
  876. call s:MRU_SaveList()
  877. call s:MRU_Refresh_Menu()
  878. endfunc
  879. " MRU_Delete_From_List {{{1
  880. " remove the entry under cursor in the MRU window from the MRU list
  881. func s:MRU_Delete_From_List()
  882. call filter(s:MRU_files,
  883. \ 'v:val != matchstr(getline("."), g:MRU_Filename_Format.parser)')
  884. setlocal modifiable
  885. del _
  886. setlocal nomodifiable
  887. call s:MRU_SaveList()
  888. call s:MRU_Refresh_Menu()
  889. endfunc
  890. " Return the list of file names in the MRU list {{{1
  891. func MruGetFiles(...)
  892. " Load the latest MRU list
  893. call s:MRU_LoadList()
  894. if a:0 == 1
  895. if g:MRU_FuzzyMatch
  896. " Return only the files fuzzy matching the specified pattern
  897. return matchfuzzy(s:MRU_files, a:1)
  898. endif
  899. " Return only the files matching the specified pattern
  900. return filter(copy(s:MRU_files), 'v:val =~? a:1')
  901. endif
  902. return copy(s:MRU_files)
  903. endfunc
  904. " Load the MRU list on plugin startup
  905. call s:MRU_LoadList()
  906. " Set the first entry in the MRU list as the alternate file
  907. " Credit to Martin Roa Villescas (https://github.com/mroavi) for the patch.
  908. " bufadd() is available starting from Vim 8.1.1610
  909. if g:MRU_Set_Alternate_File == 1 &&
  910. \ (v:version >= 802 || has('patch-8.1.1610') || has('nvim'))
  911. if !empty(s:MRU_files)
  912. let first_mru_file = s:MRU_files[0]
  913. if filereadable(first_mru_file)
  914. call bufadd(first_mru_file)
  915. let @# = first_mru_file
  916. endif
  917. endif
  918. endif
  919. " MRU autocommands {{{1
  920. " Autocommands to update the most recently used files
  921. augroup MRUAutoCmds
  922. au!
  923. autocmd BufRead * call s:MRU_AddFile(expand('<abuf>'))
  924. autocmd BufWritePost * call s:MRU_AddFile(expand('<abuf>'))
  925. autocmd BufEnter * call s:MRU_AddFile(expand('<abuf>'))
  926. " The ':vimgrep' command adds all the files searched to the buffer list.
  927. " This also modifies the MRU list, even though the user didn't edit the
  928. " files. Use the following autocmds to prevent this.
  929. autocmd QuickFixCmdPre *vimgrep* let s:mru_list_locked = 1
  930. autocmd QuickFixCmdPost *vimgrep* let s:mru_list_locked = 0
  931. augroup END
  932. " MRU custom commands {{{1
  933. if v:version >= 800
  934. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 MRU
  935. \ call s:MRU_Cmd(<q-args>, <q-mods>, <count>)
  936. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 Mru
  937. \ call s:MRU_Cmd(<q-args>, <q-mods>, <count>)
  938. command! -nargs=? -complete=customlist,s:MRU_Complete MRUToggle
  939. \ call s:MRU_Toggle(<q-args>, <q-mods>)
  940. else
  941. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 MRU
  942. \ call s:MRU_Cmd(<q-args>, '', <count>)
  943. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 Mru
  944. \ call s:MRU_Cmd(<q-args>, '', <count>)
  945. command! -nargs=? -complete=customlist,s:MRU_Complete MRUToggle
  946. \ call s:MRU_Toggle(<q-args>, '')
  947. endif
  948. command! -nargs=0 MruRefresh call s:MRU_Refresh()
  949. " FZF (fuzzy finder) integration {{{1
  950. func s:MRU_FZF_EditFile(fname) abort
  951. call s:MRU_Window_Edit_File(a:fname, 0, 'edit', 'useopen')
  952. endfunc
  953. func s:MRU_FZF_Run() abort
  954. if !exists('*fzf#run')
  955. call s:MRU_Warn_Msg('FZF plugin is not present')
  956. return
  957. endif
  958. " Load the latest MRU list
  959. call s:MRU_LoadList()
  960. call fzf#run(fzf#wrap({'source' : s:MRU_files,
  961. \ 'options' : '--no-sort',
  962. \ 'sink' : function('s:MRU_FZF_EditFile')}, 0))
  963. endfunc
  964. command! -nargs=0 FZFMru call s:MRU_FZF_Run()
  965. " }}}
  966. " restore 'cpoptions'
  967. let &cpoptions = s:cpo_save
  968. unlet s:cpo_save
  969. " vim:set sw=2 sts=2 foldenable foldmethod=marker: