Vim: how to :source a part of the buffer - vim

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>

Related

Vim print buffer names to stdout on :q

Let me jump right in.
What I'm trying to do is simply print out the file path of any open buffer when I exit vim. This is useful because I often open other files in vim buffers with a vim script I wrote that can search through my codebase for a specific function call.
I figure I can set up an autocommand, either for when I open a file or when I leave vim, and use the output from :ls to list all currently open buffers. The problem that I'm having is that I can't get any output to show up in terminal. I have tried various combinations of :!echo in my function, but to no avail. I have been trying something like the following in my .vimrc
function! PrintFileName()
:!echo "hello"
:exec "!echo world"
"... etc
endfunction
au BufRead * call PrintFileName()
Both :!echo foobar and :call PrintFileName() work for me if I do it from the command line. I also figure I might have to use some form of silent/redraw! so I don't have to hit enter to continue.
Really the main problem here is that I can't see a way to get output to stdout from inside my function and called by an autocommand.
Thanks for any help.
Okay, so I've found this solution, which works as long as I enter vim from the last line of my terminal. Otherwise this prints out a line below the current line and will get overwritten when you press enter. If anyone knows how to fix that let me know, otherwise I will use this.
function! PrintBuffers()
redir => files
:ls
redir END
" Regex to strip out everything from :ls but the buffer filenames
let files = substitute(files, '^[^"]*"', '', 'g')
let files = substitute(files, '"[^"]*\n[^"]*"', '\n', 'g')
let files = substitute(files, '"[^"]*$','','g')
" This is the magic line
exe '!echo; echo ' . shellescape(&t_te . files)
endfunction
au VimLeave * call PrintBuffers()
*Note - As I'm writing this, I realize that this won't display the right path if you did a :cd at some point. So I guess its pretty fragile, but it does the job.

Golang Formatter and Vim - How to destroy history record?

Go (Golang) programming language comes with a tool called go fmt. Its a code formatter, which formats your code automagically (alignments, alphabetic sorting, tabbing, spacing, idioms...). Its really awesome.
So I've found this little autocommand which utilizes it in Vim, each time buffer is saved to file.
au FileType go au BufWritePre <buffer> Fmt
Fmt is a function that comes with Go vim plugin.
This is really great, but it has 1 problem. Each time formatter writes to buffer, it creates a jump in undo/redo history. Which becomes very painful when trying to undo/redo changes, since every 2nd change is formatter (making cursor jump to line 1).
So I am wondering, is there any way to discard latest change from undo/redo history after triggering Fmt?
EDIT:
Ok, so far I have:
au FileType go au BufWritePre <buffer> undojoin | Fmt
But its not all good yet. According to :h undojoin, undojoin is not allowed after undo. And sure enough, it fires an error when I try to :w after an undo.
So how do I achieve something like this pseudo-code:
if lastAction != undo then
au FileType go au BufWritePre <buffer> undojoin | Fmt
end
If I get this last bit figured out, I think I have a solution.
I think this is almost there, accomplishes what you ask, but I see it's deleting one undo point (I think this is expected from undojoin):
function! GoFmt()
try
exe "undojoin"
exe "Fmt"
catch
endtry
endfunction
au FileType go au BufWritePre <buffer> call GoFmt()
EDIT
Based on MattyW answer I recalled another alternative:
au FileType go au BufWritePre <buffer> %!gofmt
:%!<some command> executes a shell command over the buffer, so I do it before writing it to file. But also, it's gonna put the cursor at top of file...
Here is my go at this. It seems to be working well both with read/write autocmds and bound to a key. It puts the cursor back
and doesn't include the top-of-file event in the undos.
function! GoFormatBuffer()
if &modifiable == 1
let l:curw=winsaveview()
let l:tmpname=tempname()
call writefile(getline(1,'$'), l:tmpname)
call system("gofmt " . l:tmpname ." > /dev/null 2>&1")
if v:shell_error == 0
try | silent undojoin | catch | endtry
silent %!gofmt -tabwidth=4
endif
call delete(l:tmpname)
call winrestview(l:curw)
endif
endfunction
I check modifiable because I use vim as my pager.
I attempted to use #pepper_chino's answer but ran into issues where if fmt errors then vim would undo the last change prior to running GoFmt. I worked around this in a long and slightly convoluted way:
" Fmt calls 'go fmt' to convert the file to go's format standards. This being
" run often makes the undo buffer long and difficult to use. This function
" wraps the Fmt function causing it to join the format with the last action.
" This has to have a try/catch since you can't undojoin if the previous
" command was itself an undo.
function! GoFmt()
" Save cursor/view info.
let view = winsaveview()
" Check if Fmt will succeed or not. If it will fail run again to populate location window. If it succeeds then we call it with an undojoin.
" Copy the file to a temp file and attempt to run gofmt on it
let TempFile = tempname()
let SaveModified = &modified
exe 'w ' . TempFile
let &modified = SaveModified
silent exe '! ' . g:gofmt_command . ' ' . TempFile
call delete(TempFile)
if v:shell_error
" Execute Fmt to populate the location window
silent Fmt
else
" Now that we know Fmt will succeed we can now run Fmt with its undo
" joined to the previous edit in the current buffer
try
silent undojoin | silent Fmt
catch
endtry
endif
" Restore the saved cursor/view info.
call winrestview(view)
endfunction
command! GoFmt call GoFmt()
I just have this in my .vimrc:
au BufWritePost *.go !gofmt -w %
Automatically runs gofmt on the file when I save. It doesn't actually reformat it in the buffer so it doesn't interrupt what I'm looking at, but it's correctly formatted on disk so all check ins are properly formatted. If you want to see the correctly formatted code looks like you can just do :e .
Doesn't do anything to my undo/redo history either
You can install the vim plugins from the default repository. Alternatively, a pathogen friendly mirror is here:
https://github.com/jnwhiteh/vim-golang
Then you can use the :Fmt command to safely do a go fmt!

In Vim how to switch quickly between .h and .cpp files with the same name?

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.

Execute a script directly within vim/mvim/gvim

TextMate has a nice feature that allows you to execute a script from within the current context and shows you the output in a separate window. This lets you write and test code on the go. I'm almost certain there is a similar feature with MacVim/gVIM, but I'm not sure what it is. Currently I save my buffers to disk, then go to the command line and execute the script in that respect. How do I improve that workflow with vim?
You can do this in vim using the ! command. For instance to count the number of words in the current file you can do:
:! wc %
The % is replaced by the current filename. To run a script you could call the interpreter on the file - for instance if you are writing a perl script:
:! perl %
vim tutorial: Mapping keys in Vim
You can map keys so perl executes current script as suggested by jts above.
map <C-p> :w<CR>:!perl %<CR>
will map Ctrl+P to write file and run it by perl
imap <C-p> <Esc>:w<CR>:!perl %<CR>
lets you call the same in insert mode.
You should have .vimrc (_vimrc for Windows) file in your vim/home folder. It has instructions on how vim should behave.
map <C-p> :w<CR>:!perl %<CR> is just instruction to map Ctrl+p to:
a) write current the file :w
b) run command (perl) using % (currently open file) as parameter :!perl %
<CR> after each command stands for "carriage return": an instruction to execute specific command. imap does the same as map but listens Ctrl+p while in insert mode.
You could run it right from vim:
:!./script.sh
All suggestions here merely showcased :!{cmd} %, which passes current buffer to the shell cmd.
But there is another option :write !{cmd}
For example, the effect of the :write !sh command is that each line of the current buffer is executed in the shell.It is often useful, when for instance you've added a couple of lines to you buffer, and want to see execution result immediately without saving the buffer first.Also it is possible to execute some range, rather than whole content of the buffer::[range]write !{cmd}
save the file and call the script using an interpreter
eg.:
:!python %
It sounds like you're looking for !:
:!{cmd} Execute {cmd} with the shell.
You can use % to denote the current filename, if you need to pass it to the script:
!proofread-script %
You can also use ! with a range, to use the command as a filter:
!{motion}{filter} " from normal mode
:{range}!{filter} " from command mode
(In the first case, as with many other commands, when you type the motion, it'll pass you into command mode, converting the motion into a range, e.g. :.,.+2!)
And finally, if you don't actually need to pass input from your file, but want the output in your file, that's essentially a trivial filter, and the fastest way to do it is !!{cmd}. This will replace the current line with the output of the command.
To execute the current executable script, use
:!./%
! executes a shell command, % is the current filename and ./ adds the current dir in front.
Put this small snippet in your .vimrc to execute the current file with one keystroke (like F5) and display the result in a new split-pane buffer.
:! is okay but you need to switch to your terminal to see the result.
While you can do that with ctrl-z and bring vim back with fg it still means you need to switch context a lot.
The way this snippet works is by first guessing the executable based on the filetype and then running it with the current file as its argument.
Next a handy utility method takes the output and dumps it into a new buffer.
It's not perfect, but really fast for common workflows.
Here's the snippet copied below:
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"""""""""""""""""""""""""" RUN CURRENT FILE """""""""""""""""""""""""""""
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Execute current file
nnoremap <F5> :call ExecuteFile()<CR>
" Will attempt to execute the current file based on the `&filetype`
" You need to manually map the filetypes you use most commonly to the
" correct shell command.
function! ExecuteFile()
let filetype_to_command = {
\ 'javascript': 'node',
\ 'coffee': 'coffee',
\ 'python': 'python',
\ 'html': 'open',
\ 'sh': 'sh'
\ }
let cmd = get(filetype_to_command, &filetype, &filetype)
call RunShellCommand(cmd." %s")
endfunction
" Enter any shell command and have the output appear in a new buffer
" For example, to word count the current file:
"
" :Shell wc %s
"
" Thanks to: http://vim.wikia.com/wiki/Display_output_of_shell_commands_in_new_window
command! -complete=shellcmd -nargs=+ Shell call RunShellCommand(<q-args>)
function! RunShellCommand(cmdline)
echo a:cmdline
let expanded_cmdline = a:cmdline
for part in split(a:cmdline, ' ')
if part[0] =~ '\v[%#<]'
let expanded_part = fnameescape(expand(part))
let expanded_cmdline = substitute(expanded_cmdline, part, expanded_part, '')
endif
endfor
botright new
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap
call setline(1, 'You entered: ' . a:cmdline)
call setline(2, 'Expanded Form: ' .expanded_cmdline)
call setline(3,substitute(getline(2),'.','=','g'))
execute '$read !'. expanded_cmdline
setlocal nomodifiable
1
endfunction
Well it depends on your OS - actually I did not test it on M$ Window$ - but Conque is one of the best plugins around there: http://code.google.com/p/conque/
Actually, it can be better, but works. You can embed a shell window in a vim "window".

Yank file name / path of current buffer in Vim

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

Resources