mru.vim 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. " File: mru.vim
  2. " Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
  3. " Version: 3.10.2
  4. " Last Modified: August 17, 2022
  5. " Copyright: Copyright (C) 2003-2022 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. " do case insensitive file name comparison
  682. let spat = tolower(a:pat)
  683. let m = filter(copy(s:MRU_files), 'stridx(tolower(v:val), spat) != -1')
  684. endif
  685. if len(m) == 0
  686. " No match. Try using it as a regular expression
  687. let m = filter(copy(s:MRU_files), 'v:val =~# a:pat')
  688. endif
  689. endif
  690. " Get the tail part of the file name (without the directory) and display
  691. " it along with the full path in parenthesis.
  692. let output = map(m, g:MRU_Filename_Format.formatter)
  693. silent! 0put =output
  694. " Delete the empty line at the end of the buffer
  695. silent! $delete _
  696. " Move the cursor to the beginning of the file
  697. normal! gg
  698. " Add syntax highlighting for the file names
  699. if has_key(g:MRU_Filename_Format, 'syntax')
  700. exe "syntax match MRUFileName '" . g:MRU_Filename_Format.syntax . "'"
  701. highlight default link MRUFileName Identifier
  702. endif
  703. setlocal nomodifiable
  704. endfunc
  705. " MRU_Complete {{{1
  706. " Command-line completion function used by :MRU command
  707. func! s:MRU_Complete(ArgLead, CmdLine, CursorPos) abort
  708. if empty(a:ArgLead)
  709. " Return the complete list of MRU files
  710. return s:MRU_files
  711. else
  712. if g:MRU_FuzzyMatch
  713. " Return only the files fuzzy matching the specified pattern
  714. return matchfuzzy(s:MRU_files, a:ArgLead)
  715. else
  716. " Return only the files matching the specified pattern
  717. return filter(copy(s:MRU_files), 'v:val =~? a:ArgLead')
  718. endif
  719. endif
  720. endfunc
  721. " MRU_Cmd {{{1
  722. " Function to handle the MRU command
  723. " pat - File name pattern passed to the MRU command
  724. func! s:MRU_Cmd(pat, splitdir, winsz) abort
  725. if empty(a:pat)
  726. " No arguments specified. Open the MRU window
  727. call s:MRU_Open_Window('', a:splitdir, a:winsz)
  728. return
  729. endif
  730. " Load the latest MRU file
  731. call s:MRU_LoadList()
  732. " Empty MRU list
  733. if empty(s:MRU_files)
  734. call s:MRU_Warn_Msg('MRU file list is empty')
  735. return
  736. endif
  737. " If Vim supports fuzzy matching, then try fuzzy matching the pattern
  738. " against the file names. Otherwise, use the specified string as a literal
  739. " string and search for filenames containing the string. If only one
  740. " filename is found, then edit it (unless the user wants to open the MRU
  741. " window always)
  742. if g:MRU_FuzzyMatch
  743. let m = matchfuzzy(s:MRU_files, a:pat)
  744. else
  745. " do case insensitive file name comparison
  746. let spat = tolower(a:pat)
  747. let m = filter(copy(s:MRU_files), 'stridx(tolower(v:val), spat) != -1')
  748. endif
  749. if len(m) > 0
  750. if len(m) == 1 && !g:MRU_Window_Open_Always
  751. call s:MRU_Edit_File(m[0], 0, a:splitdir)
  752. return
  753. endif
  754. " More than one file matches. Try to find an accurate match
  755. let new_m = filter(m, 'v:val ==# a:pat')
  756. if len(new_m) == 1 && !g:MRU_Window_Open_Always
  757. call s:MRU_Edit_File(new_m[0], 0, a:splitdir)
  758. return
  759. endif
  760. " Couldn't find an exact match, open the MRU window with all the
  761. " files matching the pattern.
  762. call s:MRU_Open_Window(a:pat, a:splitdir, a:winsz)
  763. return
  764. endif
  765. " Use the specified string as a regular expression pattern and search
  766. " for filenames matching the pattern
  767. let m = filter(copy(s:MRU_files), 'v:val =~? a:pat')
  768. if len(m) == 0
  769. " If an existing file (not present in the MRU list) is specified,
  770. " then open the file.
  771. if filereadable(a:pat)
  772. call s:MRU_Edit_File(a:pat, 0, a:splitdir)
  773. return
  774. endif
  775. " No filenames matching the specified pattern are found
  776. call s:MRU_Warn_Msg("MRU file list doesn't contain " .
  777. \ 'files matching ' . a:pat)
  778. return
  779. endif
  780. if len(m) == 1 && !g:MRU_Window_Open_Always
  781. call s:MRU_Edit_File(m[0], 0, a:splitdir)
  782. return
  783. endif
  784. call s:MRU_Open_Window(a:pat, a:splitdir, a:winsz)
  785. endfunc
  786. " MRU_Toggle {{{1
  787. " Toggle MRU
  788. " pat - File name pattern passed to the MRU command
  789. func! s:MRU_Toggle(pat, splitdir) abort
  790. " If the MRU window is open, close it
  791. let winnum = bufwinnr(s:MRU_buf_name)
  792. if winnum != -1
  793. exe winnum . 'wincmd w'
  794. if g:MRU_Use_Current_Window && !empty(expand('#'))
  795. silent! b #
  796. else
  797. silent! close
  798. endif
  799. else
  800. call s:MRU_Cmd(a:pat, a:splitdir, '')
  801. endif
  802. endfunction
  803. " MRU_add_files_to_menu {{{1
  804. " Adds a list of files to the "Recent Files" sub menu under the "File" menu.
  805. " prefix - Prefix to use for each of the menu entries
  806. " file_list - List of file names to add to the menu
  807. func! s:MRU_add_files_to_menu(prefix, file_list) abort
  808. for fname in a:file_list
  809. " Escape special characters in the filename
  810. let esc_fname = escape(fnamemodify(fname, ':t'), ".\\" .
  811. \ s:esc_filename_chars)
  812. let esc_fname = substitute(esc_fname, '&', '&&', 'g')
  813. " Truncate the directory name if it is long
  814. let dir_name = fnamemodify(fname, ':h')
  815. if v:version >= 800 || has('patch-7.4.1730')
  816. let len = strchars(dir_name)
  817. " Shorten long file names by adding only few characters from
  818. " the beginning and end.
  819. if len > 30
  820. let dir_name = strcharpart(dir_name, 0, 10) .
  821. \ '...' .
  822. \ strcharpart(dir_name, len - 20)
  823. endif
  824. else
  825. let len = strlen(dir_name)
  826. " Shorten long file names by adding only few characters from
  827. " the beginning and end.
  828. if len > 30
  829. let dir_name = strpart(dir_name, 0, 10) .
  830. \ '...' .
  831. \ strpart(dir_name, len - 20)
  832. endif
  833. endif
  834. let esc_dir_name = escape(dir_name, ".\\" . s:esc_filename_chars)
  835. let esc_dir_name = substitute(esc_dir_name, '&', '&&', 'g')
  836. let menu_path = '&File.&Recent\ Files.' . a:prefix . esc_fname .
  837. \ '\ (' . esc_dir_name . ')'
  838. let esc_mfname = s:MRU_escape_filename(fname)
  839. exe 'anoremenu <silent> ' . menu_path .
  840. \ " :call <SID>MRU_Edit_File('" . esc_mfname . "', 1, '')<CR>"
  841. exe 'tmenu ' . menu_path . ' Edit file ' . esc_mfname
  842. endfor
  843. endfunc
  844. " MRU_Refresh_Menu {{{1
  845. " Refresh the MRU menu
  846. func! s:MRU_Refresh_Menu() abort
  847. if !has('menu') || !g:MRU_Add_Menu
  848. " No support for menus
  849. return
  850. endif
  851. " Setup the cpoptions properly for the maps to work
  852. let old_cpoptions = &cpoptions
  853. set cpoptions&vim
  854. " Remove the MRU menu
  855. " To retain the teared-off MRU menu, we need to add a dummy entry
  856. silent! unmenu &File.&Recent\ Files
  857. " The menu priority of the File menu is 10. If the MRU plugin runs
  858. " first before menu.vim, the File menu order may not be correct.
  859. " So specify the priority of the File menu here.
  860. 10noremenu &File.&Recent\ Files.Dummy <Nop>
  861. silent! unmenu! &File.&Recent\ Files
  862. anoremenu <silent> &File.&Recent\ Files.Refresh\ list
  863. \ :call <SID>MRU_LoadList()<CR>
  864. exe 'tmenu File.&Recent\ Files.Refresh\ list Reload the MRU file list from '
  865. \ . s:MRU_escape_filename(s:MRU_File)
  866. anoremenu File.&Recent\ Files.-SEP1- :
  867. " Add the filenames in the MRU list to the menu
  868. let entry_cnt = len(s:MRU_files)
  869. if entry_cnt > g:MRU_Max_Menu_Entries
  870. " Show only MRU_Max_Menu_Entries file names in the menu
  871. let mru_list = s:MRU_files[0 : g:MRU_Max_Menu_Entries - 1]
  872. let entry_cnt = g:MRU_Max_Menu_Entries
  873. else
  874. let mru_list = s:MRU_files
  875. endif
  876. if entry_cnt > g:MRU_Max_Submenu_Entries
  877. " Split the MRU menu into sub-menus
  878. for start_idx in range(0, entry_cnt, g:MRU_Max_Submenu_Entries)
  879. let last_idx = start_idx + g:MRU_Max_Submenu_Entries - 1
  880. if last_idx >= entry_cnt
  881. let last_idx = entry_cnt - 1
  882. endif
  883. let prefix = 'Files\ (' . (start_idx + 1) . '\.\.\.' .
  884. \ (last_idx + 1) . ').'
  885. call s:MRU_add_files_to_menu(prefix,
  886. \ mru_list[start_idx : last_idx])
  887. endfor
  888. else
  889. call s:MRU_add_files_to_menu('', mru_list)
  890. endif
  891. " Remove the dummy menu entry
  892. unmenu &File.&Recent\ Files.Dummy
  893. " Restore the previous cpoptions settings
  894. let &cpoptions = old_cpoptions
  895. endfunc
  896. " MRU_Refresh {{{1
  897. " Remove non-existing files from the MRU list
  898. func s:MRU_Refresh()
  899. call filter(s:MRU_files, 'filereadable(v:val)')
  900. call s:MRU_SaveList()
  901. call s:MRU_Refresh_Menu()
  902. endfunc
  903. " MRU_Delete_From_List {{{1
  904. " remove the entry under cursor in the MRU window from the MRU list
  905. func s:MRU_Delete_From_List()
  906. call filter(s:MRU_files,
  907. \ 'v:val != matchstr(getline("."), g:MRU_Filename_Format.parser)')
  908. setlocal modifiable
  909. del _
  910. setlocal nomodifiable
  911. call s:MRU_SaveList()
  912. call s:MRU_Refresh_Menu()
  913. endfunc
  914. " Return the list of file names in the MRU list {{{1
  915. func MruGetFiles(...)
  916. " Load the latest MRU list
  917. call s:MRU_LoadList()
  918. if a:0 == 1
  919. if g:MRU_FuzzyMatch
  920. " Return only the files fuzzy matching the specified pattern
  921. return matchfuzzy(s:MRU_files, a:1)
  922. endif
  923. " Return only the files matching the specified pattern
  924. return filter(copy(s:MRU_files), 'v:val =~? a:1')
  925. endif
  926. return copy(s:MRU_files)
  927. endfunc
  928. " Load the MRU list on plugin startup
  929. call s:MRU_LoadList()
  930. " Set the first entry in the MRU list as the alternate file
  931. " Credit to Martin Roa Villescas (https://github.com/mroavi) for the patch.
  932. " bufadd() is available starting from Vim 8.1.1610
  933. if g:MRU_Set_Alternate_File == 1 &&
  934. \ (v:version >= 802 || has('patch-8.1.1610') || has('nvim'))
  935. if !empty(s:MRU_files)
  936. let first_mru_file = s:MRU_files[0]
  937. if filereadable(first_mru_file)
  938. call bufadd(first_mru_file)
  939. let @# = first_mru_file
  940. endif
  941. endif
  942. endif
  943. " MRU autocommands {{{1
  944. " Autocommands to update the most recently used files
  945. augroup MRUAutoCmds
  946. au!
  947. autocmd BufRead * call s:MRU_AddFile(expand('<abuf>'))
  948. autocmd BufWritePost * call s:MRU_AddFile(expand('<abuf>'))
  949. autocmd BufEnter * call s:MRU_AddFile(expand('<abuf>'))
  950. " The ':vimgrep' command adds all the files searched to the buffer list.
  951. " This also modifies the MRU list, even though the user didn't edit the
  952. " files. Use the following autocmds to prevent this.
  953. autocmd QuickFixCmdPre *vimgrep* let s:mru_list_locked = 1
  954. autocmd QuickFixCmdPost *vimgrep* let s:mru_list_locked = 0
  955. augroup END
  956. " MRU custom commands {{{1
  957. if v:version >= 800
  958. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 MRU
  959. \ call s:MRU_Cmd(<q-args>, <q-mods>, <count>)
  960. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 Mru
  961. \ call s:MRU_Cmd(<q-args>, <q-mods>, <count>)
  962. command! -nargs=? -complete=customlist,s:MRU_Complete MRUToggle
  963. \ call s:MRU_Toggle(<q-args>, <q-mods>)
  964. else
  965. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 MRU
  966. \ call s:MRU_Cmd(<q-args>, '', <count>)
  967. command! -nargs=? -complete=customlist,s:MRU_Complete -count=0 Mru
  968. \ call s:MRU_Cmd(<q-args>, '', <count>)
  969. command! -nargs=? -complete=customlist,s:MRU_Complete MRUToggle
  970. \ call s:MRU_Toggle(<q-args>, '')
  971. endif
  972. command! -nargs=0 MruRefresh call s:MRU_Refresh()
  973. " FZF (fuzzy finder) integration {{{1
  974. func s:MRU_FZF_EditFile(fname) abort
  975. call s:MRU_Window_Edit_File(a:fname, 0, 'edit', 'useopen')
  976. endfunc
  977. func s:MRU_FZF_Run() abort
  978. if !exists('*fzf#run')
  979. call s:MRU_Warn_Msg('FZF plugin is not present')
  980. return
  981. endif
  982. " Load the latest MRU list
  983. call s:MRU_LoadList()
  984. call fzf#run(fzf#wrap({'source' : s:MRU_files,
  985. \ 'options' : '--no-sort',
  986. \ 'sink' : function('s:MRU_FZF_EditFile')}, 0))
  987. endfunc
  988. command! -nargs=0 FZFMru call s:MRU_FZF_Run()
  989. " }}}
  990. " restore 'cpoptions'
  991. let &cpoptions = s:cpo_save
  992. unlet s:cpo_save
  993. " vim:set sw=2 sts=2 foldenable foldmethod=marker: