Is there a way to rename the terminal buffer? using :b to switch buffers, terminal buffers are often shown as !/usr/local/bin/fish and !/usr/local/bin/fish (1) which isn't very useful. Ideally It could auto rename itself, but I'm also okay with manually naming them (i.e. docker-compose up) after I start the job with in a terminal.
Here's a function I made for it. Disclaimer: I'm no Tim Pope
function! RenameTerminalBufferToCurrentCommand()
" unable to access $HISTFILE from vim, so change this variable to your history file
let l:historyFile = "~/.zsh_history"
let l:mostRecentCommand = system("tail -1 " . l:historyFile . " | cut -f2- -d\\;")
" i prepend "term" for easy buffer searching, but feel free to delete
let l:newFileName = "term " . fnameescape(trim(l:mostRecentCommand))
" the keepalt stops :file from creating an alternative file (alt files are
" annoying when buffer switching)
" :file renames the buffer
silent! execute "keepalt file " . l:newFileName
endfunction
tnoremap <Enter> <Enter><C-\><C-n>:call RenameTerminalBufferToCurrentCommand()<Enter>a
https://unix.stackexchange.com/questions/3497/how-to-change-the-name-of-a-vim-buffer
https://www.reddit.com/r/unix/comments/9u7le4/running_the_zsh_history_command_to_load_result/e928r7y/
https://www.reddit.com/r/vim/comments/7bcrkf/what_to_do_with_unlisted_buffers/
Related
I have some specific parts of files with unformated xml code. I need to write a vimscript function that selects the text and calls xmllint over it.
I know I can do this on the command line :'<,'>!xmllint --format -
But I really need to do the same in a vimscript function and I don't know how to make something like normal! call for visual.
I tried this but it does not work correctly :
function! MyFormat()
... stuff done here
let startl = line("'<")
let endl = line("'>")
let line = getline(startl, endl)
let r = system('echo "' . join(line, "") . '" | xmllint --format -')
call setline('.', r)
endfunction
Every line in a Vim script is an Ex command. Since you already have a working Ex command, you might as well use it.
function! MyFormat()
" ... stuff done here
'<,'>!xmllint --format -
" ... more stuff done here
endfunction
But, again, data is missing so this might work… or not, be sufficient… or not, etc.
In Vim, is there a way to make :b list the buffer numbers next to open buffers, in a way similar to the :ls command but without having to retype :b afterwards?
This great mapping, popularized by a most valuable member of the community, does exactly what you want:
nnoremap gb :ls<CR>:b
A more generic approach…
Vim uses that non-interactive list to display the result of a number of other useful commands. I've written this simple function to insert the right "stub" each time I press <CR> after one of those commands:
function! CmdCR()
" grab the content of the command line
let cmdline = getcmdline()
if cmdline =~ '\C^ls'
" like :ls but prompts for a buffer command
return "\<CR>:b"
elseif cmdline =~ '/#$'
" like :g//# but prompts for a command
return "\<CR>:"
elseif cmdline =~ '\v\C^(dli|il)'
" like :dlist or :ilist but prompts for a count for :djump or :ijump
return "\<CR>:" . cmdline[0] . "jump " . split(cmdline, " ")[1] . "\<S-Left>\<Left>"
elseif cmdline =~ '\v\C^(cli|lli)'
" like :clist or :llist but prompts for an error/location number
return "\<CR>:silent " . repeat(cmdline[0], 2) . "\<Space>"
elseif cmdline =~ '\C^old'
" like :oldfiles but prompts for an old file to edit
return "\<CR>:edit #<"
else
" default to a regular <CR>
return "\<CR>"
endif
endfunction
cnoremap <expr> <CR> CmdCR()
in vim, when I use
:make
the output of make is displayed in a "external" window, I don't like this and I use this map
nnoremap <leader>m :w <bar> make<CR><CR><CR>:copen<CR>
but, in some case the output of make is
make: Nothing to be done for `all'.
how I can add a autoclose to copen when the copen have make: Nothing to be done for all. ?
You can check the contents of the quickfix list via getqflist(). Then, I would only conditionally open the quickfix window if the first line does not match the text you don't want to see:
nnoremap <leader>m :w <bar> make<CR><CR><CR>
\:if get(get(getqflist(), 0, {}), 'text', '') !~# 'Nothing to be done' <Bar>
\ copen <Bar>
\endif<CR>
The access via get() avoids errors when the list is empty.
You could also always open the list, and then use :cclose in the conditional, if that better fits your needs.
i have a vim script that helps me a lot:
first it saves the current edit window (if possible, won't work if read only)
then it runs make and redirects errors (stderr) to temp file (stdout is ignored)
if build failed then open quick fix windows and fill it with error messages.
delete temporary file
command! -nargs=* Build call s:RunBuild()
function! s:RunBuild()
let tmpfile = tempname()
"build and surpresses build status messages to stdout
"(stdout message are not very informative and may be very very long)
"Error messages are redirected to temporary file.
let buildcmd = "make -j 2> " . tmpfile . " >/dev/null"
let fname = expand("%")
if fname != ""
" save current buffer if possible (your bad if file is read only)
write
endif
" --- run build command ---
echo "Running make ... "
let cmd_output = system(buildcmd)
"if getfsize(tmpfile) == 0
if v:shell_error == 0
cclose
execute "silent! cfile " . tmpfile
echo "Build succeded"
else
let old_efm = &efm
set efm=%f:%l:%m
execute "silent! cfile " . tmpfile
let &efm = old_efm
botright copen
endif
call delete(tmpfile)
endfunction
Then i map the F5 key to do the build in each vim mode
"F5 - run make (in normal mode)
:nnoremap :Build
"F5 - run make (in visual mode)
:vnoremap :Build
"F5 - run make (in insert mode)
:inoremap :Build
How can I redirect or pipe the output of an ex command into my current buffer or a file?
For example, I want to read the contents of all the registers into the current buffer, which in ex mode is shown using :registers.
:redir >name_of_registers_file
:registers
:redir END
:r name_of_registers_file
:help redir
The last command is very useful, since there are lots of options for redirection: to variables, to registers, how to append, further cornucopia.
I still find it weird and annoying that it uses END that way, but since everything else that can follow redir has to start with a non-word-character, at least it's not ambiguous.
PS AFAIK (which is pretty far in this case) there's no way to read it directly into the buffer: you have to store it in a register or a variable first. Check the help for the various options of how to do that.
PPS If you do want to do this using a variable —maybe to encapsulate it in a function and avoid clobbering registers or global variables— you'll have to convert the multiline string that gets written to the variable into a list. EG
:call append( '.', split(variable_you_redirected_to, "\n") )
Otherwise (if you just do append('.',var)) you end up with ^#'s (nulls) instead of newlines, since that's what vimscript uses to represent newlines in String variables.
edit: as #Bill Odom mentions, using :put =variable_you_redirected_to is a lot easier than the complicated append() expression. Thanks, Bill!
PPPS
I've written a snippet to make this stuff more convenient. It declares a function Redir(command, target) and a command R.
The command parses the last series of non-space characters as a redirection target and passes that to the function, which does the boilerplate to redirect the command output to the redirection target.
The command is everything after R and before the last space.
EG
" Store the vim buffer list in buffer_list.txt
:R ls >buffer_list.txt
" Store error messages emitted by a function being debugged
" in the 'unnamed register'
:R call FunctionBeingDebugged() #">
There are a few limitations with this: for example you won't be able to write to a filename that contains a space. The upside to this is that you don't have to quote your command. I've got it posted on gist.github.com, and I'll try to keep it updated if I end up improving it. Or you can fork it yourself</noeuphemism>!
Anyway the snippet is available here. It can be dropped into a .vimrc file or into a file in ~/.vim/plugins.
#intuited is right; the redir command is what you want. A one-liner like this will insert the output of :registers into the current buffer:
redir => m | silent registers | redir END | put=m
That's not something you'll want to type very often, however, and it's not exactly amenable to a key map. I found myself doing this fairly often, so I wrote a function and a handful of commands to make it easier. As a bonus, I can now send command output to a new window or new tab as easily as inserting it into the current buffer. Here's the code (with a few command examples at the very end):
" redir_messages.vim
"
" Inspired by the TabMessage function/command combo found
" at <http://www.jukie.net/~bart/conf/vimrc>.
"
function! RedirMessages(msgcmd, destcmd)
"
" Captures the output generated by executing a:msgcmd, then places this
" output in the current buffer.
"
" If the a:destcmd parameter is not empty, a:destcmd is executed
" before the output is put into the buffer. This can be used to open a
" new window, new tab, etc., before :put'ing the output into the
" destination buffer.
"
" Examples:
"
" " Insert the output of :registers into the current buffer.
" call RedirMessages('registers', '')
"
" " Output :registers into the buffer of a new window.
" call RedirMessages('registers', 'new')
"
" " Output :registers into a new vertically-split window.
" call RedirMessages('registers', 'vnew')
"
" " Output :registers to a new tab.
" call RedirMessages('registers', 'tabnew')
"
" Commands for common cases are defined immediately after the
" function; see below.
"
" Redirect messages to a variable.
"
redir => message
" Execute the specified Ex command, capturing any messages
" that it generates into the message variable.
"
silent execute a:msgcmd
" Turn off redirection.
"
redir END
" If a destination-generating command was specified, execute it to
" open the destination. (This is usually something like :tabnew or
" :new, but can be any Ex command.)
"
" If no command is provided, output will be placed in the current
" buffer.
"
if strlen(a:destcmd) " destcmd is not an empty string
silent execute a:destcmd
endif
" Place the messages in the destination buffer.
"
silent put=message
endfunction
" Create commands to make RedirMessages() easier to use interactively.
" Here are some examples of their use:
"
" :BufMessage registers
" :WinMessage ls
" :TabMessage echo "Key mappings for Control+A:" | map <C-A>
"
command! -nargs=+ -complete=command BufMessage call RedirMessages(<q-args>, '' )
command! -nargs=+ -complete=command WinMessage call RedirMessages(<q-args>, 'new' )
command! -nargs=+ -complete=command TabMessage call RedirMessages(<q-args>, 'tabnew' )
" end redir_messages.vim
:redir #a
:registers
:redir END
"ap
:redir #a redirects all messages from here on to a register named a. You follow this with your command whose output you want to capture (:registers in your case). :redir END ends the redirection. "ap means, "a uses the register named a and p puts the contents of the selected register into the current buffer.
See Capture ex command output at Vim Tips Wiki for more information :-)
In addition, :h :redir:
...To get the output of one command the |execute()| function can be used.
e.g.
put = execute('scriptnames')
put = execute('filter /vimfiles/ scriptnames')
It is not necessary to use a temporary variable as long as you can save the current buffer and are ok with appending the messages to the end of the current file.
From the Vim documentation:
:redi[r] >> {file} Redirect messages to file {file}. Append if {file}
already exists. {not in Vi}
http://vimdoc.sourceforge.net/htmldoc/various.html#:redir
So, to append the messages from :registers to the bottom of the current file, do this:
:write | redir >> % | silent registers | redir END | edit
:write the file so that any changes won't be lost
Begin redirecting output to %, the name of the current file.
Silently run the :registers command.
END redirecting to the file.
:edit the file to see the new changes.
You can also forcefully truncate the current file and replace it with the output messages:
:redir! > % | silent registers | redir END | edit!
But that is probably not what you want to do.
Unfortunately I have no rep, so I cannot add this as a comment to 'Francis Niu'.
The execute() function looks like the easiest way to accomplish this, and it was added in vim 8 .
Listed in the new/extended features on vim's github. version8.txt
My question is similar to this how to monitor a text file in realtime
but I want to do it in vim. I know I can read an opened file use tail -f sample.xml file, and when new content is written to the file, it'll also write the new content to my screen. Can I have vim automatically fill the new data when a file is updated?
You can :set autoread so that vim reads the file when it changes. However (depending on your platform), you have to give it focus.
From the help:
When a file has been detected to have
been changed outside of Vim and it
has not been changed inside of Vim,
automatically read it again. When the
file has been deleted this is not
done.
Don't know about automatically, but you can type:
:e!
to reload the file
Put the following in your .vimrc:
" check one time after 4s of inactivity in normal mode
set autoread
au CursorHold * checktime
like #flukus said in a comment to a previous answer you can call feedkeys["lh"] (it moves the cursor to the right and back to the left, which normaly doesn't do harm when viewing a log file)
So, if you combine the rest of the answer you have a oneliner you can run from ex (whithin vim) when needed:
:set autoread | au CursorHold * checktime | call feedkeys("lh")
***(if you would want to jump (nearly) to the end of the file, just use "G" instead of "lh" with feedkeys)***
Explanation:
autoread: reads the file when changed from the outside (but it doesnt work on its own, there is no internal timer or something like that. It will only read the file when vim does an action, like a command in ex :!
CursorHold * checktime: when the cursor isn't moved by the user for the time specified in 'updatetime' (which is 4000 miliseconds by default) checktime is executed, which checks for changes from outside the file
call feedkeys("lh"): the cursor is moved once, right and back left. and then nothing happens (... which means, that CursorHold is triggered, which means we have a loop)
Additionally you can :set syntax=logtalk to color the log
To stop the scrolling when using call feedkeys("G"), execute :set noautoread - now vim will tell, that the file was change ans ask if one wants to read the changes or not)
(Does this have any sideeffects?)
Edit: I see one side-effect: if one uses "G" as the feedkey, it will scroll down every currently opened buffer?! So, it's not possible to work in the left buffer of a splittet window while having the right buffer scroll down a logfile automatically
Edit2: Another side effect is that when you enter the command line window(by using q:) an error message always pops up.
Stick this in your .vimrc and it should work like a boss. (Taken from: http://vim.wikia.com/wiki/Have_Vim_check_automatically_if_the_file_has_changed_externally)
" Function to Watch for changes if buffer changed on disk
function! WatchForChanges(bufname, ...)
" Figure out which options are in effect
if a:bufname == '*'
let id = 'WatchForChanges'.'AnyBuffer'
" If you try to do checktime *, you'll get E93: More than one match for * is given
let bufspec = ''
else
if bufnr(a:bufname) == -1
echoerr "Buffer " . a:bufname . " doesn't exist"
return
end
let id = 'WatchForChanges'.bufnr(a:bufname)
let bufspec = a:bufname
end
if len(a:000) == 0
let options = {}
else
if type(a:1) == type({})
let options = a:1
else
echoerr "Argument must be a Dict"
end
end
let autoread = has_key(options, 'autoread') ? options['autoread'] : 0
let toggle = has_key(options, 'toggle') ? options['toggle'] : 0
let disable = has_key(options, 'disable') ? options['disable'] : 0
let more_events = has_key(options, 'more_events') ? options['more_events'] : 1
let while_in_this_buffer_only = has_key(options, 'while_in_this_buffer_only') ? options['while_in_this_buffer_only'] : 0
if while_in_this_buffer_only
let event_bufspec = a:bufname
else
let event_bufspec = '*'
end
let reg_saved = #"
"let autoread_saved = &autoread
let msg = "\n"
" Check to see if the autocommand already exists
redir #"
silent! exec 'au '.id
redir END
let l:defined = (#" !~ 'E216: No such group or event:')
" If not yet defined...
if !l:defined
if l:autoread
let msg = msg . 'Autoread enabled - '
if a:bufname == '*'
set autoread
else
setlocal autoread
end
end
silent! exec 'augroup '.id
if a:bufname != '*'
"exec "au BufDelete ".a:bufname . " :silent! au! ".id . " | silent! augroup! ".id
"exec "au BufDelete ".a:bufname . " :echomsg 'Removing autocommands for ".id."' | au! ".id . " | augroup! ".id
exec "au BufDelete ".a:bufname . " execute 'au! ".id."' | execute 'augroup! ".id."'"
end
exec "au BufEnter ".event_bufspec . " :checktime ".bufspec
exec "au CursorHold ".event_bufspec . " :checktime ".bufspec
exec "au CursorHoldI ".event_bufspec . " :checktime ".bufspec
" The following events might slow things down so we provide a way to disable them...
" vim docs warn:
" Careful: Don't do anything that the user does
" not expect or that is slow.
if more_events
exec "au CursorMoved ".event_bufspec . " :checktime ".bufspec
exec "au CursorMovedI ".event_bufspec . " :checktime ".bufspec
end
augroup END
let msg = msg . 'Now watching ' . bufspec . ' for external updates...'
end
" If they want to disable it, or it is defined and they want to toggle it,
if l:disable || (l:toggle && l:defined)
if l:autoread
let msg = msg . 'Autoread disabled - '
if a:bufname == '*'
set noautoread
else
setlocal noautoread
end
end
" Using an autogroup allows us to remove it easily with the following
" command. If we do not use an autogroup, we cannot remove this
" single :checktime command
" augroup! checkforupdates
silent! exec 'au! '.id
silent! exec 'augroup! '.id
let msg = msg . 'No longer watching ' . bufspec . ' for external updates.'
elseif l:defined
let msg = msg . 'Already watching ' . bufspec . ' for external updates'
end
echo msg
let #"=reg_saved
endfunction
let autoreadargs={'autoread':1}
execute WatchForChanges("*",autoreadargs)
Tail Bundle should do what you want. Note, haven't used it myself.
There is a plugin also:
https://github.com/djoshea/vim-autoread
This was the only way I could make this work on OSX.
If unix + neovim
:term tail -f <filename>
Obviously this won't work for everyone, but it's how I do it.
To enable reloading if you open a specific file, you can add this to your .vimrc and use a modeline like vim: set ft+=.watch:. It takes advantage of the feature to set multiple filetypes for a buffer (see below):
vim9script
command WatchFiles {
autocmd! AUWatchFile FocusGained,VimResume,BufEnter,WinEnter,CursorHold * checktime
autocmd! AUWatchFile BufEnter,InsertEnter,CursorHold,CursorHoldI <buffer> checktime
setlocal autoread
checktime
}
command UnwatchFiles {
autocmd! AUWatchFile
set autoread<
}
# To enable this, you may use e.g. a modeline: `vim: set ft+=.watch`
def WatchAutomatically()
# Check if the "list" of filetypes (a dot separated string) contains 'watch'.
if -1 != match(&filetype, "\\(^\\|\\.\\)watch\\($\\|\\.\\)")
WatchFiles
endif
enddef
augroup AUWatchFile | augroup END
autocmd BufWinEnter * call WatchAutomatically()
More details
You are able to set multiple filetypes separated by .:
When a dot appears in the value then this separates two filetype
names. Example:
/* vim: set filetype=c.doxygen : */ ~
See :help 'filetype'.
Keep in mind that you should manipulate the filetype first and set additional options after that in the modeline. Otherwise these options may be overridden by the settings specific for the filetype.
Btw, the above is a Vim9 script (which I discovered today). Translating it back to a good-old Vim script is trivial:
Use " for comments.
Transform multi-line commands like this:
command UnwatchFiles
\ autocmd! AUWatchFile
\ | set autoread<
Remove the vim9script line.
See :help Vim9-script for details.
Shortcomings
Reloading is not limited to the buffer which contains the modeline. You may disable it again with :UnwatchFiles.
It is still dump polling.
VIM will warn you when a file has been updated so that you don't overwrite changes that have been made since you opened it. It will prompt you at that point to reload the file.