vim macro until pattern is matched - vim

I have an array of states that no one is going to have to modify often, so I want to remove the white space.
I tried the following keystrokes as there are some whitespace characters between the commas in the array and the endlines:
q a /, ENTER FORWARD v /\n ENTER d
Unfortunately, whomever formatted this neglected to place any whitespace after 'Montana', so when I run the macro 51#a, it breaks after 27 iterations.
How can I have a macro only run a pattern if \s is matched, or better yet, how can I run a macro until it recognizes ); (end of array).
EDIT: Here is an example. Note the two white space characters after all entries except keys MT,NE,NV and NH.
$state_list = array('AL'=>"Alabama",
'AK'=>"Alaska",
'AZ'=>"Arizona",
'AR'=>"Arkansas",
'CA'=>"California",
'CO'=>"Colorado",
'CT'=>"Connecticut",
'DE'=>"Delaware",
'DC'=>"District Of Columbia",
'FL'=>"Florida",
'GA'=>"Georgia",
'HI'=>"Hawaii",
'ID'=>"Idaho",
'IL'=>"Illinois",
'IN'=>"Indiana",
'IA'=>"Iowa",
'KS'=>"Kansas",
'KY'=>"Kentucky",
'LA'=>"Louisiana",
'ME'=>"Maine",
'MD'=>"Maryland",
'MA'=>"Massachusetts",
'MI'=>"Michigan",
'MN'=>"Minnesota",
'MS'=>"Mississippi",
'MO'=>"Missouri",
'MT'=>"Montana",
'NE'=>"Nebraska",
'NV'=>"Nevada",
'NH'=>"New Hampshire",
'NJ'=>"New Jersey",
'NM'=>"New Mexico",
'NY'=>"New York",
'NC'=>"North Carolina",
'ND'=>"North Dakota",
'OH'=>"Ohio",
'OK'=>"Oklahoma",
'OR'=>"Oregon",
'PA'=>"Pennsylvania",
'RI'=>"Rhode Island",
'SC'=>"South Carolina",
'SD'=>"South Dakota",
'TN'=>"Tennessee",
'TX'=>"Texas",
'UT'=>"Utah",
'VT'=>"Vermont",
'VA'=>"Virginia",
'WA'=>"Washington",
'WV'=>"West Virginia",
'WI'=>"Wisconsin",
'WY'=>"Wyoming"
);
To:
$state_list=array('AL'=>"Alabama",'AK'=>"Alaska",'AZ'=>"Arizona",'AR'=>"Arkansas",'CA'=>"California",'CO'=>"Colorado",'CT'=>"Connecticut",'DE'=>"Delaware",'DC'=>"District Of Columbia",'FL'=>"Florida",'GA'=>"Georgia",'HI'=>"Hawaii",'ID'=>"Idaho",'IL'=>"Illinois",'IN'=>"Indiana",'IA'=>"Iowa",'KS'=>"Kansas",'KY'=>"Kentucky",'LA'=>"Louisiana",'ME'=>"Maine",'MD'=>"Maryland",'MA'=>"Massachusetts",'MI'=>"Michigan",'MN'=>"Minnesota",'MS'=>"Mississippi",'MO'=>"Missouri",'MT'=>"Montana",'NE'=>"Nebraska",'NV'=>"Nevada",'NH'=>"New Hampshire",'NJ'=>"New Jersey",'NM'=>"New Mexico",'NY'=>"New York",'NC'=>"North Carolina",'ND'=>"North Dakota",'OH'=>"Ohio",'OK'=>"Oklahoma",'OR'=>"Oregon",'PA'=>"Pennsylvania",'RI'=>"Rhode Island",'SC'=>"South Carolina",'SD'=>"South Dakota",'TN'=>"Tennessee",'TX'=>"Texas",'UT'=>"Utah",'VT'=>"Vermont",'VA'=>"Virginia",'WA'=>"Washington",'WV'=>"West Virginia",'WI'=>"Wisconsin",'WY'=>"Wyoming");
EDIT:
Just googled vim macro until pattern matched, and came across my own question. I have a better example now:
namespace A{
class a{}
class a{}
}
namespace B{
class b{}
class b{}
class b{}
}
Needs to become:
namespace A{
class Aa{}
class Aa{}
}
namespace B{
class Bb{}
class Bb{}
class Bb{}
}
This cannot be solved with the previously accepted answer.

Honestly the easiest answer would be to join the lines.
If you visual select the entire region and just press J (or feed the range to :join) all of the lines will end up on one line. (There may be excess whitespace in-between elements but thats easier to fix then trying to write the macro).
If you then want to remove the excess whitespace you could run
:s/,\s\+/,/g
on the joined line.
Take a look at :h J and :h :join

First of all, you can easily remove all trailing whitespace with one command:
:%s/\s\+$//e
That will work on every line of the file. If you want to do it only on this fragment of code, you can specify different range (instead of %, which is whole file). For example, you can pass line numbers (1 to 51):
:1,51s/\s\+$//e
or first visually select, and then run command removing whitespace (you have to be on the first line starting this sequence):
V/)<CR>:s/\s\+$//e
If you really want to use macro, you can slightly tweak (and simplify) your macro:
qa0f,lDq
and then run it on every line at once by first of all visually selecting all of them (again, start on line you want, I won't put gg here because it might be just part of the file):
V/)<CR>:normal #q
This will play the macro over every selected line, up to the line with ).
I hope it helps :)

Related

Move Cursor Immediately Before a Character on a Line in Vim

Say I have the following:
text function(contents) text
and I wanted it to be
text function() text
Placing the cursor right after the opening parenthesis, I thought the following command would work df); however, what I ended up with was the following
text function( text
So I would need someway to specify that I want the character just before the closing parenthesis, but I'm not sure how to do that. There may also be a better way to do this.
What would be the best way to go about this?
You were close! You need dt) as in delete till )
The f motion places the cursor on the ) (remember it like find)
As for the 'best' way to do it, there is at least a more general way: if the
cursor were somewhere in the middle of the ( and ) (or on one of them), you
can do di) (or equivalently di() to delete inside )
If you do da) (or equivalently da() to delete around ), you would
delete the stuff in between and including the brackets.
The same goes for di[, di{, di<, di', di" etc. Using these so-called
text objects, as opposed to the d{motion} way, has the advantage that you can
repeat the edit on other pairs of brackets/quotes without the cursor needing to
be in precisely the same place - it just needs to be on or in between them.
In the following you could position the cursor on e.g. the 'i' of 'initial' in
the first line, do di) to delete the words 'some initial text', then move the
cursor to the 'e' in 'more' in the second line and just do . to also delete
the words 'some more text'):
(some initial text)
(some more text)
This way also works when the brackets (or quotes) are on different lines. For
example, with the cursor somewhere between the {}, doing di} will change
this:
function( args ) {
body of function
}
to this:
function( args ) {
}

Vim delete parent parenthesis and reindent child

I'm trying to go from here:
const f = function() {
if (exists) { // delete this
const a = 'apple'
}
}
to:
const f = function() {
const a = 'apple'
}
What's the fastest way to delete and reindent everything in between?
Assuming that cursor is inside the braces; any number of lines and nested operators; "else"-branch is not supported:
[{"_dd<]}']"_dd
Explanation:
[{ go to previous unmatched brace
"_dd delete the "{"-line (now the cursor is in the first line of the block)
<]} decrease identation until the next unmatched "}"
'] go to the last changed line (i.e. "}"-line)
"_dd and delete it
If the cursor is initially set on the "{"-line and you don't care for 1-9 registers, the command can be simplified to dd<]}']dd
Assuming your cursor is somewhere on the line containing const a
?{^M%dd^Odd== (where ^M is you hitting the Enter key and ^O is you hitting Ctrl+O).
Broken down this is:
?{^M - search backwards/upwards for the opening brace
% - jump to the corresponding brace (closing brace)
dd - delete the current line
^O - jump to previous location (the opening brace)
dd - delete the line
== - indent current line
You don't need a special macro or function or anything to do this since vim gives you all the powerful text manipulation tools to do the task. If you find yourself doing this an awful lot then you could always map it to a key combination if you want.
The above only works for single lines inside curly braces, but the one below will work for multiple lines (again assuming you are on some line inside the curly braces)
<i{0f{%dd^Odd I'll leave you to figure out how this one works. Type the command slowly and see what happens.
Great answers all around, and, as pointed out, you can always map these keys to a shortcut. If you'd like to try a slightly more generic solution, you could check my "deleft" plugin: https://github.com/AndrewRadev/deleft.vim

insert n characters before pattern

I have a text file where I want to insert 20 spaces before the string 'LABEL'. I'd like to do this in vim.
I was hoping something like s/LABEL/ {20}LABEL/ would work. It doesn't.
This SO question is close to what I want to do, but I can't put 'LABEL' after the '=repeat()'. Vim regex replace with n characters
%s/LABEL/\=repeat(' ',20)/g works.
%s/LABEL/\=repeat(' ',20)LABEL/g gives me E15: Invalid expression: repeat(' ',20)LABEL
How do I get vim to evaluate =repeat() but not =repeat()LABEL?
After \=, a string is expect. And LABEL isn't a valid string
%s/LABEL/\=repeat(' ',20).'LABEL'/g
BTW thanks to \ze, you don't need to repeat what is searched.
%s/\zeLABEL/\=repeat(' ',20)/g
Note that if you need to align various stuff, you could use printf() instead
%s#label1\|other label#\=printf('%20s', submatch(0))#

GVim/Vim. How to delete all white space up to a certain word

I'm a .Net dev but recently started dabbling with Vim (or in my instance GVim) for when I need to do repetitive text editor type tasks.
My experience is basically non-existent. So please bear with me. Also I realize there are GUI tools or things I can make use of inside Visual Studio, but I'm trying out the Vim route as I'd like to master a new util/app every now and then.
Say I've got a text file which contains a lot of properties (could be any text though) like so:
public string AccountNumber { get; set; }
public string CustomerName { get; set; }
public string ExpiryDate { get; set; }
public string IdentityNumber { get; set; }
public string OfferDate { get; set; }
I'd like to make use of the string replace method to delete everything up to, and after the property name.
e.g. end with:
AccountNumber,
CustomerName, ... etc.
So far I've had success with
1) Alt + left click + drag select all the preceding white space & delete
2) :% s/public\ string\ //
3) :% s/\ {\ get;\ set;\ }/,/
It's purely out of curiosity I'd like to find out if its possible to update my 2nd step to include the removal of the white space.
I realize the ^ character means beginning of the line and that (I think) \s means white space, but that's about where my knowledge ends.
I'm thinking something like?
:% s/^\s+string//
one way to solve this is to record a macro
qq # start recording macro into register q
0w # move to first non-whitespace caracter. Omit this if no WS at start of line
d2w # delete 2 words
w # move a word forward
D # delete to en of line
q # quit macro recording
standing at the beginning of a line, do #q
For subsequent lines, repeat the macro using .
or, try the following substitution
:%s/^\s*public string\s*\([a-zA-Z]*\).*$/\1/
I came up with this,
%s/^\v\s*(\s*\w+){2}\s?(\w+).*/\2/g
precisely speaking, this line doesn't know which word is your "property". it just leave the 3rd word in the line there, remove anything else.
Using :normal would be an alternative to :s or a macro in this case:
:%norm 03dwelD
You may want to use a different range other than the whole file, %. I would suggest visually selecting the lines with V then execute :norm 03dwelD. After you type : your prompt will look like :'<,'>. This is correct.
For more help see:
:h :norm

Add a number of '=' in a rest (reStructuredText) document that equals to characters from last line?

I want to use a shortcut to add needed = (from Section/Title reStructuredText syntax) according to the last line.
So, suppose (being | the cursor position)
Title
|
and pressing an specific mapping mapped to a function, add a number of = that equals to the last line (where Title is), becoming:
Title
=====|
This sequence will get you close:
kyyp:.s/./=/g
Duplicate the previous line, then in that line, change every character to an equals sign. Map that to a key sequence you like, and try it out.
Another way:
:execute "normal " . strlen(getline(line(".") - 1)) . "i="
strlen(getline(line(".") - 1)) returns the lenght of the line above the current position. The result is that the command Ni= is executed, inserting = N times.
For a mapping I would have used:
put=repeat('=', col('$')-1)
For something more interactive, I would have use the same solution as Ned's.
(I don't like my mappings to change the various registers like #" or #/)
My vim-rst-sections vim plugin will convert lines to section headings:
http://www.vim.org/scripts/script.php?script_id=4486
In your case, you'd put the cursor on the line, and type <leader><leader>d to get a top-level heading like this:
#####
Title
#####
A few repeats of <leader><leader>d will take you down to the standard hierarchy of Python ReST sections to the =.

Resources