How to write vim script to continuously move to the next window until window has a specific filetype in vim? - vim

for example I have multiple windows in a vim instance:
+---+---+---+
| | | |
| A | | |
| | | |
+---+---+---+
Different window has different filetypes, some are plugins (NvimTree/undotree/vista), others are real editing buffer (c/c++/python/etc).
I want to define key mappings, that will run command after auto jump to the real editing buffer window. And don't run directly on plugins window(NvimTree/undotree/vista).
Currently my key mappings are:
nnoremap <silent> <expr> <Leader>1 (&filetype ==# "NvimTree" <Bar><Bar> &filetype ==# "undotree" ? "\<C-w>\<C-w>" : "").":\<C-u>BufferGoto 1\<CR>"
This key mapping could work if current window's filetype is "NvimTree" or "undotree". But it's only jump once.
I want to make it a while/for loop, continue jump if the filetypes are in a blacklist (NvimTree/undotree/vista), until it's a real editing buffer window (filetypes are c/cpp/python/etc).
How could I write this vim script?

Related

Open multiple files in vim with mixed splits

I need to open 3 files with vim using linux terminal, but they shoud be open with mixed splits.
"/src/main.cpp" being the main window "input" and output are vertically split. Please refer to the image attacheed. i tried
vim src/main.cpp -O result -o output
but it opens all windows either in all vertical or all horizontal. vim help says
-O[N] Open N windows, split vertically. Otherwise it's like -o.
If both the -o and the -O option are given, the last one on
the command line determines how the windows will be split.
If you want the splits to be of different orientations, each of a specific
size, I think the best way is to write a function that you can just call that
has the desired split arrangement. I have one for a 4-way split:
" make a 4-way split and resize the windows how I like
function! WorkSplit()
let l:currentWindow=winnr()
execute "normal! :vsplit\<cr> :buffer 2\<cr>"
execute "normal! :split\<cr> :resize -20\<cr> :b scratch2\<cr>"
execute l:currentWindow . "wincmd w"
execute "normal! :split\<cr> :resize -20\<cr> :b scratch1\<cr>"
endfunction
" split vim into 4 windows, load first and second files on buffers 1 and 2.
" make the bottom windows short and load scratch*.m
nnoremap <silent><Leader>4 :call WorkSplit()<cr>
You'll notice that this function is not very general - it looks for two files
named scratch1 and scratch2 - which I often have in my projects - and tries to
put them in the two lower splits. So if I have open 4 files in vim called A, B,
scratch1 and scratch2, doing Leader+4 will do this:
-----------------
| | |
| A | B |
| | |
| | |
-----------------
| scr1 | scr2 |
-----------------
If I don't have the scratch1 and scratch2 files, the function will complain,
but still create the splits (the lower splits will simply contain the 3rd and
4th files.

Show all local changes in vim file

I know that it is possible to jump to last change in vim, like this :
`.
-- That is: a backtick, followed by a dot.
I would like to know if it is possible to get a history of changes made or at least show last change made, not just jumping to it - is there a way to use diff to help display changes?
Also, how many changes is stored?
You can list the changes by typing :changes or use the :DiffOrig after putting in your .vimrc the command below to see the changes made to the original file:
command! DiffOrig rightbelow vertical new | set bt=nofile | r # | 0d_ | diffthis | wincmd p | diffthis
Explanation:
command DiffOrig rightbelow vertical new
Create a new command named DiffOrig which will split a new empty vertical window and move cursor to it.
Now we have two buffers if you type :buffers or :files or :ls
it will list all the existed buffers where each one has a unique id number and a name:
. The current buffer (where the cursor is active) is called %
. The buffer where the cursor was previously is called #
set bt=nofile
Set the buffertype of the new buffer to nofile
read #
Put the content of the alternate buffer (original one) in the current buffer (%) (after the line where the command is executed) (the content will be the last saved status)
0d_
Delete the line to move the content one line up.
diffthis
Activate diff in the buffer in order to display the changes.
wincmd p
Move to the other buffer window (the command is same as ctrl-w p)
diffthis
Activate diff in this buffer too to display the changes.
it seems that this have already been answered in :
see changes in vim before save
after adding following to my .vimrc :
function! s:DiffWithSaved()
let filetype=&ft
diffthis
vnew | r # | normal! 1Gdd
diffthis
exe "setlocal bt=nofile bh=wipe nobl noswf ro ft=" . filetype
endfunction
com! DiffSaved call s:DiffWithSaved()
then I can use following command :
:DiffSaved
and I will get something like this:
in example I changed F to f

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.

How can I keep modes local to a tab?

If I have a tab in insert mode and I switch to another tab that was in normal mode when I last viewed it, it's changed to insert mode. This throws me off. How can I make changing modes local to a tab?
How do you switch to the other tab? By clicking on the tab line, with the mouse (that's the only way I could reproduce this)?! You should avoid the use of the mouse within Vim, but this will change the behavior:
:autocmd TabEnter * stopinsert
For switching tabs with the keyboard, this is usually done via the gt command in normal mode, so you've already left insert mode (just use <Esc>, not <C-O>).
For more browser-like behavior, I have the following key mappings:
" CTRL-Tab next tab
noremap <C-Tab> :<C-U>tabnext<CR>
inoremap <C-Tab> <C-\><C-N>:tabnext<CR>
cnoremap <C-Tab> <C-C>:tabnext<CR>
" CTRL-SHIFT-Tab previous tab
noremap <C-S-Tab> :<C-U>tabprevious<CR>
inoremap <C-S-Tab> <C-\><C-N>:tabprevious<CR>
cnoremap <C-S-Tab> <C-C>:tabprevious<CR>
Vim's current mode is editor-wide. You cannot force it to follow tabs or buffers or windows or anything.
Something you CAN do, is store off the current mode when you leave a tab, and restore it when you enter the tab.
This works switching between normal and any one of the insert/replace modes; but switching among the insert/replace modes doesn't seem to work for some reason:
augroup TAB_MODES
au!
autocmd TabLeave * let t:lastmode = mode(1)
autocmd TabEnter * if !exists('t:lastmode') | let t:lastmode = 'n' | endif
autocmd TabEnter * if t:lastmode ==# 'n' | stopinsert | endif
autocmd TabEnter * if t:lastmode ==# 'i' | startinsert | endif
autocmd TabEnter * if t:lastmode ==# 'R' | startreplace | endif
autocmd TabEnter * if t:lastmode ==# 'Rv' | startgreplace | endif
augroup END
This uses a tab-local variable to store the current mode when you leave a file, and uses it to start the correct insert or replace mode when you re-enter the tab (or stop insert mode). Some modes are not included here; for example I couldn't get visual mode to work (I tried several ways of invoking gv without success).

Vim: Open blank files in insert mode

Is there a .vimrc command for opening blank files in insert mode? Non blank files would still open in command mode.
You can try an autocommand:
au BufNewFile * startinsert
So, with:
vim oldfile
will enter in normal mode, but with:
vim newfile
will enter in insert mode.
Note that
vim
without files will be in normal mode too. Perhaps you would need an additional autocommand for it. (EDIT: See commments for a Ben's solution to this one)
To enter insert mode only on empty new buffers:
autocmd BufNewFile * if wordcount()['chars'] == 0 | startinsert | endif
To do that only when opening vim:
autocmd VimEnter * if wordcount()['chars'] == 0 | startinsert | endif

Resources