Open a file under cursor with a line break in vim - vim

I want to set up a key mapping to open an absolute path filename highlighted in visual mode that may have a line break in the middle.
Most files I have are formatted in the following way, and I would like to highlight the path between the single quotes and open that file (formatted for MSC NASTRAN for those curious):
INCLUDE '/directory/directory/directory/directory/
directory/filename'
What's difficult is yanking that whole section results in a ^M newline character in the middle of the path. My approach was the following (walking through the :map command below):
Yank the path from selection (stored to #0)
Create new register #f where empty string was substituted for the newline char from #0
Open file path in #f
map <F8> y \| :let #f=substitute(strtrans(#0),'\^#','','g') \| :e <C-R>f<CR>
The problem is that if it's run a second time on a new file path, vim uses the previous path stored in the #f register before updating #f
Any ideas to fix it or another approach? Clearing the register with :let #f='' at the end didn't seem to work either.
I also don't have admin rights, since this is a work computer, so I don't think I can install plugins, but I'm still new to vim, any thoughts appreciated. Thanks!

An auxiliary register is unnecessary; use :exec in order to evaluate an expression and use the result as an argument to a command. I'm not sure whether strtrans() is somehow advantageous, but replacing \n directly seems to work. Either way, make sure to use fnameescape(). Also, use noremap in order to avoid recursive mappings unless you need them. If this particular mapping is intended only for visual mode, xnoremap is even better.
xnoremap <F8> y \| :exec "e" fnameescape(substitute(getreg('"'),'\n','','g'))<CR>

Related

Vim delete lines using regex and adding them to registry

I have these mappings:
vnoremap <leader>dab "hy:v/<c-r>h/d<cr>
vnoremap <leader>daa "hy:g/<c-r>h/d<cr>
Which removes multiple lines by regex search, however, the lines are just deleted, and I'd like them to go on a registry so I can pase them following running these commands.
Can this be achieved by this regex search and delete?
EDIT:
I can see that they are added to different registers, Can I make the ex-command be saved to a single register?
Explaining my solution vnoremap <leader>daa "hyqeq:g/<c-r>h/d E<cr>:
"hy stores the visual selection into #h
qeq is equivalent to removing the content of #e
d E after g// is the command :d but it uses the uppercase register #E, which means it will append new content into it, instead of overwriting
However, this has some drawbacks:
Using <c-r>h into the mapping can break the command, e.g. if the selection contains slash characters; you can avoid a few unpleasant cases by adding \V in the pattern, but it's far from perfect: vnoremap <leader>daa "hyqeq:g/\V<c-r>h/d E<cr>
An extra empty line is prepended into the #e register

How do I use Vim's escape() with <C-R> as its first argument?

My goal is to select several words in Vim's visual mode (well, neovim in my case), press leader+L and let fzf show search results for the selected string through :Rg. I came up with this:
vnoremap <expr> <leader>l 'y:<C-U>Rg '. shellescape(escape('<C-R>"', '()[]><')) .'<CR>'
Which does work, but when I select the text options(:modifier) and trigger a search, the escape() command doesn't escape the parentheses and Rg fails to return results.
In short, I'm expecting this command to fire:
:Rg 'options\(:modifier\)'
And I'm getting this instead:
:Rg 'options(:modifier)'
I'm guessing I can't use <C-R> in this context, but I can't seem to figure out why?
UPDATE: Thanks to a helpful reply from user D. Ben Knoble indicating I could drop and construct the mapping differently, I ended up with this, solving my problem:
vnoremap <leader>l "ky:exec 'Rg '. shellescape(escape(#k, '()[]{}?.'))<CR>
You don’t need to—all registers are available as variable’s prefixed with # (all are readable except #_, most are writable, I think).
So instead of <C-R>", use #"

Show result instead of X with incomplete mapping in vim

In my file at ~/.vim/ftplugin/tex/insert.vim I have the following mappings
; <Nul> is ctrl-space
imap <Nul> \,
imap <Nul><Nul> ~
imap <Nul><Nul><Nul> \enspace
imap <Nul><Nul><Nul><Nul> \quad
imap <Nul><Nul><Nul><Nul><Nul> \qquad
This allows me to insert spaces of different width by pressing ctrl-space, with each press making the whitespace longer. When I type the first ctrl-space, I get an X and it waits for me to see if I wanted to input more ctrl-spaces or if I'm done.
I would like to know if there's a way to show what the space will be, either input it directly to the buffer and change it if it's pressed again, or put it in the status bar until I'm done.
A way I think this could be possible was to check if the chars before the cursor are \, and if they are replace it with the next space, and if it's not, check the rest of the spaces I can input and finally input a \, if none of the spaces are there. I just don't know how to accomplish such a keymap.
Your current map solution is based on the mapping timeout. Until that happens, Vim is waiting for more input, and nothing has been inserted yet. In order to later revise what got inserted, you need to completely replace the approach.
This could be implemented with :map <expr> <Nul>, checking what's before the cursor, and then returning <BS> characters to wipe that followed by the replacing text.
With the mjbrownie/swapit plugin, you'll get that "toggling through alternatives" functionality (using <C-a> / <C-x>) for free: Just put the following into ~/.vim/ftplugin/tex/swapit.vim:
SwapList spaces \, ~ \enspace \quad \qqad
You can keep your original <Nul> mappings to quickly insert when you know what you want (or for the initial insert), and use swapit only to revise later on.

How to edit an existing key mapping in vim?

How do I edit an existing mapping in vim? I set the mapping in my .vim file using one of the standard mapping commands:
map ^A o]]></code>^M<GRD minus="" summary="">^M^M<p></p>^M^M</GRD>^M^M<code><![CDATA[0kkkkkk5ehi
I want to edit this long command to use "matlab" instead of code, e.g.:
map ^A o]]></matlab>^M<GRD minus="" summary="">^M^M<p></p>^M^M</GRD>^M^M<matlab><![CDATA[0kkkkkk5ehi
However, I don't want to edit the .vim file -- I will use the original mapping again. I just want to change the mapping for my current session. I tried :map ^A, but this only displays the current mapping, and I cannot copy the displayed text.
P.S. Note that the ^M and ^A characters are inserted with Ctrl-Q Ctrl-M, etc.
Tweaking a mapping for a current session only is unusual; probably that's why it's supported poorly. I would guess that you're not actually concerned about a session, but rather a Matlab filetype. For that, there's excellent support. If the 'filetype' is detected as matlab, you can put a buffer-local mapping variant in ~/.vim/after/ftplugin/matlab.vim:
map <buffer> ^A ...
Direct editing
If you'd rather stick with your original plan, I would do it like that:
Open configuration: :split ~/.vimrc
Edit the mapping in-place
Yank the line: yy
Execute to activate for the current session: :#"
Abort the edit without saving: :bdelete!
Alternative
For a more general solution, you could also capture the output of the original :map ^A command via :redir, then :put that into a :newscratch buffer, and finally :source that. It's more manual steps and typing, but could be automated by a custom command. Worthwhile if you need this often.
To set your map for the current session only, do
:map ^A o]]></matlab>^M<GRD minus="" summary="">^M^M<p></p>^M^M</GRD>^M^M<matlab><![CDATA[0kkkkkk5ehi

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".

Resources