Vim: reopen all previous files in a new session after restart - vim

I usually have long lived vim session with dozens of files open. Once in a while, I need to restart vim, say to install a new plugin or apply some new config.
How can I reopen all the previous files after restart?

You can use builtin sessions, perhaps with vim-session plugin.
A Vim session contains all the information about what you are editing.
This includes things such as the file list, window layout, global variables,
options and other information. (Exactly what is remembered is controlled by
the 'sessionoptions' option.)
The command
:mksession mysession.vim
saves the current session into the named file. Next time you start vim you can load the session:
vim -S mysession.vim
or inside vim
:source mysession.vim
You can automate saving sessions on VimLeave autocommand and reload a session on VimEnter but be careful abut restoring a session when vim is called, e.g., from git.

Here is a basic setup of automatic session save / load extracted from my config:
let s:session_loaded = 1
augroup autosession
" load last session on start
" Note: without 'nested' filetypes are not restored.
autocmd VimEnter * nested call s:session_vim_enter()
autocmd VimLeavePre * call s:session_vim_leave()
augroup END
function! s:session_vim_enter()
if bufnr('$') == 1 && bufname('%') == '' && !&mod && getline(1, '$') == ['']
execute 'silent source ~/.vim/lastsession.vim'
else
let s:session_loaded = 0
endif
endfunction
function! s:session_vim_leave()
if s:session_loaded == 1
let sessionoptions = &sessionoptions
try
set sessionoptions-=options
set sessionoptions+=tabpages
execute 'mksession! ~/.vim/lastsession.vim'
finally
let &sessionoptions = sessionoptions
endtry
endif
endfunction
It will only restore the session if you run vim without arguments, so if you do vim somefile.txt to do the quick edit, it will not touch the last session.
Bonus (this will restore cursor position inside files too):
" When editing a file, always jump to the last known cursor position.
" Don't do it when the position is invalid or when inside an event
" handler.
autocmd MyAutoCmd BufReadPost *
\ if line("'\"") > 0 |
\ if line("'\"") <= line("$") |
\ exe("norm '\"") |
\ else |
\ exe "norm $" |
\ endif|
\ endif

Related

Restoring cursor position after typing :make

I am using the following in my .vimrc to restore the cursor position after reopening the file:
" Copied from defaults.vim
" Put these in an autocmd group, so that you can revert them with:
" ":augroup vimStartup | au! | augroup END"
augroup vimStartup
au!
" When editing a file, always jump to the last known cursor position.
" Don't do it when the position is invalid, when inside an event handler
" (happens when dropping a file on gvim) and for a commit message (it's
" likely a different one than last time).
autocmd BufReadPost *
\ if line("'\"") >= 1 && line("'\"") <= line("$") && &ft !~# 'commit'
\ | exe "normal! g`\""
\ | endif
augroup END
The problem is that when I type :make, the cursor position is not restored; the cursor merely appears at the beginning of the line on which it was before typing :make.
Do you have any advise on how to fix this? Thank you for help.
The command :make doesn't seem to throw BufReadPost, so the autocommand will not execute.
You can do :make | norm! ``
:help ''

Automatically Quit Vim if NERDTree and TagList are the only 2 Buffers Left

I have this code taken from another StackOverflow user Conner, from this question Automatically quit Vim if NERDTree and TagList are the last and only buffers
(There wasn't an option for me to comment on that question, so my only option was to ask a new one).
The question is: How do I close Vim editor (in Linux Mint) if only NERDTree and TagList are the only two buffers left?
The answer provided was:
" If only 2 windows left, NERDTree and Tag_List, close vim or current tab
fun! NoExcitingBuffersLeft()
if winnr("$") == 3
let w1 = bufname(winbufnr(1))
let w2 = bufname(winbufnr(2))
let w3 = bufname(winbufnr(3))
if (exists(":NERDTree")) && (w1 == "__Tag_List__" || w2 == "__Tag_List__" || w3 == "__Tag_List__")
if tabpagenr("$") == 1
exec 'qa'
else
exec 'tabclose'
endif
endif
endif
endfun
autocmd BufWinLeave * call NoExcitingBuffersLeft()
But that does not work properly. Is closes whenever I close the last "exciting" buffer (one that is non-NERDTree or non-TagList), but it also closes whenever I try to open a new file from NERDTree (by double clicking on a filename in the "explorer").
Honestly, I do not understand this code too well. I have tried to mess around with it but I couldn't get the results I would like.
How do I alter this code to not close when I open a new file from NERDTree explorer?
Thank you, Conner, and the rest of the community!
From the Taglist manual, put on your .vimrc file
let Tlist_Exit_OnlyWindow=1
I don't use NERDTree, but you may succeed with the following
autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTreeType") && b:NERDTreeType == "primary") | q | endif

vim : insert mode problem : remaps (imap) and abbreviations (ab) in .vimrc don't work

I have a problem in vim:
If I modify the .vimrc file and add this lines:
map ;bb A78
it just works in normal mode.
If I got it, it should work in insert mode too, shouldn't it?
While editing, I've verified that everything was read properly (command ":map"):
i ;bb A78
If I do the same thing with "imap", I got the same problem: command ":imap" shows it's configured, but if I go in insert mode, and type ";bb" or ";bb" or ";bb" nothing is changed, I don't get the A78
What am I missing?
(And the marvellous codeSnippet plugin works only in normal mode too, which is a big problem to me)
If forgot to precise: I have only the plugin Tabularize, it's vim version 7.3 under cygwin, but I get the same problem in SSH / Linux Debian / vim version 7.0
If I try to do exactly what written here (to give another try, if it may help), that doesn't work either: "To use the abbreviation, switch to Insert mode and type th, followed by any whitespace above (space, tab, or carriage return)." doesn't work at all. This drives me nuts.
Here follows my .vimrc file, maybe there's something wrong here I didn't see:
set nocompatible
filetype plugin on
syntax enable
set ignorecase
set paste
set ruler
set modeline
set showcmd
set expandtab
set tabstop=2
set autoindent
set smartindent
set number
colorscheme desert
set vb t_vb=
set backup
set backupdir=~/.vim/backup
set directory=~/.vim/tmp
set fileencodings=utf-8,ucs-bom,default,latin1
set scrolloff=5
set undolevels=1000
nmap ;bw :. w! ~/.vimxfer<CR>
nmap ;br :r ~/.vimxfer<CR>
nmap ;ba :. w! >>~/.vimxfer<CR>
" Tell vim to remember certain things when we exit
" '10 : marks will be remembered for up to 10 previously edited files
" "100 : will save up to 100 lines for each register
" :20 : up to 20 lines of command-line history will be remembered
" % : saves and restores the buffer list
" n... : where to save the viminfo files
set viminfo='10,\"100,:20,%,n~/.viminfo
" when we reload, tell vim to restore the cursor to the saved position
augroup JumpCursorOnEdit
au!
autocmd BufReadPost *
\ if expand("<afile>:p:h") !=? $TEMP |
\ if line("'\"") > 1 && line("'\"") <= line("$") |
\ let JumpCursorOnEdit_foo = line("'\"") |
\ let b:doopenfold = 1 |
\ if (foldlevel(JumpCursorOnEdit_foo) > foldlevel(JumpCursorOnEdit_foo - 1)) |
\ let JumpCursorOnEdit_foo = JumpCursorOnEdit_foo - 1 |
\ let b:doopenfold = 2 |
\ endif |
\ exe JumpCursorOnEdit_foo |
\ endif |
\ endif
" Need to postpone using "zv" until after reading the modelines.
autocmd BufWinEnter *
\ if exists("b:doopenfold") |
\ exe "normal zv" |
\ if(b:doopenfold > 1) |
\ exe "+".1 |
\ endif |
\ unlet b:doopenfold |
\ endif
augroup END
set backspace=2
inoremap <silent> <Bar> <Bar><Esc>:call <SID>align()<CR>a
function! s:align()
let p = '^\s*|\s.*\s|\s*$'
if exists(':Tabularize') && getline('.') =~# '^\s*|' && (getline(line('.')-1) =~# p || getline(line('.')+1) =~# p)
let column = strlen(substitute(getline('.')[0:col('.')],'[^|]','','g'))
let position = strlen(matchstr(getline('.')[0:col('.')],'.*|\s*\zs.*'))
Tabularize/|/l1
normal! 0
call search(repeat('[^|]*|',column).'\s\{-\}'.repeat('.',position),'ce',line('.'))
endif
endfunction
:autocmd BufNewFile * silent! 0r ~/.vim/templates/%:e.tpl
:autocmd BufNewFile *.php call search('w', '', line("w$"))
Thanks a lot!
You need to make sure that vim is not in "paste" mode.
Try
:set nopaste
map doesn't make the mapping work in insert mode: for ALL modes, you want map!. See :help :map! for more information on this.
However, imap should work, so you're probably having issues either with timeouts or the 'paste' setting. The way a mapping works in insert mode is that it gives you a certain amount of time to enter the mapping (I think the default is 1 second) and if you type it slower than that it assumes you mean the individual characters. So if you do:
:map! ;bb A78
And then type:
;<pause>bb
(where <pause> is just a pause, not something you type)
You'll get ;bb, but if you type:
;bb
really quickly, you'll get A78.
To find out more about timeouts, have a look at these help pages:
:help 'timeout'
:help 'ttimeout'
:help 'timeoutlen'
:help 'ttimeoutlen'
The 'paste' option also has an effect: it disables mappings in insert mode and abbreviations. Try :set paste? to find out if you have this set and :set nopaste to disable it.
See:
:help 'paste'

How do you exit vimdiff mode in vim, specifically, for Fugitive?

I am using vim with the fugitive extension. It has a :Gdiff command which brings you into vimdiff mode, but what is the right/quick way to close/quit vimdiff mode?
I.e., let's say I am editing the file FooBar.txt under Git repository. I fire up :Gdiff, review my changes in vimdiff, and then I want to get back and continue editing FooBar.txt or any other file :)
UPDATE1: I'm going to give these quick combos a try next working day :)
"vimdiff current vs git head (fugitive extension)
nnoremap <Leader>gd :Gdiff<cr>
"switch back to current file and closes fugitive buffer
nnoremap <Leader>gD :diffoff!<cr><c-w>h:bd<cr>
UPDATE2: My current mappings (closes diff window only!)
"vimdiff current vs git head (fugitive extension)
nnoremap <Leader>gd :Gdiff<cr>
"switch back to current file and closes fugitive buffer
nnoremap <Leader>gD <c-w>h<c-w>c
Also, please help me decide if the following should be an anwser: https://stackoverflow.com/a/15975201/275980
You can execute windo set nodiff noscrollbind and then close the second window.
Update: there is a diffoff command. Use windo diffoff, not what I wrote in previous line.
According to: https://github.com/tpope/vim-fugitive/issues/36
Close the other window. The easiest way to do this if you haven't shifted focus to it is <C-W><C-O>, which means "make this Window the Only window."
I had no luck with diffoff, but I just learned that :Gedit with no argument will bring you back to the working-directory version of the file, as opposed to some earlier version you were reviewing.
And as q (no need for :q) will close the diff sidebar, you can do q followed by :Gedit to get rid of the sidebar and then go back to the current version of the file.
None of the above solutions worked for me. Ended up doing this instead:
nnoremap <Leader>D :Gedit<CR><C-w>h :q<CR><C-w>k
This works fine for me, combining some of the existing ideas here:
function! MyCloseDiff()
if (&diff == 0 || getbufvar('#', '&diff') == 0)
\ && (bufname('%') !~ '^fugitive:' && bufname('#') !~ '^fugitive:')
echom "Not in diff view."
return
endif
" close current buffer if alternate is not fugitive but current one is
if bufname('#') !~ '^fugitive:' && bufname('%') =~ '^fugitive:'
if bufwinnr("#") == -1
b #
bd #
else
bd
endif
else
bd #
endif
endfunction
nnoremap <Leader>gD :call MyCloseDiff()<cr>
An alternative to <C-W><C-O>, if you have multiple windows, would be move to the other diff window and do <C-W>c, which close only one window.
If you close the wrong diff window do a :Gedit
Be careful and don't confuse <C-W>c with <C-W><C-C>
I've found a simple solution for this. You can check it here: https://gist.github.com/radmen/5048080
" Simple way to turn off Gdiff splitscreen
" works only when diff buffer is focused
if !exists(":Gdiffoff")
command Gdiffoff diffoff | q | Gedit
endif
Check the vimdiff toggling between diffthis and diffoff here
at this page.
The code:
nnoremap <silent> <Leader>df :call DiffToggle()<CR>
function! DiffToggle()
if &diff
diffoff
else
diffthis
endif
:endfunction
Method 1:
open a compare by:
:windo diffthis
close it by:
:windo diffoff
Method 2:
I recommend just using the most simple command: :q<CR>
when you want to do it quickly, add the mapping:
" Set mapleader
let mapleader = ","
let g:mapleader = ","
and
" Quickly close the current window
nnoremap <leader>q :q<CR>
It works well for me. Exit vimdiff just by ,q, because normally your cursor in the old file.
this is what I have to leave the vimdiff windows after using :Gdiff
nnoremap gD :q!<CR> :Gedit!<CR>
noremap <leader>do :diffoff \| windo if &diff \| hide \| endif<cr>
Quite diff mode and close other diff windows. (Note: fugitive will auto delete its hidden buffers.)
My function will work both from diff window and file window. But probably won't handle itself with multiple diffs opened. For that you'll need to use fugitive#buffer(n).path() to scan and match.
command! Gdiffoff call Gdiffoff()
function! Gdiffoff()
let diffbufnr = bufnr('^fugitive:')
if diffbufnr > -1 && &diff
diffoff | q
if bufnr('%') == diffbufnr | Gedit | endif
setlocal nocursorbind
else
echo 'Error: Not in diff or file'
endif
endfunction
Add a key binding:
nnoremap <silent> <leader>gD :Gdiffoff<CR>
Yet another way. What I have in fugitive.vim - first save some info (s:gitbufname) when diff starts:
function! s:Diff(vert,...) abort
call sy#toggle()
let s:startcol = winwidth(0)
let &columns=(winwidth(0) * 2 - 20)
...
if getwinvar('#', '&diff')
let s:gitbufname = bufname("%")
wincmd p
call feedkeys(winnr."\<C-W>w", 'n')
endif
...
endfunction
and later when leaving the buffer switch window to the saved buffer and restore:
augroup fugitive_diff
autocmd!
autocmd BufWinLeave *
\ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
\ if exists('s:gitbufname') && winnr() != bufwinnr(s:gitbufname) |
\ let nr = bufnr("%") | exe bufwinnr(s:gitbufname).'wincmd w' | exe 'buf'.nr |
\ endif |
\ call s:diffoff_all(getbufvar(+expand('<abuf>'), 'git_dir')) |
\ call sy#toggle() |
\ call airline#load_theme() | call airline#update_statusline() |
\ let &columns=s:startcol |
\ endif
...
Was using the code below based on https://stackoverflow.com/a/15113951/10999673 :
if !exists(":Gdiffoff")
command Gdiffoff bw! fugitive://*
endif
but it gave me an error "E93: more than one match for ..." in a 3 way diff, so i instead used the answer from https://stackoverflow.com/a/4867969/10999673 and finally have this:
function! GetBufferList()
return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction
function! GetMatchingBuffers(pattern)
return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction
function! WipeMatchingBuffers(pattern)
let l:matchList = GetMatchingBuffers(a:pattern)
let l:count = len(l:matchList)
if l:count < 1
echo 'No buffers found matching pattern ' . a:pattern
return
endif
if l:count == 1
let l:suffix = ''
else
let l:suffix = 's'
endif
exec 'bw ' . join(l:matchList, ' ')
echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction
command! -nargs=1 Gdiffoff call WipeMatchingBuffers('fugitive://')
I just tweaked, copied and pasted the code into my .vimrc
Running :Gwrite after merging to your satisfaction will close the other two diff panes in addition to updating the git cache to mark the file as merged.

Can vim monitor realtime changes to a file

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.

Resources