mru.vim 33 KB

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