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

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

Related

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))#

vim: set fix jump marker in a source comment of the current file

I'm a heavy VIM user but I missing one thing…
setting a fix jump marker in a comment on different parts of a (large) current open file
What I expect:
setting a marker in a comment like: // vim: marker(x)
jumping to the marker with: 'x
example (using tcl code) using 'x to jump to function p_structDEF_MqC
proc p_func_arg_name_DEF_MqC {key value} {
set cmd "N"
regexp {^(\w)#(.*)} $value dummy cmd value
set ::ARG_DEFAULT(k,$key) $cmd
set ::ARG_DEFAULT(v,$key) $value
}
proc p_arg_name_DEF_MqC {key value} {
func_arg_name_DEF_${::LANG}_MqC $key $value
}
## vim: marker(x)
proc p_structDEF_MqC {name} {
global xCLASS xPREFIX
set prefix [string range $name 0 end-1]
if {$prefix ne "Mq"} {
lappend xPREFIX "$prefix"
}
lappend xCLASS "${prefix}C"
}
proc p_enumDEF_MqC {name argv} {
}
proc p_typeDEF_MqC {VAR VALUE} {
}
proc p_fupuDEF_MqC {name ret argv} {
}
You can create an autocommand that scans files for those marks,
and create them using setpos(). You can go as complex as
you want in your scripting, i.e. to analyze the next line
and determine where the mark should be. Here is a simple
implementation in a single command that creates marks on the
first character of the next line:
au BufRead * g/vim: marker([a-z])/call setpos(
\ "'".matchstr(getline('.'), '(\zs\w'),
\ [0, getpos('.')[1]+1, 1, 0] )
This answer is based mainly on :autocmd and :g. You should
check the help files for both (:h :au and :h :g). An
autocommand runs the specified command when a certain event
happens for files matching a specific pattern. So, more help
topics for you to read:
autocmd-events
autocmd-patterns
You can use the pattern to restrict this to certain files.
Then, we have the :g command which searches for lines matching
a pattern and executes a command. The search is where you modify
which flags are you looking for. Based on your comment, let's say
for example you want to make the space after : optional. Then
update your regex to:
vim: *marker([a-z])
Modify this as you need.
The command is centered in the setpos() (again search the help
for it... everything I'm saying is in the help anyway). It takes
two arguments, one is what to set and the other is what to set
to. We want to set a mark, so we need to give it an expression
like "'a" to set mark a. To figure out which letter is the
mark supposed to be applied to, we use:
matchstr(getline('.', '(\zs\w')
Here we search for the first letter after the first parenthesis
(with (\zs\w) on the text of our current line, retrieved with
getline(). Search for the help of all these functions. This is
concatenated to a quote, with "'" . {expr} to make a "'x" if
the letter was x.
Then for the second argument, if must be an array similar to what
getpos() returns. Search the help again. However we are
modifying it, to set the mark on the beginning of the next
line. Thus we only use the line item returned by getpos(),
which is the second item, and add 1 to it.
[{buffer}, {line}, {col}, {virtual-offset}]
[0, getpos('.')[1]+1, 1, 0]
the working solution based on previous answer:
au BufRead *.c silent! g/vim: \*Marker(\[a-z])/call setpos(
\ "'".matchstr(getline('.'), '(\zs\w'),
\ [0, getpos('.')[1]+1, 1, 0] )
→ thanks to #sidyll for the help.

Select first word from each line in multiple lines with Vim

I would like to copy the first words of multiple lines.
Example of code :
apiKey := fmt.Sprintf("&apiKey=%s", args.ApiKey)
maxCount := fmt.Sprintf("&maxCount=%d", args.MaxCount)
id := fmt.Sprintf("&id=%s", args.Id)
userid := fmt.Sprintf("&userid=%s", args.Userid)
requestFields := fmt.Sprintf("&requestFields=%s", args.RequestFields)
I would like to have this in my clipboard :
apiKey
maxCount
id
userid
requestFields
I tried with ctrl-v and after e, but it copies like on the image :
You could append every first word to an empty register (let's say q) using
:'<,'>norm! "Qyiw
That is, in every line of the visual selection, execute the "Qyiw sequence of normal commands to append (the first) "inner word" to the q register.
You need to have > in cpoptions for the newline to be added in between yanks (:set cpoptions+=>), otherwise the words will be concatenated on a single line.
If you want to quickly empty a register you can use qqq in normal mode (or qaq to empty register a).
Note: the unnamed register ("") will also contain what you want at the end of the operation, so you don't need to "qp to paste it, p will do.
I think the chosen answer is a really good one, the idea of appending matches to registers can be pretty useful in other scenarios as well.
That said, an alternative way to get this done might be to align the right-hand side first, do the copying and then undo the alignment. You can use a tool like tabular, Align or easy-align.
With tabular, marking the area and executing :Tab/: would result in this:
apiKey : = fmt.Sprintf("&apiKey=%s", args.ApiKey)
maxCount : = fmt.Sprintf("&maxCount=%d", args.MaxCount)
id : = fmt.Sprintf("&id=%s", args.Id)
userid : = fmt.Sprintf("&userid=%s", args.Userid)
requestFields : = fmt.Sprintf("&requestFields=%s", args.RequestFields)
You can now use visual block mode to select the first part, and then use u to undo the alignment.
Relying on the external cut program:
:'<,'>!cut -d' ' -f1

Substituting everything from = to end of the line in VIM

Let's say I have several lines like:
$repeat_on = $_REQUEST['repeat_on'];
$opt_days = $_REQUEST['opt_day'];
$opt_days = explode(",", $opt_days);
... and so on.
Let's say I use visual mode to select all the lines: how can I replace everything from = to the end of the line so it looks like:
$repeat_on = NULL;
$opt_days = NULL;
$opt_days = NULL;
With the block selected, use this substitute:
s/=.*$/= NULL;
The substitution regex changes each line by replacing anything between = and the end of the line, including the =, with = NULL;.
The first part of the command is the regex matching what is to be replaced: =.*$.
The = is taken literally.
The dot . means any character.
So .* means: 0 or more of any character.
This is terminated by $ for end of line, but this actually isn't necessary here: try it also without the $.
So the regex will match the region after the first = in each line, and replace that region with the replacement, which is = NULL;. We need to include the = in the replacement to add it back, since it's part of the match to be replaced.
When you have a block selected, and you hit : to enter a command, the command line will be automatically prefixed with a range for the visual selection that looks like this:
:'<,'>
Continue typing the command above, and your command-line will be:
:'<,'>s/=.*$/= NULL;
Which will apply the replacement to the selected visual block.
If you'll need to have multiple replacements on a single line, you'll need to add the g flag:
:'<,'>s/=.*$/= NULL;/g
Some alternatives:
Visual Block (fast)
On the first line/character do... Wl<C-v>jjCNULL;<Esc>bi<Space><Esc>
Macro (faster)
On the first line/character do... qqWllCNULL;<esc>+q2#q
:norm (fastest)
On the first line do... 3:no<S-tab> WllCNULL;<Enter>
Or if you've visually selected the lines leave the 3 off the beginning.

Resources