Vim - get output of EX mode into a variable - vim

I'm writing a vim script where I need to get the first line of the current buffer. In Ex mode I can simply type 1 and it shows me the content I want.
How can I put the output of the ex command into a variable in vim?

Chris's answer is the right approach.
Note however, that you can use the :redir command to capture the output of an Ex command into a variable:
:let myvar = ""
:redir => myvar
:command
:redir END
See :h :redir for more information.

The expression you want is getline(1). Thus, let x = getline(1).

Related

How to move to end of a file upon opening it via a command in .vimrc using vim/MacVim?

I'm trying to open a file using a command I set in my .vimrc file. The relevant line in my .vimrc is similar to the following:
command Of so /Users/Dude/Working/open_file.txt
With open_file.txt containing the following:
tabnew /Users/Dude/Working/Project/config.txt
What I'd like to do when executing the 'Of' command is navigate to the end of config.txt. I've tried adding a large line number which is unlikely to exceed the number of lines in the file like so:
tabnew /Users/Dude/Working/Project/config.txt
250000
This takes me to the end of the file but doesn't seem like the right way to do it. Ideally, I'd also like to add a new line after the last line and navigate there too.
A few things:
I would suggest you use full names instead of short names. e.g. so -> source.
source is probably the wrong choice here as you can do everything with the right-hand-side of command
May want to use ! with command so you can resource your vimrc file. e.g. command! Of ...
$ represents the last line of the file. No need to choose a magic number
Create a new line can be done with :normal o or :put _
So with some tweaks we get the following command:
command! Of tabedit /Users/Dude/Working/Project/config.txt | $put_
For more help see:
:h :command
:h :put
:h :range
:h :bar
Have a look at :h :normal in your case just write :norm Go instead of your number there.
:tabnew, like most variants of :edit (and the command-line arguments when launching Vim), takes arbitrary Ex commands via the [+cmd] argument. The $ command will move to the end of the file:
tabnew +$ /Users/Dude/Working/Project/config.txt

Capture Line numbers of lines matching a search

Example:
search:
/lastword\s*\n\zs\(\s*\n\)\{6}\ze\s*startword
This search searches 6 empty lines between a line ending with "lastword" and a line starting with "startword"
I would like to catch the linenumbers of the empty lines matching this search.
Is there a way to do this in vim?
You can use :g and :number (or :# for short) to print out the lines with line numbers.
:g/lastword\s*\n\zs\(\s*\n\)\{6}\ze\s*startword/#
To capture this content you have to use :redir to redirect the output to somewhere else. In this case below we redirect it to the unamed register (#")
:redir #"|execute 'g/pattern/#'|redir END
Note: Must use :execute with :g otherwise redir END will be executed on each matching line with the :g command.
Now as it is this is going to print via :# the starting line which is not what we want (we want the empty lines between foo and bar). We can use a range with the :# command to accomplish this.
:redir #"|execute 'g/foo\_.*bar/+,/bar/-#'|redir END
The range is now +,/bar/- which translate to start the next line (+) and the search for bar (/bar/) then subtract one line (-). We can probably simplify this some as we know the number of empty lines should be 6.
:redir #"|execute 'g/foo\_.*bar/+#6'|redir END
We can take this further by putting the content into a new buffer and remove the extra lines.
:redir #"|exe 'g/pattern/+#6'|redir END|new|pu|%s/\d\+\zs.*//|%le|g/^$/d
There is a lot going on here, but the idea is we capture the output, open a new buffer, paste the content, and then clean up the output.
Alternative
Alternatively awk might make this a bit easier. Run the following on the current Vim buffer:
:%!awk '/lastword\s*$/,/^\s*startword/ { if($0 ~ /^\s*$/) { print NR} }'
For more help see:
:h :g
:h :redir
:h :exe
:h :range
:h :new
:h :pu
:h /\zs
:h :le
:h :d

VIM check for search pattern match in vim script

I just started with vim script and try make translation of opencart language files easier. I want to have a function that looks for a given search pattern and selects it. If there is no match left in the file, it shall open the next file for editing. What I have so far:
function! Nextmatch()
normal /\(= *'\)\#<=.\{-\}\('\)\#=/
normal v//e
endfunction
function! Nextfile()
if !exists("s:filecounter")
execute "!find -iname *.php > files.txt"
normal <CR>
let s:filecounter=0
endif
let s:lines= system("wc -l < files.txt")
if s:filecounter<s:lines
w
let s:filecounter += 1
let s:sedcommand="sed '".s:filecounter."!d' files.txt"
let s:selectedfile=system(s:sedcommand)
execute 'edit' s:selectedfile
else
wq
endif
endfunction
How can I achieve that Nextfile() is called in Nextmatch() if the search pattern is not found between the cursor and the end of the current file? And is there something that you consider to be bad style in my snippet?
Quickfix commands are powerful and well integrated with some external plugins, but if you really need to use your own script, and if you need to check a match in an if statement, just do:
if search("=\\s*'\\zs[^']*\\ze", 'W') == 0
echo 'No match until the end of the buffer'
endif
See :h search(), and please note :
the double backslashes, due to the double quotes
the 'W' flag which forbids wrapping around the end of file
I simplified the pattern you gave
You could simply use the :vim command to get rid of all your script.
I think the following should do quite what you're expecting:
:noremap <f8> <esc>:cn<cr>gn
/\(= *'\)\#<=.\{-\}\('\)\#=
:vim //g *.php
Then, to go to the next pattern in all files while selecting it,
you just have to press the F8 key.
In the noremap line, gn let you select the next actual search.
You may need to do:
:set nohidden
to let you navigate threw modified buffers (but don't forget to save
them with :wa, or list them with :ls)
About your script:
It's a good habit in scripts to always use :normal! instead of :normal (unless you deliberately need it) : thus, your personnal mappings won't interfer in your scripts.

Vimscript: How to use a parameter with a function that uses read?

I just started learning Vimscript.
I want to write a function that opens the output of a bash function in a split.
If I do something like :call OutputScript("echo 'hello'"), I want a new buffer to open that has "hello" in it.
I tried the following code:
function! OutputScript(cmd)
if winbufnr(2) == -1
silent vsplit output
else
silent wincmd l
endif
silent normal ggdG
read! a:cmd
endfunction
If I :call OutputScript("echo 'hello'"), my output window looks like this:
bash: a:cmd: command not found
What syntax do I need to use cmd as a parameter for read?
Juste replace read! a:cmd by:
exe "read! ".a:cmd
The read command, as most of vim commands, don't expect vim variables. In order to use variables with common commands, you have to "encapsulate" it into an exe command, which can understand any vim expression.
The . is the vim operator for concatenating two strings.

Vim: Pipe selected text to shell cmd and receive output on vim info/command line

I want to pipe the selected text to a shell command and receive the one-line output from this shell command on the vim info/command line?
What I'm really trying to do: Pipe the selected text to a pastebin-type shell command and I want to receive the output of the shell cmd (which is the http link to the pastebin). Is this possible?
For multi line version you can do this after selecting the text:
:'<,'>:w !command<CR>
See the official Vim docs at :help :w_c.
You can map it to simple Visual mode shortcut like this:
xnoremap <leader>c <esc>:'<,'>:w !command<CR>
Hit <leader key>+c in visual mode to send the selected text to a stdin of the command. stdout of the command will be printed below vim's statusbar.
Real world example with CoffeeScript:
https://github.com/epeli/vimconfig/commit/4047839c4e1c294ec7e15682f68563a0dbf0ee6d
Simply highlight the lines using visual line select shift-v, the hit :! and type the command you wish to send the commands to. The resulting output will then replace your selected text.
When you type your command it will appear at the bottom as:
:'<,'>!somecmd
the '<,'> is indicating that the range you have visually selected will be passed to the command specified after the !
I would do it like this:
Place this function in your vimrc:
function Test() range
echo system('echo '.shellescape(join(getline(a:firstline, a:lastline), "\n")).'| pbcopy')
endfunction
This will allow you to call this function by doing:
:'<,'>call Test()
Then you can also map that like this (just under the function declaration in your vimrc):
com -range=% -nargs=0 Test :<line1>,<line2>call Test()
So you can call the function doing this:
:'<,'>Test
Note: :<','> are range selectors, in order to produce them just select the pertinent lines in visual mode and then go to command mode (pressing the colon key)
Maybe you should use something like
:echo system('echo '.shellescape(#").' | YourCommand')
Starting from some vim-7.4 version it is better to use
:echo system('YourCommand', getreg('"', 1, 1))
. This is basically the only way to keep NUL bytes untouched should they be present in the file. Passing #" in one or the other way will transform NUL bytes into NL (newline).
#matias 's solution is not work well for me, because it seems shellescape will append \ to each line.
So I use sed to accomplish this, and it works just fine!
"dump selected lines
function! DumpLines() range
echo system('sed -n '.a:firstline.','.a:lastline.'p '.expand('%'))
endfunction
com! -range=% -nargs=0 Dump :<line1>,<line2>call DumpLines()
An imperative way to do it is to:
yank your selection
drop into command mode with :
! + paste the register in the command-line like <Ctrl> r "
So: y : ! <Ctrl> r "
Another answer:
function Pastebin() range
let savedreg=#"
silent execute a:firstline.",".a:lastline."yank"
python import vim, subprocess
python p=subprocess.Popen(["pastebin"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
python p.stdin.write(vim.eval('#"'))
let #"=savedreg
python p.stdin.close()
python retstatus=p.poll()
python print p.stdout.read()
endfunction
Requires python support. Use it just like matias' function.

Resources