Change HTML attribute in neovim - vim

I found myself struggling with quickly changing attributes inside HTML tags.
For example, I have the following code
<div className="expense-date">
<div className="expense-date__###year">{year}</div>
<div>{month}</div>
<div>{day}</div>
</div>
My cursor is where the ### is, and I want to delete the whole attribute: className="expense-date__###year".
Is there any text object I can use to quickly delete it? Currently I use T ct> (or T ct if there's another attribute), but that's obviously not good enough and won't work if there's a space inside the attribute's value.

There is no built-in "attribute" text object but you can approximate one with something generic like this:
va"oB
where:
va" selects the value,
o moves the cursor to the other side of the selection,
B extends the selection to the beginning of the attribute.
This can be turned into quick and dirty pseudo-text objects:
xnoremap ix a"oB
onoremap ix :<C-u>normal vix<CR>
xnoremap ax a"oBh
onoremap ax :<C-u>normal vax<CR>
a visual mode mapping that covers the desired text,
an operator-pending mode mapping that uses the visual mode one,
to use like this:
yix
dax
vix
cax
…
See this plugin for a much smarter implementation.
Note that nothing in this answer is guaranteed to work in Neovim.

To my knowledge, there is no simple way to do this; unless you perform a deep syntax analysis, there will always be some caveats.
You can however improve your technique, for example with F"F=bF d2f".
F" goes to the beginning of the string;
F= goes to the previous = sign, skipping eventual spaces between;
b goes to attribute name (eventualy in the middle of it e.g. for data-my-attr), skipping eventual spaces;
F goes to the 1st space before the attribute name;
d2f" (or c2f") performs the job.
Of course, you can use it as a macro with q and # in order to simplify its use.

Here's another option:
vi> ........ visual select inner <>
o ............ jump to the other side
w ............ jump to the next word

Related

how to query the highest digit in a search?

Searching for the pattern /{{c\d, I'd like to get the highest digit found and use it in a macro.
For context, I'm using Anki (flashcard tool) and its cloze card type, and recently started creating my cards within vim.
Example card:
## Front
reading:
{{c1::A::reading A}}
B
{{c2::C::reading C}}
{{c1::D::reading D}}
E
## Back
...
In that example, given I'm positioned above E, I'd like to execute a macro that'll figure out that the highest cloze digit is 2 (in {{c2::C::reading C}}) and create a new cloze with highest digit incremented by one as {{c3::E::reading E}}
My macro currently looks like this:
:registers
"c ysiw}wysiw}wyiwic3::^OP::reading
ysiw} uses vim-surround to wrap the word in braces.
wysiw} repeats that operation
wyiw yanks the word
ic3:: adds c3:: in insert mode <= How do I calculate 3 from the highest cloze number in the file?
^OP paste the yanked word in insert mode
::reading adds the remaining text.
You can use a :%s command with the [/n] flag, which doesn't execute the substitution but only counts the matches.
Use that together with a \= expression on the replacement side, just for the side effects of the expression.
You can use that to append the cloze numbers to a list and then find the maximum after you've collected them all.
function! NextCloze()
let nums = [0]
let view = winsaveview()
silent %s/\m{{c\zs\d\+\ze::/\=add(nums, submatch(0))/egn
call winrestview(view)
return 1 + max(nums)
endfunction
The function is also saving and restoring the view around the %s operation, since that operation will move the cursor and we want to keep it in place.
To insert it in the middle of a mapping you can use something like:
i{{c<C-R>=NextCloze()<CR>::
Though there are probably other ways you can put that result in your buffer. For instance, if you'd like a mapping that takes the current word under the cursor and replaces it with the {{cN::...::reading ...}} block, consider these:
nnoremap <Leader>c ciW{{c<C-R>=NextCloze()<CR>::<C-R>"::reading <C-R>"}}<Esc>
xnoremap <Leader>c c{{c<C-R>=NextCloze()<CR>::<C-R>"::reading <C-R>"}}<Esc>
In Normal mode, it will act on the Word (sequence of non-white space symbols) under the cursor. In Visual mode, it will act on the visual selection. These are closer to your original #c macro.

How to convert block of text into bullet points in VIM?

In VIM, is there a way to select a block of text and convert it into bullet points or list?
Given this text:
abc
def
ghi
I want to be able to visually select them and convert them to list:
* abc
* def
* ghi
I have been doing this manually, using visual block mode <C-v> on the first column of the entire text, and then doing insert (i.e <C-v>jjjI* <esc>). However, it is very cumbersome, and I'd love to make this operation easier to execute.
Bonus: It would be great to toggle between bullet points and normal text. Also it would be great to be able to create numbered list instead of bullet list (but that may be another SO question).
The limitation here is really know where your list content begins and ends. Once you have it highlighted I'm not sure you can get much faster than I* <esc>.
If you have a long list and you know that there is an empty line immediately after the list with each element on its own line, you can move to the start of the list, then <C-V>)bI* <esc> which will be faster if your list is particularly long. ) moves you to the end of the current paragraph.
If your list ends at the end of the file, <C-v>GI* <esc> will work.
You can redo the previous set of commands with ., so moving to the start of the list and running I* <esc>, then j.j.j.j. will work, but I don't think that's much better than what you're already doing.
In general if you can find an faster way to move to the end of the list, I* <esc> is probably fast enough. If you wanted you could create an key mapping in your .vimrc to do this faster if it's something you need to do a lot.
nnoremap bip vip:s/^/* /<cr>
This mapping may make it faster.
In normal mode, you type bip the paragraph which you cursor locates is gonna be converted into bullet points.
bip -> "bullet in paragraph"
You could define the following maps:
nnoremap <m-n> vip:s/^/* /<cr>
nnoremap <m-u> vip:s/^/\=(line('.')-line("'<")+1).' '/<cr>
The first map (provided in earlier answer by Kent) will put * in front of every line of current paragraph. The second map will make a numbered list.

Pasting inside delimiters without using visual selection

In Vim, let's say I want to replace the contents of one String with the content of another.
Original input
var1 = "January"
var2 = "February"
Desired output
var1 = "January"
var2 = "January"
What I would usually do is:
Move cursor to line 1
y i " (yank inner quotes)
Move cursor to the destination quote in line 2
v i " p (visual select inner quotes, paste)
While this works well, I generally try to avoid visual mode when possible, so I am not completely satisfied with my step 4 (v i " p).
Is there any way to specify a "destination paste area" without using Visual mode? I suspect it might be something chained to g, but I can't think of anything.
There are many ways of doing this however using visual mode is the easiest.
Use the black hole register to delete the content then paste. e.g. "_di"P
Do ci"<c-r>0. <c-r> inserts the contents of a register
Simply paste and then move over a character and delete the old text. e.g pldt"
However visual mode still has my vote. I find that the concerns most people have is that using visual mode + paste is that the default register is swap with the selected text and it doesn't repeat well. Good news everybody! The 0 register always stores the last yank. The bad news is visual mode still doesn't repeat well. Take a look at this vimcast episode, Pasting from Visual mode, for more information. It mentions a few plugin that help with this.
I need this so often, I wrote a plugin to simplify and allow maximum speed: ReplaceWithRegister.
This plugin offers a two-in-one gr command that replaces text covered by a {motion} / text object, entire line(s) or the current selection with the contents of a register; the old text is deleted into the black-hole register, i.e. it's gone. It transparently handles many corner cases and allows for a quick repeat via the standard . command. Should you not like it, its page has links to alternatives.
It's not particularly pretty, but here goes:
Go to line one and yi"
Move to line two
Type "_di"hp
That deletes what's in the quotes, but sends the deleted text to a black hole register. Then it moves the cursor back one, and pastes what you yanked from line one.
All in all, you can start on the first line and type yi"j"_di"hp. Why is it that people find vim intimidating? ;)
Alternatively, yank the first line as normal, then drop to line two and type ci"<Ctrl+p> and select the previously yanked text from the menu.
Excerpt from my .vimrc:
" Delete to 'black hole' register
nnoremap <leader>d "_d
vnoremap <leader>d "_d
So, you just need to \di"P as your last step (assuming you use \ as a <leader>).
There is a plugin that addresses this problem precisely. It was presumably born out of the same feeling you're feeling, namely that Visual mode often feels less than ideal to advanced Vim users.
operator-replace – Operator to replace text with register content
With operator-replace installed the replacing goes like this.
Yank the word inside the quotes.
yi"
Move to the target line
j
Replace inside the quotes with the yanked text. (I've set up gr as the replace operator: :map gr <Plug>(operator-replace). Pick your own mapping.)
gri"
Check it out! This is part of the truly excellent textobj/operator frameworks. I couldn't work without them.

Can vim replace a text object with the default register?

Example: I have some text, like this php code
if(empty($condition)) {
// work here...
}
And my default register contains the string "$NewCondition".
I want to place the cursor inside the first set of parens (...) and perform a command that will result in this:
if($NewCondition) {
// work here...
}
So my question: Is there some way to replace the text inside the parens using the default register?
Most suggestions I've found, such as this one, fail when there's nested parens.
I also know I can name registers, such as "ayi( to yank all text in parens into register a, but I don't want to have to type two extra characters every time I'm yanking. That's why I'm asking about the default register.
In an ideal solution the default register would still contain "$NewCondition" when the replace is complete.
vi)p
Unfortunately this will also place the old text in the default register.
vi("_dP
vi( to select empty($condition)
"_d to delete it into the blackhole register
P to put the content of the default register before the cursor which is now on the last )
I use this a lot so I have this mapping to make the whole thing shorter:
vnoremap <Leader>p "_dP
so (with , as the mapleader) I do vi(,p.
As a side note, if your cursor is in the second set of parenthesis you can do vi(i( to go one level up and so on.
Try this: "_ci)<c-r>"<esc>
Some detailed explanations here: "_ means to select the "black-hole" register for the following command. So the text that gets deleted in your next operation will simply be discarded and will not be put into any registers. ci) means "change inner parentheses", it will delete all the text within current level of parenthesis pair, then bring you into insert mode. <c-r> in insert mode means to insert the text in a specified register into the current cursor position, and " selects the "default register". At last, we use <esc> to bring us back to normal mode.
Indeed a lot of key strokes! If you use such operation very often, you can consider creating a keymap for it. See :help map for more details.

Vim Surround: Create new tag but don't indent/new line

I would like to mimic Textmates CTRL+ALT+w, which creates a new pair of opening and closing HTML tags on the same line.
In VIM Surround I'm using CTRL+st in Edit mode for this, but it always indents and creates a new line after setting the tag, so that it looks like this (* = cursor position):
<p>
*
</p>
Is there a way to achieve this? :
<p>*</p>
I guess your problem is that the selected area is "line wise". For example, if you select a few lives with V and surround it with tags, the tags will be placed one line above and one bellow the selected lines.
You probably want to create a "character wise" selection, with v before surrounding it.
Anyway, please post the map you created, so we can help debugging this.
Update
After some clarification in the comments, I would tell you that the surround plugin is not the best option. As its name describes, it was created to deal with surrounded content. So you may need content to surround.
In your case, I recommend taking a look in HTML AutoCloseTag. This plugin closes the html tag once you type the >. It is certainly more appropriated, and uses less keystrokes than surround.
<p <--- Now when you type ">", if becomes:
<p>|</p> <--- Where "|" is the cursor.
Obviously, you will get this behavior to every tag. But that may be handy if you like it.
From normal mode, type vstp> to enter visual mode and output an opening and closing <p> tag on the same line at the current cursor position. Use a capital S to maintain the current indent level.
This doesn't place the cursor in between the tags as you describe, but neither does Textmate's CtrlW shortcut (I think you meant CTRL+Shift+w, not CTRL+ALT+w, as the latter just outputs a diamond sign.)
My answer is probably coming to late, but I'll try to help.
I had similar problem with Vimsurround plugin. Every time I select sentence (one line) using ctrl+V and try to surround it with something I get this:
{
var myVar
}
instead of this:
{ var myVar } // what I wanted
I found easy solution: From a normal mode I choose a line with vis command and then I type capital C (my vim surround mapping ) and choose brackets to surround.Then I get one line nicely surrounded.
The question title is technically mislabeled based on what the author was actually looking for, but since I was actually looking for the answer to the question asked in the title, I figure I should provide an answer to it as well.
To create a new tag surrounding an element without the automatic indentation Vim Surround uses when using a block wise selection (ie: VysS), you can instead do something like:
^ys$
This command will move your cursor to the first non-blank character of the line, issue the command that you want to utilize You Surround, and move to the end of the line. Then, simply start entering your tag.
The result is this:
<input type="email" name="email">
Could become something like this:
<li><input type="email" name="email"></li>
The command is repeatable as well with . and all the normal other Vim goodness.
Stumbled upon this question because I was wondering this as well - I believe the simplest way to do this is just:
yss<p>
(yss surrounds a line with something without indenting - see here: http://www.catonmat.net/blog/vim-plugins-surround-vim/)
You can accomplish this by selecting the relevant text object: :h text-objects
...and surrounding that instead of surrounding a Visual Line selection.
The most common example I found myself running into was when trying to surround one tag with another. In that situation, the it and at text objects are quite useful:
*v_at* *at*
at "a tag block", select [count] tag blocks, from the
[count]'th unmatched "<aaa>" backwards to the matching
"</aaa>", including the "<aaa>" and "</aaa>".
See |tag-blocks| about the details.
When used in Visual mode it is made characterwise.
*v_it* *it*
it "inner tag block", select [count] tag blocks, from the
[count]'th unmatched "<aaa>" backwards to the matching
"</aaa>", excluding the "<aaa>" and "</aaa>".
See |tag-blocks| about the details.
When used in Visual mode it is made characterwise.
For example, if you had your cursor in a paragraph and you wanted to surround it with a div on the same line, ysat<div> would accomplish that.

Resources