mru.vim 35 KB

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