How to auto add more blank lines to bottom when editing? - vim

like scrolloff, but it will reach bottom
I want it never reach bottom of the edit area although move the cursor to the bottom of text.
100 func coding
101 return coding
102 end
103 // bottom of file
~
~ auto added, can't reach
~
=============status bar===========

Something like this?
:autocmd CursorMoved * if line('.') == line('$') | call append('$', '') | endif
It will automatically append an empty line whenever the cursor reaches the last line.

IIUC, you could combine the comment by 244an and Ingo Karkat's answer to have vim always leave some empty lines at the bottom of its window. The following should get you part of the way:
autocmd CursorMoved *
\ if line('.') > line('$') - &scrolloff
\ && line('w0') + winheight(0) - 1 - &scrolloff < line('$')
\ | exec 'normal ^R'
\ | endif
Note that there are a couple of issues with this:
This also applies to the command window.
If you jump directly to the bottom of the file, new skips will be done until scrolloff lines are visible below the last line.

Related

Prevent opening fold under the cursor when moving the cursor to left or right in Vim

Whenever I move the cursor left or right using h or l, the fold under the cursor opens automatically. By the way, moving top or down does not have this problem. Is there any way to prevent automatically opening fold when moving horizontally?
You can create the auto command:
autocmd! CursorMoved * if foldclosed('.') != -1 |
\ nnoremap h <nop>|
\ nnoremap l <nop>|
\ else |
\ silent! unmap h|
\ silent! unmap l|
\ endif
Here foldclosed('.') returns -1 if current line is unfolded. Instead of using this auto command just avoid pressing h or l over folds.
The default value of :help 'foldopen' has hor in it; this causes horizontal movements (like l, h) to open folds.
To disable this, simply add this to your ~/.vimrc:
set foldopen-=hor

How to prompt a user for multiple entries in a list?

I'm working on a vim plugin. There are a set of valid options for a configuration parameter. I would like the user to be able to select the options they want from a list.
This is similar to inputlist, but inputlist only returns the index of the single chosen element. I'd prefer it return the indexes of all chosen elements.
How would I create a mutliselect in vim?
I don't know exactly which kind of interface you have in mind, but since you mentioned inputlist(), I thought you could simply write a loop whose body would invoke it.
Maybe something like this:
let options_chosen = []
let options_valid = [
\ 'foo',
\ 'bar',
\ 'baz',
\ 'qux',
\ 'norf'
\ ]
for i in range(1,len(options_valid))
let choice = inputlist([ 'Select your options:' ]
\ + map(copy(options_valid), '(v:key+1).". ".v:val'))
if choice >= 1 && choice <= len(copy(options_valid))
let options_chosen += [copy(options_valid)[choice - 1]]
let options_valid = filter(options_valid, 'v:val !=# options_chosen[-1]')
else
break
endif
redraw
endfor
If you execute this code, it should let you choose an option from the list options_valid. After each iteration, it should add the chosen item inside the list options_chosen and remove it from the list options_valid. The loop iterates as many times as there are items in options_valid initially. When you're done, you can stop the loop by hitting Escape.
It may not be what you want, because I don't know what interface you want to present to the user: a command, a mapping, an interactive buffer... But it may be a start, upon which you could build something else.
With an interactive buffer as the interface, I came up with this:
let s:options_valid = ['foo', 'bar', 'baz', 'qux', 'norf']
com! MultipleOptions call s:multiple_options()
fu! s:multiple_options() abort
vnew | exe 'vert resize '.(&columns/3)
setl bh=wipe bt=nofile nobl noswf nowrap
if !bufexists('Multiple Options') | sil file Multiple\ Options | endif
sil! 0put =s:options_valid
sil! $d_
setl noma ro
nno <silent> <buffer> <nowait> q :<c-u>close<cr>
nno <silent> <buffer> <nowait> <cr> :<c-u>call <sid>toggle_option()<cr>
augroup multi_op_close
au!
au WinLeave <buffer> call s:close()
augroup END
endfu
fu! s:close() abort
let g:selected_options = exists('w:options_chosen')
\ ? map(w:options_chosen.lines, 's:options_valid[v:val-1]')
\ : []
au! multi_op_close | aug! multi_op_close
close
endfu
fu! s:toggle_option() abort
if !exists('w:options_chosen')
let w:options_chosen = { 'lines' : [], 'pattern' : '', 'id' : 0 }
else
if w:options_chosen.id
call matchdelete(w:options_chosen.id)
let w:options_chosen.pattern .= '|'
endif
endif
if !empty(w:options_chosen.lines) && count(w:options_chosen.lines, line('.'))
call filter(w:options_chosen.lines, "v:val != line('.')")
else
let w:options_chosen.lines += [ line('.') ]
endif
let w:options_chosen.pattern = '\v'.join(map(
\ copy(w:options_chosen.lines),
\ "'%'.v:val.'l'"
\ ), '|')
let w:options_chosen.id = !empty(w:options_chosen.lines)
\ ? matchadd('IncSearch', w:options_chosen.pattern)
\ : 0
endfu
If you execute the command :MultipleOptions, it should open a temporary vertical viewport, in which the options stored inside the list s:options_valid should be displayed.
From there, you can hit Enter to select or deselect the current line. When an option is selected, its line is colored with the highlighting group IncSearch.
When you're done, you can close the window hitting q, and all your chosen options should be inside g:selected_options.
In lh-vim-lib I provide a lh#ui#check() function that does exactly that. Its behaviour is similar to confirm() in text mode. i.e. I rely on an old trick that hacks the statusline. (This needs to play with "\r", :redraw and so on)
You can see it live in the screencast I've made for lh-tags. Wait for the "Which kinds to you wish to display?" question. (In the screencast you should see the old code with CHECK, CONFIRM and CHOOSE)
BTW, the dialog used in lh-tags to choose one entry here can also be used (with the tagged parameter set to one) to select several entries at once.

How can I save in vim a file with the actual fold text? ("+-- 43 lines [...]")?

I am looking for a way to save to a new text file a file that is folded, with all the folds closed. In other words, just as I see it on the screen.
Is it possible?
(I will have to print the code later, and parts of it are irrelevant to my purposes; the folding mechanism would be ideal for this, my other alternative is manually adding "[X lines omitted]" to the saved text.)
Fold your text as needed and then use :TOhtml to convert to an HTML file. This will preserve your folding. If you need it as a plain text file, you can post-process e.g. with w3m, which renders HTML to text and allows to dump it to a text file.
Here is a custom :RenderClosedFolds command, which will modify the current buffer / range. It also attempts to maintain the Folded highlighting of the original folds.
":[range]RenderClosedFolds
" Replace all lines currently hidden inside closed folds
" with a single line representing 'foldtext'.
function! s:RenderClosedFolds()
if line('.') == foldclosed('.')
let l:result = foldtextresult('.')
call setline('.', l:result)
execute printf('syntax match renderedFold "\V\^%s\$" containedin=ALL keepend', escape(l:result, '"\'))
else
delete _
endif
endfunction
command! -bar -range=% RenderClosedFolds
\ highlight def link renderedFold Folded |
\ let g:ingocommands_IsEntireBuffer = (<line1> == 1 && <line2> == line('$')) |
\ if g:ingocommands_IsEntireBuffer | syntax clear renderedFold | endif |
\ let g:save_foldmethod = &l:foldmethod | setlocal foldmethod=manual |
\ execute '<line1>,<line2>folddoclosed call <SID>RenderClosedFolds()' |
\ if g:ingocommands_IsEntireBuffer | setlocal nofoldenable | endif |
\ let &l:foldmethod = g:save_foldmethod | unlet g:save_foldmethod g:ingocommands_IsEntireBuffer
I have once created a script, that saves all folds to a new buffer.

How to return to the previous point in Vim

In Vim there is a command ``.` to return exactly to where last edited text.
But my question is: How to make it automatic? What I mean is, every time I exit and reopen the same file again, it brings me to the point where I left.
I saw my friend's Vim has that behavior but he doesn't know how to do it.
You'll want to read this vim tip.
I use these commands a lot:
CTRL-O Go to [count] Older cursor position in jump list
(not a motion command).
CTRL-I Go to [count] newer cursor position in jump list
(not a motion command).
ma Set mark a at cursor position (does not move
the cursor, this is not a motion command).
'a Jump to the mark a in the current buffer.
gi Insert text in the same position as where Insert mode
was stopped last time in the current buffer.
Read this: :help last-position-jump
Put this lines on your ~/.vimrc file
" When editing a file, always jump to the last cursor position
autocmd BufReadPost *
\ if line("'\"") > 0 && line("'\"") <= line("$") |
\ exe "normal g`\"" |
\ endif
This only will help you next time you open vim to restore your cursor position.
Another idea is placing this on your ~/.bashrc
lvim='vim -c "normal '\''0"'
It will allow you to open the last edited file.
Update!
alias lvim="vim -c':e#<1'"
The above alias will open vim in command mode -c to edit an alternative file # in this case the first one <1 which is the last one.
For neovim I have this:
In your init.lua
require('autocmds')
In your lua directory create a file called autocmds.lua:
local augroups = {}
augroups.restore_position {
restore_cursor_position = {
event = "BufRead",
pattern = "*",
command = [[call setpos(".", getpos("'\""))]],
},
}
for group, commands in pairs(augroups) do
local augroup = vim.api.nvim_create_augroup("AU_"..group, {clear = true})
for _, opts in pairs(commands) do
local event = opts.event
opts.event = nil
opts.group = augroup
vim.api.nvim_create_autocmd(event, opts)
end
end

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.

Resources