When I write to a file with :w, vim sometimes (NOT ALWAYS) jumps to the end of the file after the write operation is complete. I don't understand why this happens. I've been going through my .vimrc to see if I have some kind of bug. My .vimrc is quite large so I don't include the full source here, I think the only parts of my .vimrc which are perhaps relevant to this question are the following parts:
nore ; :
inoremap jj <Esc>
" Automatically remove all trailing whitespace.
" Every time the user issues a :w command, Vim will automatically remove all
" trailing whitespace before saving
autocmd BufWritePre * :%s/\s\+$//e
" Restore cursor position
au BufReadPost *
\ if line("'\"") > 0|
\ if line("'\"") <= line("$")|
\ exe("norm '\"")|
\else|
\exe "norm $"|
\endif|
\endif
However I don't see how these parts of my .vimrc can cause the jump behavior after writing, a full source of my .vimrc is available here. I hope somebody has an idea about what is causing the unwanted jump.
Here is a command from my ~/.vimrc:
command! -range=% TR mark `|execute <line1> . ',' . <line2> . 's/\s\+$//'|normal! ``
The trick is to create mark ` before the trimming and jump back to it afterward.
You can change your autocmd to:
autocmd BufWritePre * :mark `|%s/\s\+$//e|normal! ``
Even with #romainl's addition of the mark, this still isn't fully transparent:
the view (of displayed lines) may still change (winsaveview() instead of a mark would fix that)
the :s command clobbers the last search pattern
A plugin (like my DeleteTrailingWhitespace plugin) would provide a more robust solution. (The plugin page has links to alternative plugins.)
Related
I'm a big user of Vim's ctags ctrl-] shortcut. I recently made a tag file in which the primary language is Make. When I try to ctrl-] with my cursor over a variable with a hyphen (ex. dl-routines), I get an error. If my cursor is over 'dl' within the 'dl-routines' variable I get the error
tag not found: dl
If my cursor is over 'routines' within the 'dl-routines' variable I get the error
tag not found: routines
I'm aware of Vim's
:ta tagname
However I'd like to use ctrl-] as it gives me less room for error.
In this case it's probably worth altering 'iskeyword' option to include dash. It may have many other effects, but they all should be quite useful. The only trick is to make these changes locally:
autocmd FileType make setlocal iskeyword+=45
Setting 'iskeyword' option locally is probably the simplest solution. As an addendum to #Matt's answer, if you want to keep your option clean and add 45 only when you press <C-]>, you could use this trick function.
function! CWordWithKey(key) abort
let s:saved_iskeyword = &iskeyword
let s:saved_updatetime = &updatetime
if &updatetime > 200 | let &updatetime = 200 | endif
augroup CWordWithKeyAuGroup
autocmd CursorHold,CursorHoldI <buffer>
\ let &updatetime = s:saved_updatetime |
\ let &iskeyword = s:saved_iskeyword |
\ autocmd! CWordWithKeyAuGroup
augroup END
execute 'set iskeyword+='.a:key
return expand('<cword>')
endfunction
which adds the parameter key to iskeyword and sets a self-destructing autocmd to restore your older iskeyword after 200 ms.
Than remap <C-]> in ftplugin/make.vim or with autocmd FileType make ... as the previous answer.
nnoremap <buffer> <silent> <C-]> :execute 'tag '.CWordWithKey(45)<CR>
I wish vim to remove all trailing spaces automatically keeping the cursor in its current position, so I use the following autocmd in my vimrc
autocmd BufWritePre *
\ exec "%s/\\s\\+$//e" | exec 'normal ``'
It works well normally. But if nothing is changed since last writing, a 'w' command will lead the cursor move to the last position when last writing is executed. What should I do if I wish the cursor keep its position unconditionally.
You can use the command silent! to avoid the error caused by the failed match from affecting the rest of your command:
exec "silent! %s/\\s\\+$//e" | exec 'normal ``'
See :help silent.
You can manually set the mark first via :normal! m', but it's better to save the entire view, as jumping back to the mark just restores the cursor position, but not necessarily the entire view inside the window (in case scrolling occurred).
autocmd BufWritePre *
\ let g:saveview = winsaveview() |
\ %s/\s\+$/e" |
\ call winrestview(g:saveview)
This still suffers from clobbering your search pattern (which wrapping in a :function could fix).
I would recommend to use a tested and more configurable plugin instead. Coincidentally, I've developed the DeleteTrailingWhitespace plugin that can do automatic, unconditional deletion via the following configuration:
let g:DeleteTrailingWhitespace = 1
let g:DeleteTrailingWhitespace_Action = 'delete'
You can avoid writing the file if the file has not changed, by either using the :update command to do the write, or by checking the for the "modified" option in your autocmd, like autocmd BufWritePre * if &modified | ... | endif.
I'm using QuickCursor for entering text to forms.
My problem with that is I always have MacVim open, and with hidden enabled, so when I :wq from the temp file QuickCursor make, the buffer stays in MacVim, so I have to delete it to get QuickCursor paste back to the window.
I wanted to solve this with an autocommand in my vimrc:
autocmd BufRead "/private/var/folders/fg/gv_*/T/*" set bufhidden="delete" | startinsert!
but this never run. What could be the problem ? What is the right event to use ? I tried BufWinEnter, BufNewFile, neither of them works, or maybe something else is the problem.
Ok, after several hours of try, I finally found out.
I had added quotes to the bufhidden setting and the filename. It should be:
autocmd BufRead /private/var/folders/fg/gv_*/T/* set bufhidden=delete | startinsert!
With the extra quotes it doesn't work:
"delete" is an invalid option value (see :he bufhidden)
quotes around a filename prevent the wildcards (glob characters) from matching (see doc)
If anybody else using QuickCursor, you can fine-tune it:
autocmd BufWinEnter /private/var/folders/fg/gv_*/T/* set bufhidden=delete |
exe "normal G$" | startinsert!
So it changes to insert mode at the end of the text
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.)
The following code is inside my .vimrc and generally restores the last cursor position of a file I opened with vim:
autocmd BufReadPost *
\ if line("'\"") > 1 && line("'\"") <= line("$") |
\ exe "normal! g`\"" |
\ endif
I really like this feature and want to leave it turned on, except for one file: When commiting with git, vim gets fired up and I can edit the commit message with it. However the commit message file exists before vim starts (and is prefilled), so vim sees it as an existing file and restores the last cursor position (which is usually not where I would like to start to type).
So is there a possibility to modify the above script to exclude the COMMIT_EDITMSG file?
After reading through the manual on auto commands, I noticed that it seems not possible to define the pattern they match to in such a way that they exclude a special pattern. And I also wasn't able to use some variable that contains the current filename, so that I simply expand the existing if to exclude the file.
However, based on the comment by Pavel Shved (about the gg moving to the top of the file) I thought that in the same way it should be possible to simply overwrite the effect of the position restoring, by simply moving it to the top later again. So I came up with this:
autocmd BufReadPost COMMIT_EDITMSG
\ exe "normal! gg"
Placing this after the previous autocmd BufReadPost simply chains the event execution, so vim, after executing the first and restoring the position, reads this and matches it on the excluded filename and uses the gg to move the cursor to the top, basically overwriting the effect of the original autocmd.
And that works just fine :)
I understand you have already come up with a solution but I had the same question and came up with an alternative that doesn't require chaining.
function! PositionCursorFromViminfo()
if !(bufname("%") =~ '\(COMMIT_EDITMSG\)') && line("'\"") > 1 && line("'\"") <= line("$")
exe "normal! g`\""
endif
endfunction
:au BufReadPost * call PositionCursorFromViminfo()
You should probably investigate mksession. You can set up VimEnter/VimLeave auto commands to "do the right thing" when you have specified files on the command line (as when git invokes vim). There are numerous scripts for this floating around, see http://vim.wikia.com/wiki/Working_with_multiple_sessions for example.
Try this, its move cursor to last position
set hidden
Put the following lines instead of your lines in the .vimrc:
au BufWinLeave * mkview
au BufWinEnter * silent loadview
Adding the following to .vimrc worked for me:
au FileType gitcommit au! BufEnter COMMIT_EDITMSG call setpos('.', [0, 1, 1, 0])
(from https://vim.fandom.com/wiki/Always_start_on_first_line_of_git_commit_message)