vim restores cursor position; exclude special files - vim

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)

Related

vim substitutes with cursor keeping its position

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.

Vim unwanted jump to end of file after write

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.)

How to modify the last-position-jump vimscript to not do it for git commit messages

Here's the script for convenience:
" Uncomment the following to have Vim jump to the last position when
" reopening a file
if has("autocmd")
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$")
\| exe "normal! g'\"" | endif
endif
The feature is excellent but not when used with certain cases where Vim is invoked as editor: For instance often I write a two line git commit message so the next time I commit it's gonna drop me on the second line and I have to adjust for this.
Similarly many other systems allow you to specify Vim to be used as an editor as part of some process that involves editing a file.
How can we detect this and modify our last-position-jump script? Git's actually kind of special because it uses the .git/COMMIT_EDITMSG file which stays the same across commits which is why this is happening. With a temp file it wouldn't occur on files that Vim hasn't seen before.
This probably makes the task nearly trivial (augment the script with a check if current file == COMMIT_EDITMSG...)
But, a really perfect answer is if we can detect whether Vim is invoked from the command line or if it was opened by a script. Is there a way to tell? I know unix programs can determine if they are running in a term/pseudoterm or not, though either way they do end up receiving input from the terminal.
If you don't want to modify the global rule (like in FDinoff's answer) with all sorts of exceptions, you can also undo the jump by putting the following into ~/.vim/after/ftplugin/gitcommit.vim:
:autocmd BufWinEnter <buffer> normal! gg0
Though I haven't experienced any problems with the above command, even on buffer switches, a more robust version makes the autocmd fire once:
augroup DisableLastPositionJump
autocmd! BufWinEnter <buffer> execute 'normal! gg0' | autocmd! DisableLastPositionJump BufWinEnter <buffer>
augroup END
Git commit messages have the filetype gitcommit.
Just add a check to see if the file is not of the gitcommit filetype for deciding when to jump to the last position.
if has("autocmd")
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") && &filetype != "gitcommit"
\| exe "normal! g'\"" | endif
endif
If you don't want VIM to remember where you were from the previous commit, set the following in ~/.vimrc:
autocmd FileType gitcommit call setpos('.', [0, 1, 1, 0])
Source: http://vim.wikia.com/wiki/Always_start_on_first_line_of_git_commit_message

how to use pythontidy in vim

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.)

Automatically quit vim if NERDTree is last and only buffer

I have the following in my .vimrc:
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Open NERDTree by default
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
autocmd VimEnter * NERDTree
autocmd VimEnter * wincmd p
So,
% vim file.txt
opens NERDTree and focuses the cursor in the file.txt buffer. I make my edits, and hit :q on the buffer, and I'm left with . . . NERDTree. This is annoying.
I could use :qa to close all buffers, and exit vim, but I'm used to the :q trope. So I'm wondering if there's a way to detect that the only remaining buffer is NERDTree, and "unify" the two buffers, for purposes of :q
Edit
Ask and ye shall receive: https://github.com/scrooloose/nerdtree/issues#issue/21
A script to do exactly this has been posted on the NERDTree issue list. Checkout issue-21 on GitHub for nerdtree.
This leads to the single line command for your vimrc here:
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTree") && b:NERDTree.isTabTree()) | q | endif
function! s:CloseIfOnlyControlWinLeft()
if winnr("$") != 1
return
endif
if (exists("t:NERDTreeBufName") && bufwinnr(t:NERDTreeBufName) != -1)
\ || &buftype == 'quickfix'
q
endif
endfunction
augroup CloseIfOnlyControlWinLeft
au!
au BufEnter * call s:CloseIfOnlyControlWinLeft()
augroup END
From my vimrc, based on a version from janus repo.
Enhancements: also close if only a quickfix window is left.
It uses the BufEnter autocommand instead, which is required for &bt to work properly.
An idea in need of implementation:
You could write a function which, when called, checks if the only buffer remaining (or perhaps the only non-help buffer, if you prefer) is a NERDTree buffer and, if so, deletes it (or just quits).
Then have an autocmd run it whenever a buffer is deleted / hidden / whatever actually happens when you :q (it shames me to admit I'm not entirely sure!).
You could :cabbrv q qa but I'd advise against that because you'll forget about it when you actually want q.
I like to do this: cmap bq :bufdo q<CR> to close all buffers with two keystrokes in command mode.

Resources