So I want to be able to type something like:
:hello
in vim normal mode and then have it echo out 'hello sir'.
I made the below vimscript into a plugin, and I get the error:
Not an editor command
What am I doing wrong?
vimscript
if exists("g:hello_world") || &cp || v:version < 700
finish
endif
let g:hello_world=1 " your version number
let s:keepcpo = &cpo
set cpo&vim
fun! s:hellosir()
echo 'hello sir'
endfun
command hello call hellosir()
steffen's answer is correct, but here is an example with an argument:
" a highlight color must be set up for the function to work
highlight blue ctermbg=blue guibg=blue
function! Highlight(text)
:execute "match blue /" . a:text . "/"
endfunction
:command! -nargs=1 Highlight :call Highlight(<q-args>)
To run it and highlight all occurrences of a regex:
:Highlight foobar
Note that I try not to abbreviate commands/functions in .vimrc. Save abbreviations for when you're typing on the command line.
Define your function (note the uppercase letter for user-defined functions):
:fun! Hellosir()
: echo 'hello sir'
:endfun
Now call it:
:call Hellosir()
hello sir
It is also possible to define your own ex command:
:command Hello :call Hellosir()
:Hello
hello sir
EDIT
You can combine both: Make the function script-local and access it with your (global) ex command:
fun! s:hellosir()
echo 'hello sir'
endfun
command Hello call s:hellosir()
What is wrong relates with this line:
command hello call hellosir()
First, from :h user-cmd-ambiguous
All user defined commands must start with an uppercase letter, to avoid
confusion with builtin commands. Exceptions are these builtin commands:
:Next
:X
They cannot be used for a user defined command. [...]
Second, that you have to call your function with the s: prefix in that command.
Related
How can I use vim to detect blank lines containing whitespace and delete the whitespace?
For example, I use ⎵ to represent whitespace:
def⎵function(foo):
⎵⎵⎵⎵print(foo)
⎵⎵
⎵
function(1)
Is there a vim command that converts the code above to the following?
def⎵function(foo):
⎵⎵⎵⎵print(foo)
function(1)
:g/^\s\+$/s/\s\+//
Explanation:
g — execute the command globally (for all lines)
/^\s\+$/ — search lines that contain only whitespaces
s/\s\+// — for every found line execute this
search and replace command:
search whitespaces and replace with an empty string.
Could be simplified as
:%s/^\s\+$//
% — execute for all lines
s/^\s\+$// — search and replace command:
search lines that only have whitespaces
and replace with an empty string.
I have a function that solves this problem and keeps your cursor position
if !exists('*StripTrailingWhitespace')
function! StripTrailingWhitespace()
if !&binary && &filetype != 'diff'
let b:win_view = winsaveview()
silent! keepjumps keeppatterns %s/\s\+$//e
call winrestview(b:win_view)
endif
endfunction
endif
command! Cls call StripTrailingWhitespace()
cnoreabbrev cls Cls
cnoreabbrev StripTrailingSpace Cls
nnoremap <Leader>s :call StripTrailingWhitespace()
You can use a command :cls or a shortcut <leader>s.
Actually you can change it to fit your needs.
How can I make vim's :global command ask the user if they want to execute the ex command? Similar to what happens with the :substite command with the 'c' option, for example %s:Foo:Fighters:gc
I tried:
:g/mypattern/.s:.*\n::gc
and
:g/mypattern/s:.*\n::gc
but if there is a match on below line it is jumped. For example:
MATCH
NONMATCH
MATCH
MATCH
MATCH
The result is:
NONMATCH
MATCH <<-- this should be erased.
A promptable g/FOO/d would be perfect.
There is no native way to do this. The typical method would be to record a macro and repeat a macro. Making sure you n or / at the end of the macro to advance to the next match. Skipping is now simply n and ## to execute the macro.
Custom :Global command
However if you truly want to have :global command with a confirm you can sort of mimic this by using confirm() inside the your command. The general idea is to do something like this:
:g/pat/if confirm("&yes\n&no", 2) == 1 | cmd | endif
This doesn't quite work for the following reasons:
You have no idea where your cursor is. Need something like :match and :redraw
Does not abort well. Need a way to throw an exception to abort
Very unwieldily to type this all out
I have come up with the following confirming :Global/:G command
command! -nargs=+ -range=% -complete=command Global <line1>,<line2>call <SID>global_confirm(<q-args>)
command! -nargs=+ -range=% -complete=command G <line1>,<line2>call <SID>global_confirm(<q-args>)
function! s:global_confirm(args) range
let args = a:args
let sep = args[0]
let [pat, cmd; _] = split(args[1:], '\v([^\\](\\\\)*\\)#<!%d' . char2nr(sep), 1) + ['', '']
match none
let options = ['throw "Global: Abort"', cmd, '', 'throw "Global: Abort"']
let cmd = 'exe ''match IncSearch /\c\%''.line(''.'').''l''.#/.''/'''
let cmd .= '| redraw'
let cmd .= '| exe get(options, confirm("Execute?", "&yes\n&no\n&abort", 2))'
try
execute a:firstline . ',' . a:lastline . 'g'.sep.pat.sep.cmd
catch /Global: Abort/
finally
match none
endtry
endfunction
Note: Use as-is. Uses IncSearch for highlight and forces \c.
Now you can run :G/foo/d.
Custom :Confirm command
If you rather use a similar technique to the one #Randy Morris provided and use the following :Confirm {cmd} command to confirm {cmd} before execution.
command! -nargs=+ -complete=command Confirm execute <SID>confirm(<q-args>) | match none
function! s:confirm(cmd)
let abort = 'match none | throw "Confirm: Abort"'
let options = [abort, a:cmd, '', abort]
match none
execute 'match IncSearch /\c\%' . line('.') . 'l' . #/ . '/'
redraw
return get(options, confirm('Execute?', "&yes\n&no\n&abort", 2), abort)
endfunction
This will allow you to use :g/foo/Confirm d
For more help see:
:h #
:h q
:h confirm()
:h :exe
:h get()
:h :match
:h :redraw
As far as I know there is no way to do this natively. I think I've hacked together a way to do this but it's probably buggy as I haven't written vimscript in a long time. In this I've defined a command C which accepts an ex command as its arguments. Each line returned via :global is then passed to this ex command if you press y or Y. Any other key causes this line to be skipped.
let s:GlobalConfirmSignNumber = 42
sign define GlobalConfirmMarker text=>> texthl=Search
function GlobalConfirm(cmd)
let line = getpos(".")[1]
execute "sign place " . s:GlobalConfirmSignNumber . " line=" . line . " name=GlobalConfirmMarker file=" . expand("%:p")
redraw!
echomsg "Execute? (y/N) "
try
let char = nr2char(getchar())
if (char == "y" || char == "Y")
execute a:cmd
endif
finally
" Ensure signs are cleaned up if execution is aborted.
execute "sign unplace " . s:GlobalConfirmSignNumber
endtry
redraw!
endfunction
command -nargs=* C call GlobalConfirm(<q-args>)
Here's a gif of it in action. In this gif I'm running the command norm! gUU for every line which contains ba. In this case I confirmed every match by pressing y three times.
If anyone can make improvements to this (especially the signs bit) please edit at will.
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.
Can anybody help me to get solution for my requirement?
Requirement is when a user exits from vim, cppcheck should happen and if any warning or error occurs then it should be prompted to a user.
Thanks in advance.
I assume you don't care if the command is executed asynchronously, since you're quitting the buffer anyway. You can use the :! command to run shell commands and :read to capture the output to a new window:
function! s:RunShellCommand(cmdline)
let first = 1
let words = []
" Expand and escape cmd arguments.
" shellescape() should work with '\'
for part in split(a:cmdline)
if first
" skip the cmd. ugly, i know.
let first = 0
else
if part[0] =~ '\v[%#<]'
let part = expand(part)
endif
let part = shellescape(part, 1)
endif
call add(words, part)
endfor
let expanded_cmdline = join(words)
" Create the new window
botright new
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap
call setline(1, 'Showing output from cmd: ' . expanded_cmdline)
call append(line('$'), substitute(getline(2), '.', '=', 'g'))
" This is where actual work is getting done :-)
silent execute '$read !'. expanded_cmdline
" Uncomment the line below if you want the buffer to be
" non-modifiable
" setlocal nomodifiable
1
endfunction
Then you can define an autocommand for when a buffer is unloading:
au BufUnload *.cpp s:RunShellCommand('cppcheck %')
or a somewhat more generic command which you can call at any time:
command! -complete=shellcmd -nargs=+ Shell call s:RunShellCommand(<q-args>)
Now, to prevent closing your buffer, you have to remap :wq or :q to a function that will perform the aforementioned (plus perhaps some confirmation?), since once :quit is invoked, it cannot be aborted.
At the moment I'm using two different keys to toogle the colorscheme
map <F8> :colors wombat256 <cr>
map <F9> :colors dimtag <cr>
I want to achieve a toggle behavior like this
function! ToggleDimTags()
if (g:colors_name == "wombat256")
colors dimtag
else
colors wombat256
endif
endfunction
My problem is that ToogleDimTags() is resetting the cursor position to the first line on every call, which is undesirable. Any suggestions appreciated.
As discussed in the comments, the problem is that your map calling :execute
behaves a little differently, what you probably want is :call instead:
nnoremap <F5> :call ToggleDimTags()
To clarify what #ZyX was saying, :h :exec contains the following text:
:exe :execute
:exe[cute] {expr1} .. Executes the string that results from the evaluation
of {expr1} as an Ex command.
[...]
So what :execute really does is evaluating the expression looking for a string
that will be executed as an Ex command (also called colon commands). In other words:
exec ToggleDimTags() | " <-- ToggleDimTags() is evaluated and returns 0
exec 0
Which is:
:0
Now, :h :call:
:cal :call E107 E117
:[range]cal[l] {name}([arguments])
Call a function. The name of the function and its arguments
are as specified with |:function|. Up to 20 arguments can be
used. **The returned value is discarded**.
[...]
Update
I've been thinking about your function, and using the ternary operator and a bit
of :execute magic, you can simplify it up to a point where you discard the extra
function:
nnoremap <silent> <F9> :exec "color " .
\ ((g:colors_name == "wombat256") ? "dimtag" : "wombat256")<CR>
Here, this nnoremap will not produce output (<silent>) and is based on
:exec, which is followed by this expression:
"color " . ((g:colors_name == "wombat256") ? "dimtag" : "wombat256")
When g:colors_name is set to wombat256, the expression evaluates to:
"color dimtag"
Or, otherwise:
"color wombat256"
Then either one is evaluated by :exec. Of course you can join the lines
(without forgetting to remove the backslash), I did it like this simply to avoid
a too long line.