How do I use a map in combination with search matches? - search

How do I use a map on every match found after a search?
I have created various functions which I invoke using a map.
I would like to use the maps on every search matches found.
If I search for dates in my text, how would I apply a i/v/nmap on every search-match found?
something like this?
%s/search-pattern/=\normal mode map/g
%s/search-pattern/=\insert mode map/g
Is it possible also to combine maps?
Hope I made myself clear.

Vim is quite powerful, and I suspect insert mode/normal mode maps are not the most convenient approach here.
Some idioms that may get you started:
Edit: I've built on your earlier question (
How do I visual select a calculation backwards?
) and provided a demo, explained in
chat
1. Record a macro:
qqniMyText<Esc>q
This will insert 'MyText' at each match position. Now, repeat a hundred times: 100#q
(consider setting :se nowrapscan to avoid restarting from the top).
2. Use :global
:g/somepattern/norm! Aappended<Esc>
will append the text 'appended' to each line containing the search pattern
3. Use smart substitutions:
You can do some 'static' edit actions using replacement patterns:
:%s/\v(\d\d)-(\d\d)-(\d{4})/\3\2\1/g
To transform dd-mm-yyyy into yyyymmdd date stamps.
To do a dynamically evaluated substitution (using vimscript with \= in the replacement expression) you can do virtually anything (including, sending mail or printing a document, if you would really want to):
:%s/\v<DB_\w+>/\=substitute(submatch(0), '\v_?([^_])([^_]*)', '\U\1\L\2', 'g')/g
To transform 'database style' names like
var DB_USER_ID = f();
var DB_USER_FIRST_NAME = f();
var DB_USER_LAST_NAME = f();
var DB_USER_HOME_ADDRESS = f();
into 'camel case style names' like:
var DbUserId = f();
var DbUserFirstName = f();
var DbUserLastName = f();
var DbUserHomeAddress = f();
Live demo with expression evaluations
Edit In response to the comment/chat: You can use the approach #1 for this quite easily:
/\v\c\s*\zs(\s{-}(((sqrt|log|sin|cos|tan|exp)?\(.{-}\))|(-?[0-9,.]+(e-?[0-9]+)?)|([-+*/%^]+)))+(\s*\=?)?\s*
qqa<M-.><Esc>nq
Now you can repeat for all of the document:
:set nowrapscan
100#q

If there's only one match in every line, you could use :global instead of :s:
:%g/search-pattern/normal nrX
The :[range]normal positions the cursor at the beginning of the line, therefore the n to go to the first match before the mapping (I use rX as an example). You could write a custom command that would handle all matches in a line, but I would solve your use case with a recursive macro instead:
First, perform the search: /search-pattern, then record a macro containing your mapping, which jumps to the next match at the end: qarXnq. You can now manually apply the macro repeatedly via #a, or make it recursive via qA#aq, or :let #a .= '#a'. Execute this once #a, and it will run until it runs out of matches.

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

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

Vim: Adding automatic alphabetic and/or numeric index to current file name?

I want to implement a loose version of Niklas Luhmann's
Zettelkasten
in Vim. At the core of his method are note snippets that Continue the current
note or Brahch off from it, introducing a slightly different topic or
concept. In the note name, letters indicate branches and numerals
indicate continuations. Like so:
note100
note101
note101a # branches off from note100 (related topic)
note101b # also branches off from note100 (related topic)
note101b01 # continues note101b (same topic)
note101b02 # also continues note101b (same topic)
note101c
note102
To implement this in Vim, I need new file
names that are automatically enumerated either as a "continuation" or
a "branch" of the note in current buffer. As a non-coder making first "real" steps in Vimscript, this is where I'm at with the Branching Note function:
function! ZettelkastenNewBranchingNote()
let b:current_note_name = expand('%:t:r')
let b:new_branching_note = call(BranchingFunctionThatReturnsNewNoteName)
silent execute 'edit' b:new_branching_note
echomsg 'New branching note ' b:new_branching_note 'created.'
endfunction
The BranchingFunctionThatReturnsNewNoteName() should take
b:current_note_name and extend it with automatic alphabetical(!)
index (counting alphabetically upwards). How could I accomplish this?
Also, for my New Continued Note function: how could I numerically
count upwards from the last numeric part of the current file name? (E.g. 100a01 > 100a02.)
Thanks for any advice!
(Somewhat relatedly, here
the Nexus plugin is suggested, but I'd prefer to keep my script
self-contained.)
You provide a great deal of context (which is great), but are light on the needed algorithm. To me, it looks like this: If the current file ends with a letter, increase it, else (it's a number), append an a to start the alphabetical sequence.
Checks are done in Vim with regular expressions; \a is a short form for [A-Za-z] (you could also write [[:alpha:]]; yes it's that flexible), and $ anchors it to the end of the name:
if b:current_note_name =~ '\a$'
...
Extract the last character with matchstr().
let lastAlpha = matchstr(b:current_note_name, '\a$')
if lastAlpha ==? 'z'
" TODO: Handle overflow
endif
To "increase" an alphabetical character, convert it first to a number, increase, then back:
let newAlpha = nr2char(char2nr(lastAlpha) + 1)
To replace, you use substitute(), again with the same regexp.
let b:new_branching_note = substitute(b:current_note_name, '\a$', newAlpha, '')
Appending is simple:
else
let b:new_branching_note = b:current_note_name . 'a'
endif

how can i assign indented block in vim a special syntax highlighter?

for convenience in grouping couchdb functions
i created a file format that groups separate things together using yaml
it basically contains entries in the form of name.ext: |
followed by a intended block of code in the language fitting to .ext
for more pleasant editing i'd like to have vim use the correct syntax highlighters for them
edit
some code examples as requested
simple:
map.coffee: |
(doc) ->
for item in doc.items:
emit [doc.category, item], null
return
reduce: _count
more complex:
map.coffee: |
(doc) ->
emit doc.category, {items: 1, val: doc.value}
return
reduce.coffee: |
(keys, values, rereduce) ->
ret = {items: 0, val: 0}
for v in values
ret.items += doc.items
ret.val += doc.val
return ret
I believe that what you want it to make use of Vim's syntax regions (:help syn-region). But regions take delimiters as parameters.
You have a well defined start but not a defined end, maybe you could work your way around by establishing some conventions here like "2 empty new lines at the end".
There are similar answers that might give you a hint (including the docs) on how to implement a solution, like: Embedded syntax highligting in Vim
Also interesting and similar approach is this Vimtip: http://vim.wikia.com/wiki/Different_syntax_highlighting_within_regions_of_a_file
You have to write your own syntax file, and define a syntax region for each of your entries. Inside that region, you can then syntax-include the corresponding language as defined by your ext. Read all the details at :help :syn-include.
If that sounds too complicated, check out my SyntaxRange plugin. It is based on the Vimtip mentioned by alfredodeza. With it, you can quickly assign a syntax to a range of lines, e.g. :11,42SyntaxInclude perl

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