Related
Is there a way to search the list of recently used file in Vim? The list can be displayed using
browse old
but / does not work. I am aware of some plugins (e.g. MRU) but would prefer to not use a plugin.
Here's a short scriptlet that opens the file list in a scratch buffer. As a bonus, it defines a local <Enter> mapping to :edit the current file. With this, you can search with all built-in commands like /:
:new +setl\ buftype=nofile | 0put =v:oldfiles | nnoremap <buffer> <CR> :e <C-r>=getline('.')<CR><CR>
If you really want to avoid a plugin:
:new The old files will be printed into this buffer
:redir #X where X is a temporary register`
:silent echo(v:oldfiles) 'Silent' is there to not actually print onto your screen
:redir END
"Xp paste the temporary register
(optional) Do some regex-fu to put each file on its own line.
Put the above into a function and voila. Also :help redir
It's actually not very hard to write a simple (simplistic?) MRU command with completion that works like :edit or :split:
" this is our 'main' function: it couldn't be simpler
function! MRU(arg)
execute 'edit ' . a:arg
endfunction
" the completion function, again it's very simple
function! MRUComplete(ArgLead, CmdLine, CursorPos)
return filter(copy(v:oldfiles), 'v:val =~ a:ArgLead')
endfunction
" the actual command
" it accepts only one argument
" it's set to use the function above for completion
command! -nargs=1 -complete=customlist,MRUComplete MRU call MRU(<f-args>)
Here is a .vimrc version of code above. Just add following lines to .vimrc and map to desired keys (in my case it is 'o). In addition define patterns to remove "junk" files. Also cursor is placed at the top for convenience.
Most hard thing is to map an Enter inside nested nmap. ^V is the result of doubled Ctrl-V. ^R is the result of Ctrl-V+Ctrl-R. ^M is the result of Ctrl-V+Enter. You need manually repeat those symbols - not just Copy/Paste. Spent hours to understand this magic - so I'm glad to share. This technology lets you add own macroses in .vimrc.
" Browse Old Files
nnoremap <silent> 'o :enew<CR>:set buftype=nofile<CR>:set nobuflisted<CR>:exe "0put =v:oldfiles"<CR>:nmap <buffer> ^V^V^M :e ^V^V^R=getline('.')^V^V^M^V^V^M<CR>:g/\v(stdout\|nerd\|fugitive)/d<CR>:0<CR>
This is my take on Ingo's answer above for my .vimrc:
Opens the old files in either a vertical split or tab, then maps enter to open file under cursor! magic!
" open old files list and map enter to open line
" vertical split
noremap <leader>vv :vnew +setl\ buftype=nofile <bar> 0put =v:oldfiles <bar> nnoremap <lt>buffer> <lt>CR> :e <lt>C-r>=getline('.')<lt>CR><lt>CR><CR><CR>
" in new tab
noremap <leader>vt :tabnew +setl\ buftype=nofile <bar> 0put =v:oldfiles <bar> nnoremap <lt>buffer> <lt>CR> :e <lt>C-r>=getline('.')<lt>CR><lt>CR <CR><CR>
As everyone knows, we can :source current buffer by :so % .
But sometimes I want to :source just a part of the buffer, not the whole buffer. Say, I just added something to my .vimrc, and want to source that part, but I don't want to re-source all the rest stuff.
I tried select text and :so (actually :'<,'>so ) , but it reported that range is not allowed. So, how could this be done?
Of course I can save needed part to the temp file and source it, but it is clearly annoying.
Why not have the following mappings for sourcing a file (or a range):
" source the current file
nmap <leader>vs :source %<CR>
" source a visual range
vmap <leader>vs y:#"<CR>
Now, you can press ,vs (if your mapleader is ,), and the selected range will be sourced or otherwise, in normal mode, current file will be sourced.
You can define the following command, which operates on the current line or passed range:
":[range]Execute Execute text lines as ex commands.
" Handles |line-continuation|.
command! -bar -range Execute silent <line1>,<line2>yank z | let #z = substitute(#z, '\n\s*\\', '', 'g') | #z
Thanks to Ingo Karkat, I have taken his main idea and improved it. What I wanted to improve:
We have to use additional user-specified command :Execute instead of standard :so (ok, we can name user-specified command :So, anyway it's annoying to use new capitalized version of the command)
There is little side effect: register #z is corrupted after executing the command.
With my script below, we can use :so {file} command as before, and we are also able to use it with range: :'<,'>so (which actually expands to :'<,'>Source)
Here:
" This script provides :Source command, a drop-in replacement for
" built-in :source command, but this one also can take range and execute just
" a part of the buffer.
"
" Sources given range of the buffer
function! <SID>SourcePart(line1, line2)
let tmp = #z
silent exec a:line1.",".a:line2."yank z"
let #z = substitute(#z, '\n\s*\\', '', 'g')
#z
let #z = tmp
endfunction
" if some argument is given, this command calls built-in command :source with
" given arguments; otherwise calls function <SID>SourcePart() which sources
" visually selected lines of the buffer.
command! -nargs=? -bar -range Source if empty("<args>") | call <SID>SourcePart(<line1>, <line2>) | else | exec "so <args>" | endif
" in order to achieve _real_ drop-in replacement, I like to abbreviate
" existing :so[urce] command to the new one.
"
" So, we can call :so % just as before, and we are also call '<,'>so
cnoreabbr so Source
cnoreabbr sou Source
cnoreabbr sour Source
cnoreabbr sourc Source
cnoreabbr source Source
The following works if you only selected one line:
yq:p<enter>
This will also work:
y:<control-r>"<enter>
Suppose I have a folder with lots of .h and .cpp files. I frequently need to do the following:
open a file prefix_SomeReallyLongFileName.h,
make some changes to it,
and then open prefix_SomeReallyLongFileName.cpp.
I can do this using :e <filename> using auto-complete, but as the prefix is same for many of the files, this becomes inconvenient.
Is there a quick way to open a file with same name as current file, but a different extension?
Do other people come across this situation too, and if so what is your preferred way of navigating the C++ files in a directory? Thanks.
You can use the :r (root) filename modifier which removes the last extension (check out :h filename-modifiers for more information)
:e %:r.cpp
where
% is shorthand for current filename.
:r removes the extension
.cpp simply appends that string at the end.
This effectively substitutes the current file's extension with another, then open the file with the newer extension.
An even shorter way (courtesy of Peter Rincker),
:e %<.cpp
Relevant documentation at :h extension-removal
According to the Vim wiki there are quite a few suggested ways.
I will outline a few options from the article:
a.vim or FSwitch.vim plugins
using ctags
:e %<.c or :e %<.h. %< represents the current file w/o the extension
A quick mapping nnoremap <F4> :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>. Add this to your ~/.vimrc.
Install “unimpaired” and then use ]f and [f to go the previous and next file. Since source and header have they same name except for the suffix, they are next and previous files.
This is just using simple(?!) vimscript, so you can put it into your vimrc,
now it works for .c files, but can be modified pretty easily for .cpp (obviously), it even has some "error handling" in the inner if-statements (that is probably pointless), but if anyone needs it, hey, it's there! Without it it's way much shorter (just leave the :e %<.h, for example), so choose whatever you want.
function! HeaderToggle() " bang for overwrite when saving vimrc
let file_path = expand("%")
let file_name = expand("%<")
let extension = split(file_path, '\.')[-1] " '\.' is how you really split on dot
let err_msg = "There is no file "
if extension == "c"
let next_file = join([file_name, ".h"], "")
if filereadable(next_file)
:e %<.h
else
echo join([err_msg, next_file], "")
endif
elseif extension == "h"
let next_file = join([file_name, ".c"], "")
if filereadable(next_file)
:e %<.c
else
echo join([err_msg, next_file], "")
endif
endif
endfunction
then add further to your vimrc something along these lines:
let mapleader = "," " <Leader>
nnoremap <Leader>h :call HeaderToggle()<CR>
Now whenever you're in normal mode, you press comma , (this is our <Leader> button) then h and function from the above gets called, and you will toggle between files. Tada!
Adding my two cents ;) to the above great answers:
Install Exuberant Ctags
Put the following code into your .vimrc
" Jump to a file whose extension corresponds to the extension of the current
" file. The `tags' file, created with:
" $ ctags --extra=+f -R .
" has to be present in the current directory.
function! JumpToCorrespondingFile()
let l:extensions = { 'c': 'h', 'h': 'c', 'cpp': 'hpp', 'hpp': 'cpp' }
let l:fe = expand('%:e')
if has_key(l:extensions, l:fe)
execute ':tag ' . expand('%:t:r') . '.' . l:extensions[l:fe]
else
call PrintError(">>> Corresponding extension for '" . l:fe . "' is not specified")
endif
endfunct
" jump to a file with the corresponding extension (<C-F2> aka <S-F14>)
nnoremap <S-F14> :call JumpToCorrespondingFile()<CR>
inoremap <S-F14> <C-o>:call JumpToCorrespondingFile()<CR>
" Print error message.
function! PrintError(msg) abort
execute 'normal! \<Esc>'
echohl ErrorMsg
echomsg a:msg
echohl None
endfunction
https://github.com/ericcurtin/CurtineIncSw.vim is an option.
Once configured searches the current directory recursively and the directory your source file is in recursively for the file you want to switch to.
You can switch from .cc to .h files with :VH.
I am new to Vim and trying to add a new shortcut. I was wondering how I can put current file's path to the command dynamically. So that every time I use that shortcut, my command will executed with correct filepath in it.
What you are looking for is expand("%"). It will return the file you are currently editing. If you use expand("%:p") you will get the full path of that file. So say you would want to have a shortcut to print your current file in the command bar and you wanted it mapped to F5. Then you would add the following in your .vimrc:
map <F5> :echo expand("%:p")<CR>
nnoremap <expr> <leader>cd (expand("%:p:h") !~ '^/tmp') ? ":lcd %:p:h\<CR>:echo expand(\"%:p:h\")\<CR>" : "echo \"foo\"\<CR>"
I have this line in my .vimrc to what you want. My leader is ',' so when I type ,cd it changes the local directory to that of the current file.
Assuming the current buffer is a file open for edit, so :e does not display E32: No file name.
I would like to yank one or all of:
The file name exactly as show on the status line, e.g. ~\myfile.txt
A full path to the file, e.g. c:\foo\bar\myfile.txt
Just the file name, e.g. myfile.txt
TL;DR
:let #" = expand("%")>
this will copy the file name to the unamed register, then you can use good old p to paste it. and of course you can map this to a key for quicker use.
:nmap cp :let #" = expand("%")<cr>
you can also use this for full path
:let #" = expand("%:p")
Explanation
Vim uses the unnamed register to store text that has been deleted or copied (yanked), likewise when you paste it reads the text from this register.
Using let we can manually store text in the register using :let #" = "text" but we can also store the result of an expression.
In the above example we use the function expand which expands wildcards and keywords. in our example we use expand('%') to expand the current file name. We can modify it as expand('%:p') for the full file name.
See :help let :help expand :help registers for details
Almost what you're asking for, and it might do: in INSERT mode, Ctrl+R % pulls the current filename into where you are (command prompt, edit buffer, ...). See this Vim Tip for more.
If you want to put the current buffer filename in your system-level clipboard, try changing the register to #+:
" relative path
:let #+ = expand("%")
" full path
:let #+ = expand("%:p")
" just filename
:let #+ = expand("%:t")
Edit 20140421:
I commonly use these, so I created some shortcuts. Linux Vims apparently operate slightly differently than Mac Vims, so there is a special case for that as well. If you put the following in your ~/.vimrc:
" copy current file name (relative/absolute) to system clipboard
if has("mac") || has("gui_macvim") || has("gui_mac")
" relative path (src/foo.txt)
nnoremap <leader>cf :let #*=expand("%")<CR>
" absolute path (/something/src/foo.txt)
nnoremap <leader>cF :let #*=expand("%:p")<CR>
" filename (foo.txt)
nnoremap <leader>ct :let #*=expand("%:t")<CR>
" directory name (/something/src)
nnoremap <leader>ch :let #*=expand("%:p:h")<CR>
endif
" copy current file name (relative/absolute) to system clipboard (Linux version)
if has("gui_gtk") || has("gui_gtk2") || has("gui_gnome") || has("unix")
" relative path (src/foo.txt)
nnoremap <leader>cf :let #+=expand("%")<CR>
" absolute path (/something/src/foo.txt)
nnoremap <leader>cF :let #+=expand("%:p")<CR>
" filename (foo.txt)
nnoremap <leader>ct :let #+=expand("%:t")<CR>
" directory name (/something/src)
nnoremap <leader>ch :let #+=expand("%:p:h")<CR>
endif
Then for example <leader>cf will copy the relative path of the current buffer (the default leader is backslash (\)). I often use these for running commands on a file or doing other things on the command line. I don't really use the last filename / directory name often.
You might consider more intuitive mappings like <leader>cfr for relative, <leader>cfa for absolute, <leader>cff for just filename, <leader>cfd for directory.
Answer tested on Neovim/Ubunutu.
:let #+=#%
From what I can tell, the % register already contains the relative filepath, so it's as simple as moving the contents of the % register to whatever register represents your favourite clipboard.
This SO answer deals with copying from one register to another
Seems pretty straightforward to me. No need for any hard-to-remember expand() stuff.
If you do :reg you will see the name of the current file in the % register. You can paste it with "%p, for example.
If, like me, you often switch to the 'alternate' buffer, it is very handy that its full path-and-file-name are put in the # register. You can paste it with "#p, for example.
Note (just in case this is behaviour specific to my setup): I am using VIM 7.4.52 on Ubuntu 14.04.4 LTS.
Combining information from a couple of other answers: If you want to yank the current full path to a file and put it into the command buffer in another window, first do :let #" = expand("%:p"), then move to another window and type Ctrl+R ".
Useful for copying a file while staying in the same directory and keeping the old one open. For example:
Start: Editing src/com/benatkin/paint/shapes/Circle.java
Type :let #" = expand("%:p") (The path gets yanked to the main clipboard buffer.)
Open a new window with :sp
Type :e Ctrl+R"
Use the arrow keys to go back to Circle and change it to Square, and press <CR>
End: Editing src/com/benatkin/paint/shapes/Square.java
Here is my solution:
" filename / dirname of the current file {{{
" copy result to the system clipboard and echo the result
" the cb> prompt means the clipboard
" *f*ile *n*ame, ex. init.vim
map <Leader>fn :let #+ = expand("%:t") \| echo 'cb> ' . #+<CR>
" *f*ile *p*ath, ex. /home/user/nvim/init.vim
map <Leader>fp :let #+ = expand("%:p") \| echo 'cb> ' . #+<CR>
" *d*irectory *p*ath, ex. /home/user/nvim
map <Leader>dp :let #+ = expand("%:p:h") \| echo 'cb> ' . #+<CR>
" *d*irectory *n*ame, ex. nvim
map <Leader>dn :let #+ = expand("%:p:h:t") \| echo 'cb> ' . #+<CR>
" }}}
If you're on terminal vim, and want to copy to system clipboard: For something easy to remember, that doesn't requiring predefined mappings/functions:
:!echo %
prints the current buffer's relative path to terminal, where you can copy it and then ENTER back to vim. (as already mentioned, you can postfix the command with :p or :t to get absolute or basename, if you can remember that....)
I put it in my vimc:
nnoremap yl :let #" = expand("%:p")<cr>
I use xclip to access X's clipboard, so I use:
nmap <localleader>d :call system("xclip -i -selection clipboard", expand("%:p"))<CR>
If you wish to simply yank the path of the currently open file name then simply press:
step1: ctrl + g [You will see the entire root path at the bottom of window]
step2: select the path with the mouse
step3: Paste with the middle mouse wheel