send current line to external command in vim (without capture) - vim

The goal is to use the current line as a TODO and send this to some external program. Something like this:
:! /usr/bin/todo "content of current line"
I know of the filtering command but this implies that I want to edit the current buffer which I do not want (:.! acts as a filter). I know how to get the current file with '%' but isn't there any way to get some other content ? Maybe by using :execute ...

:.! works as a filter, but :.w ! (mind the space!) just passes the output. See :help :w_c. I.e.
:.w !/usr/bin/todo -

You can insert contents of registers into command line, so doing something like:
"1y$ //yank current row to register 1
: CTRL-R 1 //CTRL-R followed by register id pastes register to command line
should do the trick.

You might like something like these mappings (i.e. saved in your .vimrc or pasted into a : prompt):
cmap <C-R>' <C-R>=shellescape(getline('.'))<CR>
cmap <C-R><C-R>' <C-R><C-R>=shellescape(getline('.'))<CR>
Once installed, you use them like this:
:!/usr/bin/todo ^R'
(type an actual Control‑R where the above example shows ^R).
You can think of them as command-line mode versions of the registere-based Control‑R and Control‑R Control‑R (see :help c_CTRL-R, and :help c_CTRL-R_CTRL-R) where the “imaginary” register ' always contains the shell-quoted contents of the current line.
Because these mappings use the same prefix as built-in mappings (see the :help topics mentioned above), you must enter the final single quote within timeoutlen milliseconds (see :set timeoutlen?), or it will default to the built-in mapping (see :help map-typing).

Related

In vim toggle case of all characters in a text file with a single command

In Vim I need to convert all lowercase to uppercase and all uppercase to lowercase with a single command. So if my text file looks like this..
Hello World
.. it needs to be toggled to look like this..
hELLO wORLD
I know :%s/[a-z]/\U&/g will change all lowercase to uppercase and that :%s/[A-Z]/\L&/g will change all uppercase to lowercase. But how would I write that to do both at the same time?
In addition I know if my cursor is at the top of the file VG~ will toggle case everything but that's not the answer I need. Thank you.
<Esc>1GVG~
Explanation:
<Esc> — return to Normal mode; just in case we're in Insert mode or Command line
1G — jump to the 1st line
V — start Visual mode
G — jump to the last line extending selection
~ — toggle case in the selection
Or
<Esc>1Gg~G
g~<motion> — change case during motion; the motion is G (jump to last line)
Docs: http://vimdoc.sourceforge.net/htmldoc/change.html#~
Looks like you already know everything you need. ggVG~ marks all your code and toggles the case. If you want a single command you can either use:
:nnoremap <keybinding> ggVG~
or use this function, which does the same, but keeps your current position in the file:
function ToggleCase()
exec "normal! mqHmw"
exec "normal! ggVG~"
exec "normal! 'wzt`q"
endfunction
command ToggleCase silent call ToggleCase()
the first and last exec mark your position in the file and restore them, after the case toggling. See: :h marks
type :ToggleCase to use the function. Of cause you can bind this to a keybinding as well.
:nnoremap <keybinding> :ToggleCase<cr>
Since you mentioned using a single command and you mentioned some :%s/.../ substitutions, I'll offer this one:
:%normal! g~~
This will run the g~~ command to switch case of a single line, for each line of the buffer.
One more way to accomplish this, if you're ok adopting a plug-in, is to use the kana/vim-textobj-entire plug-in for a text object for the entire buffer.
As the plug-in README.md file says:
Though these are trivial operations (e.g. ggVG), text object versions are more handy, because you do not have to be conscious of the cursor position (e.g. vae).
With this plug-in installed and enabled, you can switch case of the whole buffer with:
g~ae

How can I get vim find to ignore whitespace?

I have XML entries that span two or more lines sometimes, and so I can look for something like this:
something on one line
But if I try the vim command /something on one line and the line is like this:
something on one
line
then it doesn't find it, because the second text block is actually seen as
something on one^J line
Which might have something to do with the fact that I'm using a DOS formatted file.
How can I get vim search to ignore whitespace and newlines?
In vim search, _s means a new line, a space or a tab. So you can try something such as:
/something on one\_s*line
to match the example string you used as example.
Replacing the spaces with \_s\+ manually is cumbersome. The Search for visually selected text topic on the Vim Tips Wiki has a mapping that does this for you for the current visual selection. You can use it like the built-in * mapping, to search (ignoring spaces) for the current selection. Very handy!
While I like the existing answers, I was looking for a way to do the substitution of spaces to \_s* "automagically" for every search that I type.
Luckily, such a solution is possible, as explained in this reddit post. I ended up using the following command:
cnoremap <expr><space> '/?' =~ getcmdtype() ? '\_s*' : ' '
This makes it so that whenever you type a space in a search command (either / or ?), all spaces that you type are automatically replaced by \_s*.
In fact, I only wanted this behavior for LaTeX files, so I added the following to my .vimrc:
autocmd FileType tex cnoremap <expr><space> '/?' =~ getcmdtype() ? '\_s*' : ' '

Reusing the previous range in ex commands in VIM

Is it possible to reuse the range of ex commands in VIM?
As an example, I can write (copy) lines 4 to 10 from my current file to a new file using the following command:
:4,10w foo/bar.txt
But what I really want to do is move the lines to a new file. I can do this like so:
:4,10w foo/bar.txt
:4,10d
But it's a bit annoying to have to type 4,10 both times.
So I have two questions:
Generally, Is there a way to reference the previously used range in ex commands?
Specifically, if there is not a way to do (1), is there any easier way to cut and paste a number of lines from one file into a new one.
I usually use cat to do this:
:4,10!cat > foo/bar.txt
This works because you're piping the lines through cat and replacing them with the resulting output, which is nothing. And of course you can append to the end of an existing file by doing >> instead of >.
I am unaware of an answer to (1), but to answer (2), there are a number of different ways of doing it that don't require reselecting the lines by hand. In visual mode this will work:
4GV10G
:w foo/bar.txt
gvd
because gv reselects the previous selection, which is almost what you want, without using an ex range.
But you could just turn the problem on its head, and try:
:4,10d
:sp foo/bar.txt
pZZ
to cut, then paste into a new file, then close it.
Other than using the Vim history (:Cursor Up, q:) and removing the previous command so that just the range is kept, there's no way to re-use the last range, no magic variable.
If I used this move lines combination more often, I would write a custom command for it:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _
You need to specify the range only once and save typing.
You can write something like this for other combinations, too. The main challenge is specifying all the command attributes (bang, range, completion), and, later, remembering the custom command name.
Generally, what I do is delete the lines from the one file, switch to the other file, and paste.
Also, I generally use marks. Instead of typing the actual numbers, I hit mb to mark the beginning line, then go to the end line and hit d'b to delete back to the line marked as b. But you can use mb to mark a begin line, and me to mark an end line, then run an ex command:
:'b,'e w somefile.txt<Enter>
Of course you can use any letters from a through z for your marks; I usually use b and e but you can use what you like.
How I would move the lines:
m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^
Ctrl+^ switches from the current open file to the previous open file. (You could also just open a pane and switch panes, if you prefer. Panes don't work in plain vi but do work in vim.)
The above assumes that you have set the autowrite option on. With autowrite, the :n command and Ctrl+^ both just write the current file and then switch files, instead of complaining that the file has been changed without you saving it. You can also do the above and just explicitly write the file before using :n or Ctrl+^.
By the way, I use Ctrl+^ so much that I mapped it onto K. Easier to type, but I got in that habit long ago when I used to have to sometimes use a dumb terminal that couldn't type Ctrl+^.
By the way, when you delete lines, they go into the "unnamed buffer". In vim, the unnamed buffer is preserved when you switch files. In original vi, the unnamed buffer is cleared. So the above won't work with old vi. You can make it work by deleting into a named buffer, then pasting from the named buffer; that works in any version of vi.
m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^
The above deletes into the buffer named a, then pastes from a in the other file. This does work in vim of course; it's just that you don't need it.
Here's a command-line mapping that achieves this. I've bound it to CTRL-G CTRL-U, since it performs a similar action as CTRL-U. (But you can change that, of course!)
" c_CTRL-G_CTRL-U Remove all characters between the cursor position and
" the closest previous |:range| given to a command. When
" directly after a range, remove it.
" Useful to repeat a recalled command line with the same
" range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\#<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)
let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
if empty(l:upToRange)
return getcmdline()
else
call setcmdpos(strlen(l:upToRange) + 1)
return l:upToRange . l:cmdlineAfterCursor
endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>
as a plugin
My CmdlineSpecialEdits plugin has (among many others) this mapping as well.
You can also do something like this to write the contents of the anonymous register to file2.txt
:4,10d | :call writefile(split(##, "\n", 1), 'file2.txt')
You can do the deleting first, and then open a new tab and paste the contents - so :4,10d, then :tabe foo/bar.txt, followed by p... does that sound better?
In Vim 8 and NVIM 0.3.7 as of writing, you can actually edit your command list and hit enter to execute.
:4,10w foo/bar.txt
q:
q: is to enter interactive ex command
Once you open the interactive command list, you can then edit it and press enter to execute.
I love moopet's answer though, it's efficient.

Custom vi command: How to insert current date with a variable's content?

I'm new to the vi editor and I would like to create a simple custom command in .vimrc that inserts something like 2012-03-13 22:21:17.0 +0100 / Daniel.
Actually, my command (in .vimrc) is as follows:
command! InsertTime :normal a<C-R>=strftime('%F %H:%M:%S.0 %z')<CR>
I also set a variable:
let myname="Daniel"
InsertTime inserts the date perfectly. But how can I concatenate it with the content of my variable?
To concatenate, vim scripts use . caracter. So try this one :
In vimrc:
let myname="Daniel"
command! InsertTime :normal a<C-R>=strftime('%F %H:%M:%S.0 %z') . "/" . myname<CR>
no tested there.
Since you said you're new to "vim" I am going to assume you don't know any of the things I'm about tell you. Mucho sorry if you already know them.
If you're going to do this a lot (insert the line "%F %H:%M:%S.0 %z / Daniel"), instead of defining a command, which you have to invoke with a :command_name, define a macro and/or an input macro that can be invoked with just two or three character.
To define an input macro, do the following at the ':' prompt, or add it to your $HOME/.exrc or $HOME/.vimrc file (without the preceding ':'):
:map <C-X><C-X> Go<ESC>!!date '+\%F \%H:\%M:\%S.0 \%z'<CR>A / Daniel<ESC>
Now when you're in "vi" (but not in input mode), typing control-Xcontrol-X will:
G go to last line in file; replace this with the "motion" keys sequence appropriate for your use (or nothing at all if you want to append the line right after the cursor)
o open a new line
<ESC> escape out of input mode
!!date ... invoke the date command, replace the current line with its stdout (output)
A append at the end of the line (now having the "date")
/ Dan... verbatim intput text
<ESC> escape out of input mode
control-Xcontrol-X can be some unusual sequence that you'd normally not use for anything, nor used by any "vi" operation that you might use. I use as the first character, because in "vi", decrements the next integer on the line after the cursor, if any. That is something I hardly ever do. I define my macros to be invoked with <C-X><C-B>, <C-X><C-D>, <C-X>s1, etc.
To create an input macro, well, that's another whole long subject, and I'm tired of typing today, so, another day. :)

Run bash command on Vim and copy result to clipboard

How can I create a Vim command and copy it's results to clipboard?
I want to convert Markdown to HTML and copy the result to the clipboard. So far I got:
nmap md :%!/bin/markdown/Markdown.pl --html4tags
But this will substitute my opened file on Vim to the result of Markdown.
You didn't say which system you're using, but generally saving it in the +
register should work. You can call system():
:let #+=system("markdown --html4tags", join(getline(1,line("$")), "\n"))
The system() function takes the second parameter (optional) as input to the
command, and here I'm using a chain of other functions to retrieve the contents
of the current buffer. Not sure, but there should be a better way to do it (if
someone knows, please let me know).
Alternatively, you can pass markdown your file name as input directly:
:let #+=system("markdown --html4tags " . shellescape(expand("%:p")))
But keep in mind that you'll need to write the file before calling this.
Two important notes:
I didn't type your full path to markdown. Use it.
I didn't use maps here, the final result would be something like:
nnoremap md :let #+=system(...)
get the xsel package
and pipe stdout to xsel --clipboard
For instance:
cat /etc/passwd | xsel --clipboard
Is that what you're looking for?
Filling in a missing piece (2+ years late). With the clarification that the user was on a Mac and since the asker's "why doesn't it work for me?" question was not answered.
To redirect the output of a command to the system clipboard from within MacVim (GUI version) you need to set the '*' to be the "clipboard register" you need to change the clipboard setting to 'unnamed':
set clipboard 'unnamed' # 'cb' can be substituted for 'clipboard'
Then sidyll's answer should work except specify the '*' register and not the '+' register:
:let #*=system(...)
The clipboard feature is likely not compiled into the "terminal version" of MacVim and when it is available option setting is different from 'unnamed'. To see more details regarding what works where and how, see the documentation in MacVim using the Vim help command:
:help 'clipboard' (include the single quotes since it's a set option!)
(I'll skip the command mapping issue since it always takes me several tries and I still have to look it up; finding the help for the mapping commands should be easier than finding it for the * register.)

Resources