Selecting text and saving it with keymap - vim

I've made c# command line program that takes a window handle and a string as parameters. It brings the window with the handle to the foreground, uses SendKeys to send the string that was passed, and brings the window that was previously at the foreground back to the foreground.
I made this program so that I could send selections of text from vim to any program I want, such as a python interactive.
I could not figure out how to send the selection directly to the program, but I did find out how to save selected text in visual mode(How can I save a text block in visual mode to a file in Vim? - It saves whole lines, but that is okay for my purposes. Ideally though, it would not save just lines). So I changed the styring paraeter of my program to a filename. My plan was that vim would save the selected text to a file, and call the program with the filename.
Here is part of my vimrc
let g:send_keys_handle = 0
let g:send_keys_file = $home . "\\vim\\send.txt"
function! SendSelection()
if g:send_keys_handle
silent !cls
let x = "silent !cmdHost " . g:send_keys_handle . " " . "\"" . g:send_keys_file . "\""
echom x
execute x
endif
endfunction
command! SendS call SendSelection()
noremap <F5> <esc>va(a(a(a(a(a(a(a(a(a(a(a(a(a(a(a(:w ~\vim\send.txt<cr>
The key mapping is not complete, but vim isn't even executing all of it now. When I press F5, it selects the topmost parentheses as expected, but it does not save the selection to a file.
The SendS command does work, so that is not the problem.

With all those repeated a( text objects, you presumably intend to capture the text in the outermost parentheses. Unfortunately, if there are less parens than text objects, Vim will beep and abort the mapping, so the remainder won't be executed.
A possible solution is to split this into two parts, and use :normal! for the selection. That will only abort the :normal part; the remainder of the mapping will still be executed. You need to re-establish the visual selection with gv, though.
nnoremap <F5> :execute 'normal! va(a(a(a(a(a(a(a(a(a(a(a(a(a(a(a('<CR>gv:w ~\vim\send.txt<CR>
An alternative to :write (which also emits messages, and sets the alternate file unless you prepend :keepalt) would be yanking of the selection, and use of the lower-level writefile() function to store the selection in a file.

Related

vimscript - edit another file from a custom command

I'd like to create, move around and append to other_file.py after calling a custom command from within this_file.py.
So for instance, I'd like to write several lines of text to a range of lines in the other file.
Is there some way I can basically enter :edit mode in other_file.py from this function? This would allow me to move around, search and append to other_file as though I were in it.
Here's a run through of where I'm at:
I am in this_file.py, and I have called:
:MyComm other_file:line_1/line_2/line_3
This activates the following vimscript:
function MyFunc(param_string)
let param_split = split(a:param_string,":")
let file_name = param_split[0] . ".py"
let lines = split(class_split[1],"/")
call system("touch " . file_name)
" Here is where I want to loop through the lines and use them in other_file.py
endfunction
:command -nargs=1 MyComm :call MyFunc(<f-args>)
alternative A
You can continue going through the external commands, as you've started with touch. So, you could use sed or awk to replace / append lines to the other file. With Vim's system() command, you can pass stdin input, too. For example, if you want to copy lines from the current buffer, grab them with getline() and pass them to system(), either as a List or a String.
alternative B
However, I would recommend avoiding external commands, and do the text editing tasks inside Vim. For that you just need to :execute 'split' other_file, and do the edits just as you would interactively, using Ex commands or :normal. Note that there are some special Ex commands (like :wincmd w replacing <C-w>w normal mode commands) that can make this more readable.

Is it possible to have Ctrl-K cut/uncut functionality in Vim like there is in Nano and Pico?

In case you're not familiar with how it works, in Pico and Nano you can hit ctrl-k multiple times and it will add each line to the clipboard. Then you can ctrl-u to "uncut" this. It's a very useful command. Vim does something similar with the dd command, but it only works one line at a time. Thus, you have to use visual mode to properly accomplish the above.
I couldn't find a good answer online so I rolled my own solution. You can add this to your vimrc file:
imap <C-k> <Esc>:execute #a ? 'normal! "Bdd' : 'normal! "bdd'<cr>:let #a=1<cr>:echo ''<cr>i
imap <C-u> <Esc>"bPi
autocmd CursorMovedI * execute(':let #a=0')
The register #a is used to track whether or not the cut line should be appended. The register #b is used as the clipboard register. Whenever the cursor position changes, you stop being in "append" mode. Thus, you can hit ctrl-k over and over to keep appending lines, but as soon as you move the cursor you go back to normal. I'm pretty sure this is how Nano and Pico implement it under the hood.
Is anyone aware of a cleaner solution?
Intro to Registers
The Vim commands you are looking for are delete/cut, dd and put/paste, p. Each of these commands by default use the unnamed register, "". So dd will delete a line and put the freshly deleted line into the unnamed register. p will put the contents from the unnamed register into your current buffer.
Vim has more than just the unnamed register, it also has named registers. These registers are a-z.
To see what your registers are set to you can execute :registers.
To use a named register prefix your command with quote and a lowercase letter, e.g. "add
Upper case letters will append instead of replace the contents of a register, e.g "Add"
The Vim Way
"add the first line, then append the next line via "Add. For repeated deletions use .
Use a text object. e.g. dap for delete a paragraph. See :h text-objects
Delete text via some motion. e.g. d} is delete till end of paragraph
Delete to a regex patter. e.g. d/foo<cr>
Lastly would be using visual mode, V6jd. Nothing wrong with using visual mode
Additionally you want to stay out of insert mode unless you are inserting text. You only want to be in insert move for short burst at a time. Most of the time you should be in Normal mode, hence the name.
For a nice post on the Vi/Vim way see this StackOverflow post: Your problem with Vim is that you don't grok vi.
Alternatives
If none of the standard Vim tricks satisfy your needs you may want to look into plugins that support line exchanging like Unimpaired or LineJuggler.
However if you really do want something similar this nano/pico features you can use the following by putting it in your ~/.vimrc file:
nnoremap Q :<c-u>call <SID>DeleteAppend()<cr>
function! s:DeleteAppend()
let save = #a
let #a = ##
let reg = get(g:, 'append_tick', -1) == b:changedtick ? 'A' : 'a'
execute 'normal! "' . reg . 'dd'
let g:append_tick = b:changedtick
let ## = #a
let #a = save
endfunction
The Q normal command will now delete a line and append additional deleted lines until a another command is executed. Example usage: QQjjQQQp
For more help please see
:h d
:h p
:h "
:h registers
:h :reg
:h motion
:h text-objects
:h .
:h visual-mode
Yes, there are many cleaner solutions:
12dd{motion}p " cut 12 lines, move elsewhere, paste
d5j{motion}p " cut from here to 5 lines down, move elsewhere, paste
d/foo<CR>{motion}p " cut from here to next 'foo', move elsewhere, paste
:.,23d<CR>:12put<CR> " cut from here to line 23, paste after line 12
:.,+5d<CR>:0put<CR> " cut from here to fifth line below, paste at top of buffer
or the truly amazing:
:.,11m35<CR> " move the lines between this one and 11 to after line 35
You can even do:
Vjjjjjjjjjjd{motion}p
One of the big advantages of Vim over other editors is its expressive language: don't think in "nano", think in "Vim".

How to automatically remove newline at end of yanked linewise visual selection?

Is there any way to yank a linewise visual selection without the newline at the end of the last line of the selection? More specifically, I'd like to be able to copy a line or lines from vim to the system clipboard and paste it elsewhere without the last command being executed without a chance to edit it.
I can get the desired effect by executing this command on the register in question after yanking:
:let #*=substitute(#*,'\n$','','g')
but is there any way to execute that command automatically? I am using MacVim at the moment and there doesn't appear to be a way to map an extra command to ⌘-C, so I'd have to have another mapping to remember to execute after copying if I can't find another solution.
My original answer to myself has been working well enough that I pretty much forgot about it, but now I've decided to set clipboard=unnamed to always yank to the system clipboard. A side effect is that yanking to the clipboard also affects the unnamed register, from which I don't want the trailing newline to be removed.
The new (and more Vim-ish) answer is a mapping to convert the linewise visual selection to characterwise before yanking it:
vnoremap <C-c> <Esc>'<0v'>g_y
Now I copy with <C-c> if I intend to paste in a command line or just y any other time. Pretty much what user Explosion Pills suggested but some added stuff to include the first character of the first line and last of the last line.
I came up with a solution that works for me, inspired by one of the answers here: Run command when vim enters visual mode
There is no real equivalent to a hypothetical VisualEnter or VisualLeave, but the CursorHold event along with changing updatetime will trigger after exiting visual mode. I also added the gv map to make it work when copying the previous selection.
" Remove last newline after copying visual selection to clipboard
function! RemoveClipboardNewline()
if &updatetime==1
let #*=substitute(#*,'\n$','','g')
set updatetime=4000
endif
endfunction
function! VisualEnter()
set updatetime=1
endfunction
vnoremap <expr> <SID>VisualEnter VisualEnter()
nnoremap <script> V V<SID>VisualEnter
nnoremap <script> gv gv<SID>VisualEnter
autocmd CursorHold * call RemoveClipboardNewline()
I don't like the idea of writing a swap file every 0 milliseconds, but I tested this in both Windows and OS X, and it works and causes no noticeable performance problems. It doesn't appear to write the swap file unless there are changes.
Also, I'm a little confused as to why this works at all, since it should trigger the function and set updatetime back to 4000 while still in visual mode, but apparently the value of updatetime doesn't change until after exiting visual mode, which works out nicely here.

Vim split unless open

When using split windows in VIM, sometimes I create new split of a file already opened in another split (typically with plugins that open a unit test for given file in a new split).
Is there a way how to remap split command so that it checks whether the file is already opened before splitting, and if it is, give a focus on it?
You can't remap the existing split command, as far as I know, but you can achieve the same same effect by writing a new function Split and then using a command-mode abbreviation (cabbrev).
Here's a function/mapping that should do what you want.
function! MySplit( fname )
let bufnum=bufnr(expand(a:fname))
let winnum=bufwinnr(bufnum)
if winnum != -1
" Jump to existing split
exe winnum . "wincmd w"
else
" Make new split as usual
exe "split " . a:fname
endif
endfunction
command! -nargs=1 Split :call MySplit("<args>")
cabbrev split Split
Note that this will only "check" for existing splits in the current tab, and hidden buffers are ignored. (However, it shouldn't be too difficult to add more cases to enhance this functionality.)
An alternative would be using :drop {file}.
Edit the first {file} in a window.
- If the file is already open in a window change to that
window.
- If the file is not open in a window edit the file in the
current window. If the current buffer can't be abandoned,
the window is split first.
Also using :sb to switch buffers might also be of use. See vim: move to buffer?
For more information:
:h :drop
:h :sb
:h 'swb'
That's one of the features of searchInRuntime.
:GSplit and :GVSplit can be renamed, they support filename completion, and they will ask which file to open when several match the pattern in &path.

Reading the result of an exe file in :vs in vim

Many of my programs are console like applications, which take a data file, and print out the results, either on screen (more often) or in another file.
Since these days I'm doing some analysis which requires a lot of little tempting with one input data file, just to get a few numbers, I usually go: edit data file, start program, get result, edit data file, start program, get result ...
So I was wondering, is there a way in vim, while having open the input file, to define a function which would open a vertical split and load the result of program (pro12.exe) in it ?
How would one go about that ?
The easiest thing to do is probably write a Makefile to run the program (and redirect output to a file), and :set the autoread option on in Vim. Then, you can create a macro r by typing qr:make<Enter>q and run the macro with #r. The macro will cause Vim to invoke make, which will run the program to update the data file. The autoread option will make sure that vim refreshes the updated file without prompting you first.
Assuming pro12.exe is in your %PATH%, this will invoke your helper app and vert-split-open a static output file name. Map to whatever key you like. QND: won't work when relative paths (bufnames) change via cd. YMMV
fun! RunPro12()
bufdo if bufname(bufnr($)) == '/path/to/pro12.exe.output' | close | endif
silent exe '!pro12.exe'
vs /path/to/pro12.exe.output
endfun
map <f3> :call RunPro12()<cr>
I don't like :bufdo solutions as this command always messes up the current windows organisation.
In normal time, I'd use lh#buffer#jump() that jumps to the window where the named buffer is opened if it is already opened, or opens the window otherwise.
function! s:Run0()
call lh#buffer#jump('/path/to/pro12.exe.output', 'vsp')
silent exe '!ls'
endfunction
Given the requested "to define a function which would open a vertical split", we could simply use:
function! s:Run1()
let bn = bufnr('/path/to/pro12.exe.output')
if bn >= 0
exe 'bw '.bn
endif
silent exe '!pro12'
vsp /path/to/pro12.exe.output
endfunction
nnoremap µ :call <sid>Run1()<cr>
Last thing. If as I suspect pro12 writes to stdout (the standard output, i.e. ~ the console), the last two lines of the function shall become:
vsp /path/to/pro12.exe.output
r!pro12
The "/path/to/pro12.exe.output" may be replaced with anything unique. Typically, I'd use a scratch buffer with a unique name having "pro12 result" in it.
PS: if in the function (this is important) you add a
nnoremap <buffer> q :bw<cr>
you'll be able to simply quit the output window simply by hitting q, from within the output window.

Resources