How to get command line arguments from VimL? - vim

How can I access the arguments that were given to Vim on the command line under Windows?
I'm looking for the the equivalent to argv[] in C.
Under linux you can read /proc/self/cmdline.
Example:
vim -c ":echo split( readfile( \"/proc/self/cmdline\", 1 )[0], \"\n\" )"
print
[
'vim',
'-c',
':echo split( readfile( "/proc/self/cmdline", 1 )[0], "\n" )'
]
argc() and argv() don't work. They just return number of files.
Example:
vim -c ':echo "argc:" . argc() . ", argv:" . string( argv() )' file1 file2
print
argc:2, argv:['file1', 'file2']

:help argc()
:help argv()
:help argidx()
And maybe also :help argument-list, just to be sure.
Simple example:
for arg in argv()
echo arg
endfor

In the specific case of needing argv[0] (i.e., the name vim was called by), you can use v:progname.

Related

pipe string to bash command in a function

I have a string (as a result of another function, but for now let's store it in s for sake of simplicity) and I want to write it out, through a pipe to a bash command. (It has to be a pipe, the command does not accept this kind of input as an argument.)
So the question is, how should I invoke mycommand, that is, what goes to ...?
function! MyFunc()
let s = "my string"
execute ... !mycommand --flag
endfunction
Via here string:
:let s = "my string"
:set shell=/bin/bash
:exe "!cat <<< " . shellescape(s)
Output
my string
Via pipe:
:exe "!echo " . shellescape(s) . " | cat "
Could you try this
:execute "!\"".s."\" | mycommand"

How to write a .vimrc function to match a filename in a given file and open it with the vsplit command?

I have a file called Index.txt with the following lines:
/Project/A/B/C/D/main.c
/Project/A/B/C/D/main_backend.c
/Project/A/B/C/D/main_frontend.c
I'd like to create a command called Fsearch to execute a search in Index.txt using regular expressions, match the first occurrence and execute :vsplit with it. For example, if I execute:
:Fsearch main_backend.c
Vim should execute:
:vsplit /Project/A/B/C/D/main_backend.c
and If I execute:
:Fsearch main*.c
Vim should execute:
:vsplit /Project/A/B/C/D/main.c
This is what I've tried so far but I'm pretty sure it could be improved:
function! CopyMatches(reg)
let l:file = grep -m 1 a:reg ~/Index.txt
echom l:file
if len(l:file) > 0
exec ':vsp ' . l:file
else
echom 'File not found: ' . l:file
end
endfunction
command! -nargs=* Fsearch call CopyMatches('<args>')
Any suggestion?
You could try this:
function! CopyMatches(reg)
execute "silent! grep!" a:reg " ~/Index.txt"
redraw!
let l:file = getqflist()
if len(l:file) > 0
let l:path_head = fnamemodify( "~/workspace", ":p" )
for l:item in l:file
let l:current_file = l:path_head . "/" . l:item["text"]
if match( l:current_file, getcwd() ) != -1
execute 'vsplit' fnamemodify( l:current_file, ":~:.")
return
endif
endfor
echom "File not found:" a:reg
else
echom "File not found:" a:reg
endif
endfunction
command! -nargs=* Fsearch call CopyMatches('<args>')
Explanation:
The :grep built-in command is a wrap used by Vim to execute an external grep (see :help grep for more information).
The :grep! form of the :grep command doesn't jump to the first match automatically (i.e., :grep! won't open Index.txt).
The :silent! command is used to suppress the default full screen grep output.
Vim uses quickfix list with :grep so you can get all occurrences from getqflist() function (see :help getqflist() for details)

How to setup vimscript command to call a function?

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.

Vim—make lines in a buffer enterable

I wrote the following one-liner command.
command! -nargs=? Gfind execute "split | enew | cd `git-pwd` | read !git ls-files | grep " . expand("<args>") . " " <bar>
(
git-pwd returns the current get repo's root or './'
#!/bin/bash
git-pwd() {
local root=$(git rev-parse --show-cdup 2>&1 )
[[ "$root" == "" ]] && { echo "./" && return; }
[[ "$root" == fatal:* ]] { echo "./" && return 1; }
echo "$root"
}
[[ "$BASH_SOURCE" == "$0" ]] && git-pwd "$#"
)
It allows me to do :Gfind regex and a split window opens up with the list of files whose path/name matches the regex. That I can use for navigation with gf.
I wonder if there's an easy way to make the returned files in that window enterable like in cwindow or NERDTree.
Alternatives
Use :Gedit with completion abbreviated paths. e.g. :Gedit f/b/b<tab> which will expand to :Gedit foo/bar/baz (Requires fugitive.vim)
Glob with :Gedit. e.g. :Gedit **/baz<tab>
Use a fuzzy finder like CtrlP.vim to jump around your project.
Use something like projectionist.vim to build navigation commands for a well structured project.
There are low tech solutions that use :find and 'path':
set path=.,**
nnoremap <leader>f :find *
These are not mutually exclusive as you can use all these methods as each have their own pro's and con's. I personally use projectionist.vim as my main navigation method.
Answer to your question
I would imagine the best thing would be to use the QuickFix list. Your command would populate the quickfix list with the files matching your pattern and then you can use :copen to actually see the files and move between them or use :cnext/:cprev type commands to move between files.
Put the following in your ~/.vimrc file:
command! -nargs=? Gfind call s:gfind(<q-args>)
function! s:gfind(pat)
let grepprg = &grepprg
let errorformat = &errorformat
let &grepprg = 'cd ' . shellescape(fugitive#repo().dir()) . ' && git ls-files | grep '
let &errorformat = '%f'
execute 'grep ' . a:pat
cwindow
let &grepprg = grepprg
let &errorformat = errorformat
endfunction
Notes: I have not tested this code. It also uses fugitive.vim, but you can use your git-cwd trick if you rather. You may want to remove cwindow command to depending on your workflow.

How do I write a vim function to output the result of a system command?

What I have so far:
function! GetMarker()
return system('echo $random `date` | md5sum | cut -d" " -f1')
endfunction
I would like to be able to do a :getmarker and have it insert the output of that system command at my cursor, with no new lines.
Also what is the difference between function! and function?
Edit: before any of you ask, I need the random string to mark sections in my code so I can find them again by referencing my notes in my todo wiki.
Edit1. Take two. Trying to absorb the feedback from Luc. Without temp file (readfile() turned out to be not available in VIM 6.x I have on some systems).
:function InsertCmd( cmd )
: let l = system( a:cmd )
: let l = substitute(l, '\n$', '', '')
: exe "normal a".l
: redraw!
:endfunction
:imap <silent> <F5> <C-O>:call InsertCmd( 'echo date \| md5sum \| cut -d" " -f1' )<CR>
:map <silent> <F5> :call InsertCmd( 'echo date \| md5sum \| cut -d" " -f1' )<CR>
:put can't be used because it works line-wise. I replaced <Esc>...<Insert> with the all better <C-O>. I left redraw in, as it helps for the cases of called command produces output to the stderr.
Or using <C-R>=:
:function InsertCmd( cmd )
: let l = system( a:cmd )
: let l = substitute(l, '\n$', '', '')
: return l
:endfunction
:imap <silent> <F5> <C-R>=InsertCmd( 'echo date \| md5sum \| cut -d" " -f1' )<CR>
Also what is the difference between function! and function?
Exclamation on the end of command most of the time means force to execute. (Looking in the :help is advised since different commands use ! differently, but VIM tries to document all forms of the commands.) In the case of the function it tells VIM to override previous definition of the function. E.g. if you put the code above into the func1.vim file, first time :source func1.vim would work fine, but the second time it would fail with error that function InsertCmd is already defined.
I did once before try to implement something similar here. I'm not good at VIM programming, thus it looks lame and the suggestion from Luc should take precedence.
Here it goes anyway:
:function InsertCmd( cmd )
: exe ':silent !'.a:cmd.' > /tmp/vim.insert.xxx 2>/dev/null'
: let l = readfile( '/tmp/vim.insert.xxx', '', 1 )
: exe "normal a".l[0]
: redraw!
:endfunction
:imap <silent> <F5> <Esc>:call InsertCmd( 'hostname' )<CR><Insert>
:map <silent> <F5> :call InsertCmd( 'hostname' )<CR>
Despite being lame, it works though.
You can trim/chomp the last newline with matchstr(), substitute, [:-2], etc
function s:GetMarker()
let res = system('echo $random `date` | md5sum | cut -d" " -f1')
" then either
let res = matchstr(res, '.*\ze\n')
" or
let res = res[:-2]
" or
let res = substitute(res, '\n$', '', '')
return res
endfunction
command! -nargs=0 GetMarker put=s:GetMarker()
Banging the function/command definition (with '!') will permit you to source the script where it is defined several times and thus to update the function/command you are maintaining without having to exit vim.
I was running into similar problems with trying to map a hotkey to insert the current date and time. I solved the newline problem by just including a <backspace>, but this still inserted newlines when I was indented (backspace would kill the last character, but when I was indented I got newline+tab and only the tab would go away).
So I did this -- just turned smartindent off, insert the string, then turn it back on:
imap <F5> <esc>:set nosmartindent<CR>a<C-R>=system('echo $random `date` \| md5sum \| cut -d" " - f1')<CR><Backspace><esc>:set smartindent<CR>a
...which works, but it gets un-indented if you're sitting on a new, auto-indented line. To get around that, insert a character to hold your place, then escape, turn off smartindent, get rid of the extra character, and do the rest:
imap <F5> x<esc>:set nosmartindent<CR>a<backspace><C-R>=system('echo $random `date` \| md5sum \| cut -d" " -f1')<CR><Backspace><esc>:set smartindent<CR>a
This seems to work.

Resources