It is easy to use vimscript to determine if a filename was specified to vim by using argc(). Is there a way to determine if the - flag was given to specify piped input was given to vim? It doesn't count piped input as a filename and argc() is empty.
Edit
Thanks to the wonderful accepted answer below, I have a way to open NerdTree if there are no filenames and stndin is not being used.
let wmuse_nt = 0
autocmd StdinReadPost * let wmuse_nt = 1
autocmd vimenter * if !argc() && wmuse_nt == 0 | NERDTree | endif
You could use an autocmd to run something before or after vim reads from stdin with the StdinReadPre or StdinReadPost events. The help is copied below.
StdinReadPost
StdinReadPost After reading from the stdin into the buffer,
before executing the modelines. Only used
when the "-" argument was used when Vim was
started --.
StdinReadPre
StdinReadPre Before reading from stdin into the buffer.
Only used when the "-" argument was used when
Vim was started --.
Related
How can one detect stdin input in .vimrc?
I have the following command in my ~/.vimrc:
autocmd BufWinEnter * silent loadview
to open a file with a cursor at the last line position. However, if I use vim on stdin, I get the following error message:
Error detected while processing BufWinEnter Autocommands for "*":
E32: No file name
Press ENTER or type command to continue
How can one detect that vim is used on stdin in .vimrc to suppress execution of the command in such cases?
Thank you for your help!
v:argv is a list that begins with the complete path of the program, and contains each argument, if any.
If you call Vim like this:
$ vim
:echo v:argv outputs something like:
['/path/to/vim']
If you call Vim like this:
$ vim foo.txt bar.json
:echo v:argv outputs something like:
['/path/to/vim', 'foo.txt', 'bar.json']
If you call Vim like this:
$ echo 'foo bar baz' | vim -
:echo v:argv outputs something like:
['/path/to/vim', '-']
Therefore, you can condition the execution of :loadview to the content of v:argv[1]. Note that we use get() because the index 1 may not exist:
autocmd BufWinEnter * if get(v:argv, 1, '') != '-' | silent loadview | endif
Reference:
:help :get()
:help v:argv
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!
The answer to the question
Using vim Sessions Only With GUI? suggests using
au VimLeave * mksession! ~/.gvimsession
au VimEnter * source ~/.gvimsession
My problem is when I start Vim, say, by issuing $ gvim test.html, the test.html file is loaded into a buffer that is not shown.
How can I test if arguments where passed and not execute the au VimEnter in such a case?
Or, alternatively, how can I switch to the buffer holding the file with the given file name.
One can test whether there are any command-line arguments (using the
argc() function) and load the previously saved session only when
there are not any:
:autocmd VimEnter * if argc() == 0 | source ~/.gvimsession | endif
what I am using now is ,
autocmd BufWritePost *.py !python PythonTidy.py % %
It really call the tidy programe and change the file, but the vim does not reload the new file.
And I do not want to install another plugin for it.
=======================
note: I found it's dangerous about this feature, PythonTidy will will output a empty file if the command faild, it means if you have syntax error, you will lose your file unless press "u" to get it,but you can't save before you fix syntax error.
I call :!PythonTidy % % manually after pylint complete now.
Use BufWritePre instead of BufWritePost and combine Vim range filtering with PythonTidy’s stdin/stdout mode.
autocmd FileType python autocmd BufWritePre <buffer> let s:saveview = winsaveview() | exe '%!python PythonTidy.py' | call winrestview(s:saveview) | unlet s:saveview
(The use of autocmd FileType python autocmd BufWritePre <buffer> makes this a bit more accurate than matching on a glob pattern: it means “any time a Python file is detected, install this autocmd for that buffer” – so it works independently of file name.)
Unfortunately this cannot preserve your cursor position if you undo the filtering. (You are filtering a whole-file range; when undoing a filter operation, the cursor jumps to the first line in the range; so you end up at the top of the file.) I was hoping to find a way to create a no-op undo state, before, so you could hit u twice and get back to the right place, but I can’t make that work as yet. Maybe someone else knows how.
hi the following fixed the cursor postion problem
function! PythonTidySaver()
let oldpos=getpos('.')
%!PythonTidy
call setpos('.',oldpos)
endfunction
autocmd! bufwritepost *.py call PythonTidySaver()
Based on :help :e:
*:e* *:edit*
:e[dit] [++opt] [+cmd] Edit the current file. This is useful to re-edit the
current file, when it has been changed outside of Vim.
This fails when changes have been made to the current
buffer and 'autowriteall' isn't set or the file can't
be written.
Also see |++opt| and |+cmd|.
{Vi: no ++opt}
So you'd need to use :e after updating the file externally. However, :! doesn't let you use | normally (see :help :!), so you need to wrap it:
autocmd BufWritePost *.py execute "!python PythonTidy.py % %" | e
(:autocmd doesn't interpret | normally either, which is why you don't need to escape it yet again.)
I usually open many files in tabs with vim -p. Is it possible to check if any of the files was changed outside of Vim since editing started?
Add these lines to your .vimrc:
au FocusGained,BufEnter * :silent! checktime
au FocusLost,WinLeave * :silent! w
Basically, check for and reload (or discard) external changes when Vim or the current buffer gains focus, and optionally, auto-save when leaving focus.
Source: Vim Wiki.
I came across an interesting find related to this question today...
Hidden in /usr/share/vim/vim71/vimrc_example.vim there is this command:
" Convenient command to see the difference between the current buffer and the
" file it was loaded from, thus the changes you made.
command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
\ | wincmd p | diffthis
It will open a vimdiff-like window with the current buffer and the underlying file highlighting all of the changes between the two.
vim usually warns me automatically if it detects an external change to a file; however, from perusing the documentation it looks like you can invoke that check manually with :checktime
Unfortunately I don't know how to disable that aforementioned automatic check to test and see if checktime does the right thing, so this answer might be completely off-base.
Use :edit
:help :edit for more info.
You can find out if the buffer in the active window is modified by running the command:
:set mod?
If it returns nomodified, then the contents of the buffer match those of the corresponding file. If it returns modified, then the buffer has unsaved changes.
By default, the status-line shows a [+] symbol if the current buffer has been modified. The status line is generally only visible if you have split windows. If you want to show the status line, even when you have just a single window, run:
:set laststatus=2
There's a good article about customizing your status line on Vim Recipes.
let s:pid=system("ps -p $$ -o ppid=")+0
if !exists('g:watches')
let g:watches={}
else
finish
endif
function! ModWatch(fname, match)
let fname=fnamemodify(a:fname, ':p')
if has_key(g:watches, fname)
return
endif
let shellscript=
\"while true ; do".
\" inotifywait ".shellescape(fname)." ; ".
\" kill -WINCH ".s:pid." ; ".
\"done"
echo shellscript
echo shellescape(shellscript)
let pid=system("sh -c ".shellescape(shellscript)." &>/dev/null & echo $!")+0
call extend(g:watches, { fname : pid })
augroup ModWatch
execute "autocmd! BufWipeOut ".a:match
execute "autocmd BufWipeOut ".a:match.' call DeleteWatch("'.
\escape(fname, '\"|').'")'
augroup END
endfunction
function! DeleteWatch(fname)
call system("kill ".g:watches[a:fname])
unlet g:watches[a:fname]
endfunction
augroup ModWatch
autocmd!
autocmd VimResized * checktime
autocmd BufNew * call ModWatch(expand("<afile>"), expand("<amatch>"))
augroup END