Use :set for custom option in vim - vim

I currently have the following in my .vimrc
let g:Myvar="noisy"
function Myfirstfunction()
if g:Myvar=="noisy"
echo "noisy"
else
echo "quiet"
endif
endfunction
I would like to be able to change (or toggle) g:Myvar using :set
:set g:Myvar=quiet
Of course, the current set up doesn't work, hence the question: how can I toggle a custom option using :set? I'm not convinced that my approach so far is valid, so I am very open to it being overhauled.

let g:Variable = "noisy"
function! MyFirstFunction()
if g:Variable == "noisy"
echo "noisy"
else
echo "quiet"
endif
endfunction
I don't see the problem with the above. "It works on my machine" :) But, I'm not quite sure what exactly are you asking next - let g:Variable="quiet" works also.
Are you asking how to make function that will toggle one variable between two different values?
let g:Variable = 1
function! TogglingVariable()
if g:Variable == 1
let g:Variable = 0
echo "Variable is now 0"
else
let g:Variable = 1
echo "Variable is now 1"
endif
endfunction
If neither of these is what you want, you'll have to explain it a bit more then. This is all I could conclude from the question.

Related

How to remove digraph highlighting in vim

Using vim or nvim, when I open a new vim terminal digraphs display correctly. See below:
However, whenever I change colorscheme (any, it is not specific to a particular colorscheme) - then the digraphs appear highlighted. The highlighting remains even when switching back to the original colorscheme. This happens with any digraph, not just the one shown in this question.
See below:
Cannot find a way to remove that highlighting, or prevent it happening in the first place. Have tried commands like :highlight nonascii none but had no luck. Any help / suggestions much appreciated.
Most/Some of the colorschemes aren't really made for hotswap.
It seems to be a rather common problem with solarized f.e.
There is a plugin which handles a change cleanly: https://github.com/xolox/vim-colorscheme-switcher (I haven't tested it).
And I copied a bunch of functions for that somewhere which works arround the problems most of the time. I don't know where I got it from, but I want to be clear, it is not my work!
function! s:Find_links() " {{{1
" Find and remember links between highlighting groups.
redir => listing
try
silent highlight
finally
redir END
endtry
for line in split(listing, "\n")
let tokens = split(line)
" We're looking for lines like "String xxx links to Constant" in the
" output of the :highlight command.
if len(tokens) == 5 && tokens[1] == 'xxx' && tokens[2] == 'links' && tokens[3] == 'to'
let fromgroup = tokens[0]
let togroup = tokens[4]
let s:known_links[fromgroup] = togroup
endif
endfor
endfunction
function! s:Restore_links() " {{{1
" Restore broken links between highlighting groups.
redir => listing
try
silent highlight
finally
redir END
endtry
let num_restored = 0
for line in split(listing, "\n")
let tokens = split(line)
" We're looking for lines like "String xxx cleared" in the
" output of the :highlight command.
if len(tokens) == 3 && tokens[1] == 'xxx' && tokens[2] == 'cleared'
let fromgroup = tokens[0]
let togroup = get(s:known_links, fromgroup, '')
if !empty(togroup)
execute 'hi link' fromgroup togroup
let num_restored += 1
endif
endif
endfor
endfunction
function! s:AccurateColorscheme(colo_name)
call <SID>Find_links()
exec "colorscheme " a:colo_name
call <SID>Restore_links()
endfunction
command! -nargs=1 -complete=color MyColorscheme call <SID>AccurateColorscheme(<q-args>)

Vim: How do I see whether a command is switched on?

I want to implement a function that cycles through setting the line numbers.
I'm having trouble evaluating the state of the number command. For instance I have tried:
function! CycleNumbers()
if exists(":number")
set nonumber
elseif exists(":nonumber")
set number
endif
endfunction
Is there a way to test for number being off? Something like number==off?
Thank you for your help
why not check the option value? something like :
let &number = &number? 0: 1
or simply as you said in comment, set nu!
to get the information, if line number is shown, read &number variable. if it's 1 the number is currently showing. 0, not.
something like:
if &number
"showing
else
"not showing
endif
fill your logic codes there.
You can toggle an option with the exclamation mark (on the command line):
:set number!
See also :help set-!
If you insist on having a function, then you want something like
fu! CycleNumbers()
set number!
endfu
Edit
If you want to query the current value of the option, you use the &option syntax:
if &number == 0
...
else
...
endif

why my tabpagenr always returns 1

I have this loop in my .vimrc to display the tab title as "1: File1.txt" or "2: File2.tx", etc, but both tabpagenr('$') and tabpagenr() always returns 1 no matter how many tabs I open. What am I doing wrong?
for t in range(tabpagenr('$'))
if (t + 1) == tabpagenr()
let &titlestring = t + 1 . ': '
endif
endfor
let &titlestring .= expand("%:M")
if &term == "screen" || &term == "xterm"
set title
endif
It looks like there are some bits missing from your sample code: how do you expect to change your tab labels with only those few lines?
Anyway, without an argument, tabpagenr() returns the number of the current tab. Since you are always in the same tab during your loop, that function always returns the same number.
:help setting-tabline has an example, did you read it?
You didn't tell us on which events your code is executed. If you plainly put this in your ~/.vimrc, it will only be executed once during Vim startup. You need to use :autocmd to update the 'titlestring', at least on every tab page change (i.e. the TabEnter event), or better use an expression in the option to have it continuously evaluated:
:set titlestring=%{tabpagenr()}

How to clear output of function call in VIM?

I know my question title is not explanatory enough so let me try to explain.
I created a vim function that displays my current battery state. My function is as follows:
function! BatteryStatus()
let l:status = system("~/battery_status.sh")
echo join(split(l:status))
endfunction
I have mapped the above function as nnoremap <F3> :call BatteryStatus()<cr>. Now, when I press F3 it displays my battery status as Discharging, 56%, 05:01:42 remaining which is my required output but my question is how do I make the above output disappear.
Currently what happens is after function call it continuously displays the output and I have to manually use :echo to clear the command window(:).
So, what necessary changes are to be made in my function so that I can achieve toggle like behaviour.
battery_status.sh
acpi | awk -F ": " '{print $2}'
PS: This is part of a learning exercise. So, please don't suggest alternative vim scripts.
Simplistic straightforward way to toggling the output:
let s:battery_status_output_flag = "show"
function! BatteryStatus()
if s:battery_status_output_flag == "show"
let l:status = system("~/battery_status.sh")
echo join(split(l:status))
let s:battery_status_output_flag = "clear"
else
echo ""
let s:battery_status_output_flag = "show"
endif
endfunction
Note s: prefix, see :help script-variable
You can define a autocmd:
:au CursorHold * redraw!
Vim redraws itself 4 sec (set by updatetime option) after it's idle.
vim
function! pseudocl#render#clear()
echon "\r\r"
echon ''
endfunction
You can just Ctrl+ L to clear the message on the status
line.

modifying a vim function constantly giving me: "Not enough arguments for function"

I have this function:
function! Find(name)
let l:list=system("find . -name '".a:name."' | perl -ne 'print \"$.\\t$_\"'")
let l:num=strlen(substitute(l:list, "[^\n]", "", "g"))
if l:num < 1
echo "'".a:name."' not found"
return
endif
if l:num != 1
echo l:list
let l:input=input("Which ? (CR=nothing)\n")
if strlen(l:input)==0
return
endif
if strlen(substitute(l:input, "[0-9]", "", "g"))>0
echo "Not a number"
return
endif
if l:input<1 || l:input>l:num
echo "Out of range"
return
endif
let l:line=matchstr("\n".l:list, "\n".l:input."\t[^\n]*")
else
let l:line=l:list
endif
let l:line=substitute(l:line, "^[^\t]*\t./", "", "")
execute ":e ".l:line
endfunction
command! -nargs=1 Find :call Find("<args>")
When I try adding a parameter, so the declaration becomes function! Find(name, search_dir), it always tells me i don't have enough parameters when I call the function in vim using :Find x y(where as Find: x would work when ther was only 1 parameter in the function declaration.
Any idea how I can add multiple parameters?
The end goal is to have a Find function that finds in a specified subdirectory.
An addition to #Peter Rincker answer: you should never use "<args>" if you want to pass parameter to a function as there is already a builtins <q-args> and <f-args> which do not need additional quoting and do not introduce a possibility of code injection (try Find ".string(g:)." with your code). First will pass all parameters as one item, second will produce a list of parameters suitable for a function call:
command -nargs=+ Find :call Find(<f-args>)
Another things to consider:
(system() call) Never pass user input to shell as-is, use shellescape(str, 1). You may have unexpected problems here.
strlen(substitute(l:input, "[0-9]", "", "g"))>0 condition is equivalent to input=~#'\D', but is much bigger.
You don't need to specify l:: it is the default scope inside functions.
There is a built-in glob() function: the whole system() line can be replaced with
join(map(split(glob('./*'.escape(a:name, '\`*[]?').'*'), "\n"), 'v:key."\t".v:val'), "\n")
Don't forget to escape everything you execute: execute ":e ".l:line should be written as execute "e" fnameescape(line) (it is the third place where code injection is possible in such a simple code snippet!).
It is better to use lists here, in this case you don't need to use something to add line numbers:
function s:Find(name)
let list=split(glob('./*'.fnameescape(a:name).'*'), "\n")
if empty(list)
echo string(a:name) "not found"
return
endif
let num=len(list)
if num>1
echo map(copy(list), 'v:key."\t".v:val')
let input=input("Which?")
if empty(input)
return
elseif input=~#'\D'
echo "Not a number"
return
elseif input<1 || input>num
echo "Out of range"
return
endif
let line=list[input]
else
let line=list[0]
endif
execute "e" fnameescape(line)
endfunction
command! -nargs=1 Find :call Find(<f-args)
Neither you nor me handle the situation where filename that matches pattern contains a newline. I know how to handle this (you can see my vim-fileutils plugin (deprecated) or os.listdir function of os module of frawor plugin (in alpha stage, not posted to vim.org)). I don't think such situation is likely, so just remember that it is possible.
You need to change -nargs=1 to -nargs=+. This will mean you have to have arguments but does not specify a number. I suggest you change your Find function to Find(...) and use a:0 get the number of arguments to error out if an invalid number of arguments are used.
Example function and command with multiple parameters:
command! -nargs=+ -complete=dir Find call Find(<f-args>)
fun! Find(name, ...)
let dir = getcwd()
if a:0 == 1
let dir = getcwd() . '/' . (a:1 =~ '[/\\]$' ? a:1 : a:1 . '/')
elseif a:0 != 0
echohl ErrorMsg
echo "Must supply 1 or 2 arguments"
echohl NONE
endif
let &efm = "%f"
cexpr []
caddexpr split(glob(dir . '**/*' . escape(a:name, '\`*[]?') . '*'), '\n')
copen
aug Find
au!
au BufLeave <buffer> ccl|aug! Find
aug END
endfun
For more help
:h :command-nargs
:h ...
Edit
Added example of function that excepts multiple parameters as suggest by #ZyX.

Resources