VimL print values in a list infinitly - vim

I have a list of symbols, and want to use the Vim8 timers API to loop over the symbols list every 80ms and return that symbol. I worked out something like this:
let s:frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
let s:numTestimonials = len(s:frames)
let s:start = 0
function! PrintValues()
return s:frames[s:start]
let s:start = (s:start) + 1 % s:numTestimonials
endfunction
let timer = timer_start(80, 'PrintValues', {'repeat': -1})
But as soon as it reaches the last symbol in the list, it will throw an error, E684: list index out of range: 10. Any ideas how to accomplish what I want?

You need to have the 1 inside parentheses before the mod
let s:start = (s:start + 1) % s:numTestimonials
1 % s:numTestimonials is always 1 and is evaluated before adding s:start

Some little changes made to your code (Tested on vim 7.4):
let timer = timer_start(500, 'PrintValues', {'repeat': -1})
let s:frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
let s:numTestimonials = len(s:frames)
let s:start = 0
function! PrintValues(timer)
execute "normal! i".s:frames[s:start]
let s:start = (s:start + 1) % s:numTestimonials
endfunction

Related

Switching to next/prev connection in DBExt

In DBExt, you can change your connection with
DBSetOption profile=<profile name>
But is there a way to switch between connections like next or previous? Or would I have to make a vimscript function to deal with that?
Solved with a Vimscript:
let g:dbext_default_profile_one = '...'
let g:dbext_default_profile_two = '...'
let g:dbext_default_profile = 'one'
let s:dbext_profiles = ['one', 'two']
let s:current_profile_number = 0
function! Next_dbext_profile()
" Reset current_profile_number if too high
if s:current_profile_number >= len(s:dbext_profiles)
let s:current_profile_number = 0
endif
let l:exec_string = ':DBSetOption profile=' . s:dbext_profiles[s:current_profile_number]
echo l:exec_string
execute l:exec_string
let s:current_profile_number = s:current_profile_number + 1
endfunction

Get "usable" window width in vim script

How do I get the width of 3 (marked with green color in the image) in vim script?
If there is no signs column, and there are no other "special columns", I can get it with
winwidth(0) - (max([len(line('$')), &numberwidth-1]) + 1)
I think, you should be able to get that width using:
:set virtualedit=all
:norm! g$
:echo virtcol('.')
Alternatively, you could check, whether a signcolumn is present (e.g. using redir)
:redir =>a |exe "sil sign place buffer=".bufnr('')|redir end
:let signlist=split(a, '\n')
:let width=winwidth(0) - ((&number||&relativenumber) ? &numberwidth : 0) - &foldcolumn - (len(signlist) > 1 ? 2 : 0)
My ingo-library plugin has a ingo#window#dimensions#NetWindowWidth() function for that.
Answering because I can't comment yet:
Christian's answer gives the wrong result in the case that the actual number of lines in the file exceeds &numberwidth (because &numberwidth is just a minimum, as kshenoy pointed out). The fix is pretty simple, though, just take the max() of &numberwidth and the number of digits in the last line in the buffer (plus one to account for the padding vim adds):
redir =>a | exe "silent sign place buffer=".bufnr('') | redir end
let signlist = split(a, '\n')
let lineno_cols = max([&numberwidth, strlen(line('$')) + 1])
return winwidth(0)
\ - &foldcolumn
\ - ((&number || &relativenumber) ? lineno_cols : 0)
\ - (len(signlist) > 2 ? 2 : 0)
Kale's answer corrected one corner case where the number of lines is exceeding what &numberwidth can display. Here I fix another corner case where the signcolumn option is not set to auto
function! BufWidth()
let width = winwidth(0)
let numberwidth = max([&numberwidth, strlen(line('$'))+1])
let numwidth = (&number || &relativenumber)? numberwidth : 0
let foldwidth = &foldcolumn
if &signcolumn == 'yes'
let signwidth = 2
elseif &signcolumn == 'auto'
let signs = execute(printf('sign place buffer=%d', bufnr('')))
let signs = split(signs, "\n")
let signwidth = len(signs)>2? 2: 0
else
let signwidth = 0
endif
return width - numwidth - foldwidth - signwidth
endfunction
None of the above answers take into account the following points -
Plugins use sign-groups (if available), so simply running exe "silent sign place buffer=".bufnr('') does not show the sign's placed in the plugin's group
Neovim has support for variable signcolumn width
So this is the answer that finally set the ball rolling for me. Of course it is influenced by all of the above answers -
function! BufferWidth()
let width = winwidth(0)
let numberwidth = max([&numberwidth, strlen(line('$')) + 1])
let numwidth = (&number || &relativenumber) ? numberwidth : 0
let foldwidth = &foldcolumn
if &signcolumn == 'yes'
let signwidth = 2
elseif &signcolumn =~ 'yes'
let signwidth = &signcolumn
let signwidth = split(signwidth, ':')[1]
let signwidth *= 2 " each signcolumn is 2-char wide
elseif &signcolumn == 'auto'
let supports_sign_groups = has('nvim-0.4.2') || has('patch-8.1.614')
let signlist = execute(printf('sign place ' . (supports_sign_groups ? 'group=* ' : '') . 'buffer=%d', bufnr('')))
let signlist = split(signlist, "\n")
let signwidth = len(signlist) > 2 ? 2 : 0
elseif &signcolumn =~ 'auto'
let signwidth = 0
if len(sign_getplaced(bufnr(),{'group':'*'})[0].signs)
let signwidth = 0
for l:sign in sign_getplaced(bufnr(),{'group':'*'})[0].signs
let lnum = l:sign.lnum
let signs = len(sign_getplaced(bufnr(),{'group':'*', 'lnum':lnum})[0].signs)
let signwidth = (signs > signwidth ? signs : signwidth)
endfor
endif
let signwidth *= 2 " each signcolumn is 2-char wide
else
let signwidth = 0
endif
return width - numwidth - foldwidth - signwidth
endfunction
Starting from Vim 8.2.3627, getwininfo()'s output has a textoff containing the
number of columns occupied by any 'foldcolumn', 'signcolumn' and line number in front of the text
therefore subtracting that to the width entry, e.g. computing
getwininfo(win_getid()).width - getwininfo(win_getid()).textoff
should give the desired result.
Before textoff was available, it seems to me that the followinig computation does cut it:
let textoff = max([&numberwidth, (&number ? len(line('$')) + 1 : (&relativenumber ? winfo.height + 1 : 0))])
\ + &foldcolumn
\ + (empty(sign_getplaced(bufname(), {'group': '*'})[0].signs) ? 0 : 2)
I made use of both solutions in this plugin of mine for showing soft-wrapped lines.

Is there any way to make VIM fold one line more?

I am using an adaptation of the VIM folding settings published here and here. Now, notice the program below:
When folded on my VIM, it produces the following result:
I don't really like that look, I'd prefer the following:
Which I got by rewriting the code:
But leaving the code like that is horrible. Is it possible to update the VimScript so I get the folding I want, without having to leave the JavaScript code like that?
Here is my exact setting:
setlocal foldmethod=expr
setlocal foldexpr=GetPotionFold(v:lnum)
setlocal foldminlines=0
function! s:NextNonBlankLine(lnum)
let numlines = line('$')
let current = a:lnum + 1
while current <= numlines
if getline(current) =~? '\v\S'
return current
endif
let current += 1
endwhile
return -2
endfunction
function! s:IndentLevel(lnum)
return indent(a:lnum) / &shiftwidth
endfunction
function! GetPotionFold(lnum)
if getline(a:lnum) =~? '\v^\s*$'
return '-1'
endif
let this_indent = <SID>IndentLevel(a:lnum)
let next_indent = <SID>IndentLevel(<SID>NextNonBlankLine(a:lnum))
if next_indent == this_indent
return this_indent
elseif next_indent < this_indent
return this_indent
elseif next_indent > this_indent
return '>' . next_indent
endif
endfunction
function! NeatFoldText()
let line = getline(v:foldstart)
let lines_count = v:foldend - v:foldstart + 1
let lines_count_text = '| ' . printf("%10s", lines_count . ' lines') . ' |'
let foldchar = ' '
let foldtextstart = strpart(line, 0, (winwidth(0)*2)/3)
let foldtextend = lines_count_text . repeat(foldchar, 6)
let foldtextlength = strlen(substitute(foldtextstart . foldtextend, '.', 'x', 'g')) + &foldcolumn
return foldtextstart . repeat(foldchar, winwidth(0)-foldtextlength) . foldtextend
endfunction
set foldtext=NeatFoldText()
hi Folded ctermbg=255 ctermfg=21
hi FoldColumn ctermbg=white ctermfg=darkred
And here is the sample code:
function foo(x){
var y = x*x;
var z = y+y;
return z;
};
function bar(x){
var y = x*x;
var z = y+y;
return z;
};
function foobar(x){
var y = x*x;
var z = y+y;
return z;
};
function barfoo(x){
var y = x*x;
var z = y+y;
return z;
};
set foldmethod=marker
set foldmarker={,}
Should give you what you want.
You are using a fold expression that is (as far as I can see from a glance) designed for indent folding (used for HAML, Python, Haskell and other indent-syntax languages).
For C, you should just be using :set foldmethod=syntax.

The conceal feature in vim still makes me move over all the characters

Here is what I do
:syntax match conceal Test +[A-Z0-9]\{6}+
:set conceallevel=2
:set concealcursor=nvi
So when I write 123456 in vim I expect it to be nothing there. But what actually happens when I move over that area is that I have to move 6 times in the direction I want to move for the cursor to pass that area.
Is there a way to work around this? I want vim to see it as if there is nothing there and that when I move over that area it's like there is nothing there. But I still want to be able to search for it and delete it.
Currently there is no built-in way to do this. You can use synconcealed() to determine whether there is a concealed character under the cursor and what it is concealed to and remap all moving keys to respect it: like this:
function! ForwardSkipConceal(count)
let cnt=a:count
let mvcnt=0
let c=col('.')
let l=line('.')
let lc=col('$')
let line=getline('.')
while cnt
if c>=lc
let mvcnt+=cnt
break
endif
if stridx(&concealcursor, 'n')==-1
let isconcealed=0
else
let [isconcealed, cchar, group]=synconcealed(l, c)
endif
if isconcealed
let cnt-=strchars(cchar)
let oldc=c
let c+=1
while c<lc && synconcealed(l, c)[2]==group | let c+=1 | endwhile
let mvcnt+=strchars(line[oldc-1:c-2])
else
let cnt-=1
let mvcnt+=1
let c+=len(matchstr(line[c-1:], '.'))
endif
endwhile
return ":\<C-u>\e".mvcnt.'l'
endfunction
nnoremap <expr> l ForwardSkipConceal(v:count1)
. Note: this does the thing for one single motion (l) and in normal mode, just to show the way it may be done.
ZyX's solution from above did not work for me: apparently the ID of a concealed text region can change while traversing it, causing the motion to stop prematurely.
I have been using an alternate version pasted below (also with the missing BackwardSkipConceal function). It's not pretty but works well when substituting mathematical characters in LaTeX documents or C++ code.
function! ForwardSkipConceal(count)
let cnt=a:count
let mvcnt=0
let c=col('.')
let l=line('.')
let lc=col('$')
let line=getline('.')
while cnt
if c>=lc
let mvcnt+=cnt
break
endif
if stridx(&concealcursor, 'n')==-1
let isconcealed=0
else
let [isconcealed, cchar, group] = synconcealed(l, c)
endif
if isconcealed
let cnt-=strchars(cchar)
let oldc=c
let c+=1
while c < lc
let [isconcealed2, cchar2, group2] = synconcealed(l, c)
if !isconcealed2 || cchar2 != cchar
break
endif
let c+= 1
endwhile
let mvcnt+=strchars(line[oldc-1:c-2])
else
let cnt-=1
let mvcnt+=1
let c+=len(matchstr(line[c-1:], '.'))
endif
endwhile
return ":\<C-u>\e".mvcnt.'l'
endfunction
function! BackwardSkipConceal(count)
let cnt=a:count
let mvcnt=0
let c=col('.')
let l=line('.')
let lc=0
let line=getline('.')
while cnt
if c<=1
let mvcnt+=cnt
break
endif
if stridx(&concealcursor, 'n')==-1 || c == 0
let isconcealed=0
else
let [isconcealed, cchar, group]=synconcealed(l, c-1)
endif
if isconcealed
let cnt-=strchars(cchar)
let oldc=c
let c-=1
while c>1
let [isconcealed2, cchar2, group2] = synconcealed(l, c-1)
if !isconcealed2 || cchar2 != cchar
break
endif
let c-=1
endwhile
let c = max([c, 1])
let mvcnt+=strchars(line[c-1:oldc-2])
else
let cnt-=1
let mvcnt+=1
let c-=len(matchstr(line[:c-2], '.$'))
endif
endwhile
return ":\<C-u>\e".mvcnt.'h'
endfunction
nnoremap <expr> l ForwardSkipConceal(v:count1)
nnoremap <expr> h BackwardSkipConceal(v:count1)

is there a way on vim to show all column numbers in the current buffer line?

It would be very nice to have an option that would show all the column numbers of the current line or maybe of all the buffer, so I could know where exactly to navigate. Is there such an option or do i have to program it myself (nooo XD)?
:h 'statusline'
It is as easy as defining exactly what you what to see printed. e.g.
" RulerStr() comes from http://www.vanhemert.co.uk/vim/vimacros/ruler2.vim
function! RulerStr()
let columns = &columns
let inc = 0
let str = ""
while (inc < columns)
let inc10 = inc / 10 + 1
let buffer = "."
if (inc10 > 9)
let buffer = ""
endif
let str .= "....+..." . buffer . inc10
let inc += 10
endwhile
let str = strpart(str, 0, columns)
return str
endfunction
let s:saved_stl = {}
function! s:ToggleRuler()
let buf = bufnr('%')
if has_key(s:saved_stl, buf)
let &l:stl = s:saved_stl[buf]
unlet s:saved_stl[buf]
else
let s:saved_stl[buf] = &l:stl
setlocal stl=%{RulerStr()}
endif
endfunction
nnoremap <silent> µ :call <sid>ToggleRuler()<cr>
You can use "set ruler". It will show the line number and column position at the bottom.

Resources