mru.vim 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  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. if has('patch-8.2.1978') && mode() ==# 'n'
  473. let l:firstline = line('.')
  474. let l:lastline = l:firstline + v:count
  475. if l:lastline > line('$')
  476. let l:lastline = line('$')
  477. endif
  478. elseif has('patch-8.2.1978') && mode() !=# 'n'
  479. let l:firstline = line('.')
  480. let l:lastline = line('v')
  481. if l:firstline > l:lastline
  482. let [l:firstline, l:lastline] = [l:lastline, l:firstline]
  483. endif
  484. else
  485. let l:firstline = a:firstline
  486. let l:lastline = a:lastline
  487. endif
  488. let fnames = getline(l:firstline, l:lastline)
  489. if g:MRU_Auto_Close == 1 && g:MRU_Use_Current_Window == 0
  490. " Automatically close the window if the file window is
  491. " not used to display the MRU list.
  492. silent! close
  493. endif
  494. let multi = 0
  495. for f in fnames
  496. if empty(f)
  497. continue
  498. endif
  499. " The text in the MRU window contains the filename in parenthesis
  500. let file = matchstr(f, g:MRU_Filename_Format.parser)
  501. call s:MRU_Window_Edit_File(file, multi, edit_type, open_type)
  502. if l:firstline != l:lastline
  503. " Opening multiple files
  504. let multi = 1
  505. endif
  506. endfor
  507. endfunc
  508. " MRU_Warn_Msg {{{1
  509. " Display a warning message
  510. func! s:MRU_Warn_Msg(msg) abort
  511. echohl WarningMsg
  512. echo a:msg
  513. echohl None
  514. endfunc
  515. " MRU_Open_Window {{{1
  516. " Display the Most Recently Used file list in a temporary window.
  517. " If the 'pat' argument is not empty, then it specifies the pattern of files
  518. " to selectively display in the MRU window.
  519. " The 'splitdir' argument specifies the location (topleft, belowright, etc.)
  520. " of the MRU window.
  521. func! s:MRU_Open_Window(pat, splitdir, winsz) abort
  522. " Load the latest MRU file list
  523. call s:MRU_LoadList()
  524. " Check for empty MRU list
  525. if empty(s:MRU_files)
  526. call s:MRU_Warn_Msg('MRU file list is empty')
  527. return
  528. endif
  529. " Save the current buffer number. This is used later to open a file when a
  530. " entry is selected from the MRU window. The window number is not saved,
  531. " as the window number will change when new windows are opened.
  532. let s:MRU_last_buffer = bufnr('%')
  533. let bname = s:MRU_buf_name
  534. " If the window is already open, jump to it
  535. let winnum = bufwinnr(bname)
  536. if winnum != -1
  537. if winnr() != winnum
  538. " If not already in the window, jump to it
  539. exe winnum . 'wincmd w'
  540. endif
  541. setlocal modifiable
  542. " Delete the contents of the buffer to the black-hole register
  543. silent! %delete _
  544. else
  545. if g:MRU_Use_Current_Window
  546. " Reuse the current window
  547. " If the current buffer has unsaved changes or is a special buffer
  548. " or is the preview window and 'hidden' is not set, then open a
  549. " new window. Otherwise, open in the current window.
  550. if !&hidden && (&modified || !empty(&buftype) || &previewwindow)
  551. let split_window = 1
  552. else
  553. let split_window = 0
  554. endif
  555. " If the __MRU_Files__ buffer exists, then reuse it. Otherwise open
  556. " a new buffer
  557. let bufnum = bufnr(bname)
  558. if bufnum == -1
  559. if split_window
  560. let cmd = 'botright split ' . bname
  561. else
  562. let cmd = 'edit ' . bname
  563. endif
  564. else
  565. if split_window
  566. let cmd = 'botright sbuffer ' . bufnum
  567. else
  568. let cmd = 'buffer ' . bufnum
  569. endif
  570. endif
  571. exe cmd
  572. if bufnr('%') != bufnr(bname)
  573. " Failed to edit the MRU buffer
  574. return
  575. endif
  576. else
  577. " Open a new window at the bottom
  578. let cmd = 'silent! '
  579. if empty(a:splitdir)
  580. let cmd .= 'botright '
  581. else
  582. let cmd .= a:splitdir . ' '
  583. endif
  584. let sz = a:winsz
  585. if sz == 0
  586. let sz = g:MRU_Window_Height
  587. endif
  588. let cmd .= sz . 'split '
  589. " If the __MRU_Files__ buffer exists, then reuse it. Otherwise open
  590. " a new buffer
  591. let bufnum = bufnr(bname)
  592. if bufnum == -1
  593. let cmd .= bname
  594. else
  595. let cmd .= '+buffer' . bufnum
  596. endif
  597. exe cmd
  598. endif
  599. endif
  600. setlocal modifiable
  601. " Mark the buffer as scratch
  602. setlocal buftype=nofile
  603. if g:MRU_Use_Current_Window
  604. " avoid using mru buffer as alternate file
  605. setlocal bufhidden=wipe
  606. else
  607. setlocal bufhidden=delete
  608. endif
  609. setlocal noswapfile
  610. setlocal nobuflisted
  611. setlocal nowrap
  612. setlocal nonumber
  613. if exists('&relativenumber')
  614. setlocal norelativenumber
  615. endif
  616. if exists('&signcolumn')
  617. setlocal signcolumn=no
  618. endif
  619. setlocal foldcolumn=0
  620. " Set the 'filetype' to 'mru'. This allows the user to apply custom
  621. " syntax highlighting or other changes to the MRU bufer.
  622. setlocal filetype=mru
  623. if !g:MRU_Use_Current_Window
  624. " Use fixed height and width for the MRU window
  625. setlocal winfixheight winfixwidth
  626. endif
  627. " Setup the cpoptions properly for the maps to work
  628. let old_cpoptions = &cpoptions
  629. set cpoptions&vim
  630. " Create mappings to select and edit a file from the MRU list
  631. let l:cmd = has('patch-8.2.1978') ? '<cmd>' : ':'
  632. let l:silent = has('patch-8.2.1978') ? '' : '<silent> '
  633. execute 'nnoremap <buffer> ' . l:silent . '<CR> ' .
  634. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,useopen")<CR>'
  635. execute 'vnoremap <buffer> ' . l:silent . '<CR> ' .
  636. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,useopen")<CR>'
  637. execute 'nnoremap <buffer> ' . l:silent . 'o ' .
  638. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newwin_horiz")<CR>'
  639. execute 'vnoremap <buffer> ' . l:silent . 'o ' .
  640. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newwin_horiz")<CR>'
  641. execute 'nnoremap <buffer> ' . l:silent . '<S-CR> ' .
  642. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newwin_horiz")<CR>'
  643. execute 'vnoremap <buffer> ' . l:silent . '<S-CR> ' .
  644. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newwin_horiz")<CR>'
  645. execute 'nnoremap <buffer> ' . l:silent . 'O ' .
  646. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newwin_vert")<CR>'
  647. execute 'vnoremap <buffer> ' . l:silent . 'O ' .
  648. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newwin_vert")<CR>'
  649. execute 'nnoremap <buffer> ' . l:silent . 't ' .
  650. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newtab")<CR>'
  651. execute 'vnoremap <buffer> ' . l:silent . 't ' .
  652. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,newtab")<CR>'
  653. execute 'nnoremap <buffer> ' . l:silent . 'v ' .
  654. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("view,useopen")<CR>'
  655. execute 'nnoremap <buffer> ' . l:silent . 'p ' .
  656. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("view,preview")<CR>'
  657. vnoremap <buffer> <silent> p
  658. \ :<C-u>if line("'<") == line("'>")<Bar>
  659. \ call <SID>MRU_Select_File_Cmd('open,preview')<Bar>
  660. \ else<Bar>
  661. \ echoerr "Only a single file can be previewed"<Bar>
  662. \ endif<CR>
  663. execute 'nnoremap <buffer> ' . l:silent . 'u ' . l:cmd . 'MRU<CR>'
  664. execute 'nnoremap <buffer> ' . l:silent . '<2-LeftMouse> ' .
  665. \ l:cmd . 'call <SID>MRU_Select_File_Cmd("edit,useopen")<CR>'
  666. nnoremap <buffer> <silent> d
  667. \ :<C-U>call <SID>MRU_Delete_From_List()<CR>
  668. nnoremap <buffer> <silent> q :close<CR>
  669. " Restore the previous cpoptions settings
  670. let &cpoptions = old_cpoptions
  671. " Display the MRU list
  672. if empty(a:pat)
  673. " No search pattern specified. Display the complete list
  674. let m = copy(s:MRU_files)
  675. else
  676. " Display only the entries matching the specified pattern. First try
  677. " fuzzy matching or as a literal pattern.
  678. if g:MRU_FuzzyMatch
  679. let m = matchfuzzy(s:MRU_files, a:pat)
  680. else
  681. let m = filter(copy(s:MRU_files), 'stridx(v:val, a:pat) != -1')
  682. endif
  683. if len(m) == 0
  684. " No match. Try using it as a regular expression
  685. let m = filter(copy(s:MRU_files), 'v:val =~# a:pat')
  686. endif
  687. endif
  688. " Get the tail part of the file name (without the directory) and display
  689. " it along with the full path in parenthesis.
  690. let output = map(m, g:MRU_Filename_Format.formatter)
  691. silent! 0put =output
  692. " Delete the empty line at the end of the buffer
  693. silent! $delete _
  694. " Move the cursor to the beginning of the file
  695. normal! gg
  696. " Add syntax highlighting for the file names
  697. if has_key(g:MRU_Filename_Format, 'syntax')
  698. exe "syntax match MRUFileName '" . g:MRU_Filename_Format.syntax . "'"
  699. highlight default link MRUFileName Identifier
  700. endif
  701. setlocal nomodifiable
  702. endfunc
  703. " MRU_Complete {{{1
  704. " Command-line completion function used by :MRU command
  705. func! s:MRU_Complete(ArgLead, CmdLine, CursorPos) abort
  706. if empty(a:ArgLead)
  707. " Return the complete list of MRU files
  708. return s:MRU_files
  709. else
  710. if g:MRU_FuzzyMatch
  711. " Return only the files fuzzy matching the specified pattern
  712. return matchfuzzy(s:MRU_files, a:ArgLead)
  713. else
  714. " Return only the files matching the specified pattern
  715. return filter(copy(s:MRU_files), 'v:val =~? a:ArgLead')
  716. endif
  717. endif
  718. endfunc
  719. " MRU_Cmd {{{1
  720. " Function to handle the MRU command
  721. " pat - File name pattern passed to the MRU command
  722. func! s:MRU_Cmd(pat, splitdir, winsz) abort
  723. if empty(a:pat)
  724. " No arguments specified. Open the MRU window
  725. call s:MRU_Open_Window('', a:splitdir, a:winsz)
  726. return
  727. endif
  728. " Load the latest MRU file
  729. call s:MRU_LoadList()
  730. " Empty MRU list
  731. if empty(s:MRU_files)
  732. call s:MRU_Warn_Msg('MRU file list is empty')
  733. return
  734. endif
  735. " If Vim supports fuzzy matching, then try fuzzy matching the pattern
  736. " against the file names. Otherwise, use the specified string as a literal
  737. " string and search for filenames containing the string. If only one
  738. " filename is found, then edit it (unless the user wants to open the MRU
  739. " window always)
  740. if g:MRU_FuzzyMatch
  741. let m = matchfuzzy(s:MRU_files, a:pat)
  742. else
  743. let m = filter(copy(s:MRU_files), 'stridx(v:val, a:pat) != -1')
  744. endif
  745. if len(m) > 0
  746. if len(m) == 1 && !g:MRU_Window_Open_Always
  747. call s:MRU_Edit_File(m[0], 0, a:splitdir)
  748. return
  749. endif
  750. " More than one file matches. Try to find an accurate match
  751. let new_m = filter(m, 'v:val ==# a:pat')
  752. if len(new_m) == 1 && !g:MRU_Window_Open_Always
  753. call s:MRU_Edit_File(new_m[0], 0, a:splitdir)
  754. return
  755. endif
  756. " Couldn't find an exact match, open the MRU window with all the
  757. " files matching the pattern.
  758. call s:MRU_Open_Window(a:pat, a:splitdir, a:winsz)
  759. return
  760. endif
  761. " Use the specified string as a regular expression pattern and search
  762. " for filenames matching the pattern
  763. let m = filter(copy(s:MRU_files), 'v:val =~? a:pat')
  764. if len(m) == 0
  765. " If an existing file (not present in the MRU list) is specified,
  766. " then open the file.
  767. if filereadable(a:pat)
  768. call s:MRU_Edit_File(a:pat, 0, a:splitdir)
  769. return
  770. endif
  771. " No filenames matching the specified pattern are found
  772. call s:MRU_Warn_Msg("MRU file list doesn't contain " .
  773. \ 'files matching ' . a:pat)
  774. return
  775. endif
  776. if len(m) == 1 && !g:MRU_Window_Open_Always
  777. call s:MRU_Edit_File(m[0], 0, a:splitdir)
  778. return
  779. endif
  780. call s:MRU_Open_Window(a:pat, a:splitdir, a:winsz)
  781. endfunc
  782. " MRU_Toggle {{{1
  783. " Toggle MRU
  784. " pat - File name pattern passed to the MRU command
  785. func! s:MRU_Toggle(pat, splitdir) abort
  786. " If the MRU window is open, close it
  787. let winnum = bufwinnr(s:MRU_buf_name)
  788. if winnum != -1
  789. exe winnum . 'wincmd w'
  790. if g:MRU_Use_Current_Window && !empty(expand('#'))
  791. silent! b #
  792. else
  793. silent! close
  794. endif
  795. else
  796. call s:MRU_Cmd(a:pat, a:splitdir, '')
  797. endif
  798. endfunction
  799. " MRU_add_files_to_menu {{{1
  800. " Adds a list of files to the "Recent Files" sub menu under the "File" menu.
  801. " prefix - Prefix to use for each of the menu entries
  802. " file_list - List of file names to add to the menu
  803. func! s:MRU_add_files_to_menu(prefix, file_list) abort
  804. for fname in a:file_list
  805. " Escape special characters in the filename
  806. let esc_fname = escape(fnamemodify(fname, ':t'), ".\\" .
  807. \ s:esc_filename_chars)
  808. let esc_fname = substitute(esc_fname, '&', '&&', 'g')
  809. " Truncate the directory name if it is long
  810. let dir_name = fnamemodify(fname, ':h')
  811. if v:version >= 800 || has('patch-7.4.1730')
  812. let len = strchars(dir_name)
  813. " Shorten long file names by adding only few characters from
  814. " the beginning and end.
  815. if len > 30
  816. let dir_name = strcharpart(dir_name, 0, 10) .
  817. \ '...' .
  818. \ strcharpart(dir_name, len - 20)
  819. endif
  820. else
  821. let len = strlen(dir_name)
  822. " Shorten long file names by adding only few characters from
  823. " the beginning and end.
  824. if len > 30
  825. let dir_name = strpart(dir_name, 0, 10) .
  826. \ '...' .
  827. \ strpart(dir_name, len - 20)
  828. endif
  829. endif
  830. let esc_dir_name = escape(dir_name, ".\\" . s:esc_filename_chars)
  831. let esc_dir_name = substitute(esc_dir_name, '&', '&&', 'g')
  832. let menu_path = '&File.&Recent\ Files.' . a:prefix . esc_fname .
  833. \ '\ (' . esc_dir_name . ')'
  834. let esc_mfname = s:MRU_escape_filename(fname)
  835. exe 'anoremenu <silent> ' . menu_path .
  836. \ " :call <SID>MRU_Edit_File('" . esc_mfname . "', 1, '')<CR>"
  837. exe 'tmenu ' . menu_path . ' Edit file ' . esc_mfname
  838. endfor
  839. endfunc
  840. " MRU_Refresh_Menu {{{1
  841. " Refresh the MRU menu
  842. func! s:MRU_Refresh_Menu() abort
  843. if !has('menu') || !g:MRU_Add_Menu
  844. " No support for menus
  845. return
  846. endif
  847. " Setup the cpoptions properly for the maps to work
  848. let old_cpoptions = &cpoptions
  849. set cpoptions&vim
  850. " Remove the MRU menu
  851. " To retain the teared-off MRU menu, we need to add a dummy entry
  852. silent! unmenu &File.&Recent\ Files
  853. " The menu priority of the File menu is 10. If the MRU plugin runs
  854. " first before menu.vim, the File menu order may not be correct.
  855. " So specify the priority of the File menu here.
  856. 10noremenu &File.&Recent\ Files.Dummy <Nop>
  857. silent! unmenu! &File.&Recent\ Files
  858. anoremenu <silent> &File.&Recent\ Files.Refresh\ list
  859. \ :call <SID>MRU_LoadList()<CR>
  860. exe 'tmenu File.&Recent\ Files.Refresh\ list Reload the MRU file list from '
  861. \ . s:MRU_escape_filename(s:MRU_File)
  862. anoremenu File.&Recent\ Files.-SEP1- :
  863. " Add the filenames in the MRU list to the menu
  864. let entry_cnt = len(s:MRU_files)
  865. if entry_cnt > g:MRU_Max_Menu_Entries
  866. " Show only MRU_Max_Menu_Entries file names in the menu
  867. let mru_list = s:MRU_files[0 : g:MRU_Max_Menu_Entries - 1]
  868. let entry_cnt = g:MRU_Max_Menu_Entries
  869. else
  870. let mru_list = s:MRU_files
  871. endif
  872. if entry_cnt > g:MRU_Max_Submenu_Entries
  873. " Split the MRU menu into sub-menus
  874. for start_idx in range(0, entry_cnt, g:MRU_Max_Submenu_Entries)
  875. let last_idx = start_idx + g:MRU_Max_Submenu_Entries - 1
  876. if last_idx >= entry_cnt
  877. let last_idx = entry_cnt - 1
  878. endif
  879. let prefix = 'Files\ (' . (start_idx + 1) . '\.\.\.' .
  880. \ (last_idx + 1) . ').'
  881. call s:MRU_add_files_to_menu(prefix,
  882. \ mru_list[start_idx : last_idx])
  883. endfor
  884. else
  885. call s:MRU_add_files_to_menu('', mru_list)
  886. endif
  887. " Remove the dummy menu entry
  888. unmenu &File.&Recent\ Files.Dummy
  889. " Restore the previous cpoptions settings
  890. let &cpoptions = old_cpoptions
  891. endfunc
  892. " MRU_Refresh {{{1
  893. " Remove non-existing files from the MRU list
  894. func s:MRU_Refresh()
  895. call filter(s:MRU_files, 'filereadable(v:val)')
  896. call s:MRU_SaveList()
  897. call s:MRU_Refresh_Menu()
  898. endfunc
  899. " MRU_Delete_From_List {{{1
  900. " remove the entry under cursor in the MRU window from the MRU list
  901. func s:MRU_Delete_From_List()
  902. call filter(s:MRU_files,
  903. \ 'v:val != matchstr(getline("."), g:MRU_Filename_Format.parser)')
  904. setlocal modifiable
  905. del _
  906. setlocal nomodifiable
  907. call s:MRU_SaveList()
  908. call s:MRU_Refresh_Menu()
  909. endfunc
  910. " Return the list of file names in the MRU list {{{1
  911. func MruGetFiles(...)
  912. " Load the latest MRU list
  913. call s:MRU_LoadList()
  914. if a:0 == 1
  915. if g:MRU_FuzzyMatch
  916. " Return only the files fuzzy matching the specified pattern
  917. return matchfuzzy(s:MRU_files, a:1)
  918. endif
  919. " Return only the files matching the specified pattern
  920. return filter(copy(s:MRU_files), 'v:val =~? a:1')
  921. endif
  922. return copy(s:MRU_files)
  923. endfunc
  924. " Load the MRU list on plugin startup
  925. call s:MRU_LoadList()
  926. " Set the first entry in the MRU list as the alternate file
  927. " Credit to Martin Roa Villescas (https://github.com/mroavi) for the patch.
  928. " bufadd() is available starting from Vim 8.1.1610
  929. if g:MRU_Set_Alternate_File == 1 &&
  930. \ (v:version >= 802 || has('patch-8.1.1610') || has('nvim'))
  931. if !empty(s:MRU_files)
  932. let first_mru_file = s:MRU_files[0]
  933. if filereadable(first_mru_file)
  934. call bufadd(first_mru_file)
  935. let @# = first_mru_file
  936. endif
  937. endif
  938. endif
  939. " MRU autocommands {{{1
  940. " Autocommands to update the most recently used files
  941. augroup MRUAutoCmds
  942. au!
  943. autocmd BufRead * call s:MRU_AddFile(expand('<abuf>'))
  944. autocmd BufWritePost * call s:MRU_AddFile(expand('<abuf>'))
  945. autocmd BufEnter * call s:MRU_AddFile(expand('<abuf>'))
  946. " The ':vimgrep' command adds all the files searched to the buffer list.
  947. " This also modifies the MRU list, even though the user didn't edit the
  948. " files. Use the following autocmds to prevent this.
  949. autocmd QuickFixCmdPre *vimgrep* let s:mru_list_locked = 1
  950. autocmd QuickFixCmdPost *vimgrep* let s:mru_list_locked = 0
  951. augroup END
  952. " MRU custom commands {{{1
  953. if v:version >= 800
  954. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 MRU
  955. \ call s:MRU_Cmd(<q-args>, <q-mods>, <count>)
  956. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 Mru
  957. \ call s:MRU_Cmd(<q-args>, <q-mods>, <count>)
  958. command! -nargs=? -complete=customlist,s:MRU_Complete MRUToggle
  959. \ call s:MRU_Toggle(<q-args>, <q-mods>)
  960. else
  961. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 MRU
  962. \ call s:MRU_Cmd(<q-args>, '', <count>)
  963. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 Mru
  964. \ call s:MRU_Cmd(<q-args>, '', <count>)
  965. command! -nargs=? -complete=customlist,s:MRU_Complete MRUToggle
  966. \ call s:MRU_Toggle(<q-args>, '')
  967. endif
  968. command! -nargs=0 MruRefresh call s:MRU_Refresh()
  969. " FZF (fuzzy finder) integration {{{1
  970. func s:MRU_FZF_EditFile(fname) abort
  971. call s:MRU_Window_Edit_File(a:fname, 0, 'edit', 'useopen')
  972. endfunc
  973. func s:MRU_FZF_Run() abort
  974. if !exists('*fzf#run')
  975. call s:MRU_Warn_Msg('FZF plugin is not present')
  976. return
  977. endif
  978. " Load the latest MRU list
  979. call s:MRU_LoadList()
  980. call fzf#run(fzf#wrap({'source' : s:MRU_files,
  981. \ 'options' : '--no-sort',
  982. \ 'sink' : function('s:MRU_FZF_EditFile')}, 0))
  983. endfunc
  984. command! -nargs=0 FZFMru call s:MRU_FZF_Run()
  985. " }}}
  986. " restore 'cpoptions'
  987. let &cpoptions = s:cpo_save
  988. unlet s:cpo_save
  989. " vim:set sw=2 sts=2 foldenable foldmethod=marker: