global submatch()? - vim

I wanted to find a line, then match several words within that line BUT instead of substituting them I would prefer to simply save them (by appending to a register, or exporting to a file).
Is back-referencing (i.e. submatch(1) or \1) doable in this regards, or is that only through the substitution? I realize I could substitute back to the file I am working on - altering it - but I would prefer to export it.
Is there a way to call a function (to save the submatch) within substitute without damaging the file? Or, preferably, use the global search to capture a portion of the line and then pass that as a parameter onto a function call that would do the saving as desired?

Try
:%s/pattern \(saved portion\)/\=[submatch(0), SaverFunc(submatch(1))][0]/
or
:%s/pattern \(saved portion\)\zs/\=SaverFunc(submatch(1))[-1]
. In last case SaverFunc must return either string or number (without explicit :return statement it will return number 0). It does not matter which string or number will be returned: string_or_number[-1] always expands to an empty string.

You might be looking for :h :global and do something like this:
:g/pattern/call func_to_get_and_save_text()
That would call the function on every line matching the given pattern.

Related

Inject a code into the unique function in multiple files (Sublime editor)

Taking the advice of Xaelias I'll modify this post, because the initial question was quite unclear and overcomplicated.
So basically I have multiple script files that need to be edited (too many of them to afford to edit them manually). What I need to do for each script file is to insert a certain code at a specific location inside each of them.
So if my script file was called foo.script, and if the code inside it was as follows:
cut_bar() {
some code...
}
cut_cabbage() {
some code...
}
...
Then I'd like my final look of the foo.script file to be as follows:
cut_bar() {
some code...
message msg_foo_bar
}
cut_cabbage() {
some code...
message msg_foo_cabbage
...
(cut_ is a universal prefix shared by all those functions inside of which the code needs to be added).
Is there any way I can do this in Sublime Text editor? Or is there no other way but to develop a small program that does all of this (in which case, tips would be appreciated likewise!).
We'll try a first solution, which is far from perfect, but might be enough for what you want to do. If it does not work, we'll try another solution.
You want to do a search and replace (⌥⌘F (on mac) or Find→Replace...).
Make sure that the RegEx modifier is active (.* on the left)
Find What: (?<=^cut_)(.*?)(\(\)\h*\{(?:.|\v)*?)(^\}\h*$) (explanations below)
Replace With: \1\2\tmsg_foo_\1\n\3
If it does not work as intended, maybe we'll just need to tweak a little the RegEx. Or maybe we'll need to resort to python and ST plugins!
RegEx explanation:
(?<=^cut_): we want our match to start at the beginning of a line (^) with cut_ this is not captured because as you said, this is a constant, so we don't really care
(.*?): this matches the rest of the name of the method. This is captured and will be \1
(\(\)\h*\{(?:.|\v)*?): we stop the name capture at (), \h is for any horizontal space (spaces or tabs) that could be between the end of the name, and {, then we match every character and \v (vertical space such as new lines) (this is captured as \2)
(^\}\h*$): ... up until we meet a line that starts with }, and might have any kind of horizontal space before the end of the line ($), this is captured as \3
From there, the replace part is kind of straightforward I guess.
\1\2\ is everything we captured but do not want to modify (the name of the method, and its body, except for the last line })
Then we put what you wanted, msg_foo_\1 which will transform into msg_foo_bar for example, and then put back the ending }

Delete text with GREP in Textwrangler

I have the following source code from the Wikipedia page of a list of Games. I need to grab the name of the game from the source, which is located within the title attribute, as follows:
<td><i>007: Quantum of Solace</i><sup id="cite_ref-4" class="reference"><span>[</span>4<span>]</span></sup></td>
As you can see above, in the title attribute there's a string. I need to use GREP to search through every single line for when that occurs, and remove everything excluding:
title="Game name"
I have the following (in TextWrangler) which returns every single occurrence:
title="(.*)"
How can I now set it to remove everything surrounding that, but to ensure it keeps either the string alone, or title="string".
I use a multi-step method to process these kind of files.
First you want to have only one HTML tag per line, GREP works on each line so you want to minimise the need for complicated patterns. I usually replace all: > with >\n
Then you want to develop a pattern for each occurrence of the item you want. In this case 'title=".?"'. Put that in between parentheses (). Then you want add some filling to that statement to find and replace all occurrences of this pattern: .?(title=".?").
Replace everything that matches .?(title=".?").* with \1
Finally, make smart use of the Textwrangler function process lines containing, to filter any remaining rubbish.
Notes
the \1 refers to the first occurrence of a match between () you can also reorder stuff using multiple parentheses and use something like (.?), (.) with \2, \1 to shuffle columns.
Learn how to do lazy regular expressions. The use of ? in these patterns is very powerfull. Basically ? will have the pattern looking for the next occurrence of the next part of the pattern not the latest part that the next part of your pattern occurs.
I've figured this problem out, it was quite simple. Instead of retrieving the content in the title attribute, I'd retrieve the page name.
To ensure I only struck the correct line where the content was, I'd use the following string for searching the code.
(.)/wiki/(.)"
Returning \2
After that, I simply remove any cases where there is HTML code:
<(.*)
Returning ''
Finally, I'll remove the remaining content after the page name:
"(.*)
Returning ''
A bit of cleaning up the spacing and I have a list for all game names.

How can I pass the current line to a Vimscript function?

I am trying to create a Vim mapping that will operate on the current line, taking a string like this:
[boiled cabbage, mad donkey, elephant, very dark fudge]
And quoting all the list elements to end up with this:
["boiled cabbage", "mad donkey", "elephant", "very dark fudge"]
I tried with vim regexes, but figured it would be easier to write a function that takes the current line as an argument and returns the transformed line. I have no problem performing the transformation in vimscript. But how can I pass the current line to the function, and how do I replace the line with the transformed line?
To get current line you can use
let line=getline('.')
(note: you can also do getline(10, 20) to get a list of 11 lines).
To set current line you can use
call setline('.', line)
. You can also replace a number of lines starting with current if you pass a list to this function.
You can yank it into a register and then access it from there. "byy yanks the line your cursor is at. You can access it then by using #b
Check out http://vim.wikia.com/wiki/Word_under_cursor_for_command

Using Vim, how do you use a variable to store count of patterns found?

This question was helpful for getting a count of a certain pattern in Vim, but it would be useful to me to store the count and sum the results so I can echo a concise summary.
I'm teaching a class on basic HTML to some high schoolers, and I'm using this script to be quickly check numbers of required elements throughout all their pages without leaving Vim. It works fine, but when students have more than 10 .html files it gets cumbersome to add up the various sections by hand.
Something like:
img_sum = :bufdo %s/<img>//gen
would be nice. I think I'll write a ruby script to check the pages more thoroughly and check for structure, but for now I'm curious about how to do this in Vim.
The problem can be solved by a counter separate from the one built-in into the
:substitute command: Use Vim-script variable to hold the number of pattern
matches. A convenient way to register every match and modify a particular
variable accordingly, is to take advantage of the substitute with an
expression feature of the :substitute command (see :help sub-replace-\=).
The idea is to use a substitution that evaluates an expression increasing
a counter on every occurrence, and does not change the text it is operating
on.
The first part of the technique cannot be implemented straightforwardly
because it is forbidden to use Ex commands in expressions (including \=
substitute expressions), and therefore it is not possible to use the :let
command to modify a variable. Answering the question "gVim find/replace
with counter", I have proposed a simple trick to overcome that limitation,
which is based on using a single-item list (or dictionary containing a single
key-value pair). Since the map() function transforms a list or a dictionary
in place, that only item could be changed in a constrained expression context.
To do that, one should call the map() function passing an expression
evaluating to the new value along with the list containing the current value.
The second half of the technique is how to avoid changing text when using
a substitution command. In order to achieve that, one can make the pattern
have zero-width by prepending \ze or by appending \zs atoms to it (see
:help /\zs, :help /\ze). In such a way, the modified pattern captures
a string of zero width just before or after the occurrence of the initial
pattern. So, if the replacement text is also empty, substitution does not
cause any change in the contents of a buffer. To make the substitute
expression evaluate to an empty string, one can just extract an empty
substring or sublist from the resulting value of that expression.
The two ideas are put into action in the following command.
:let n=[0] | bufdo %s/pattern\zs/\=map(n,'v:val+1')[1:]/ge
I think that answer above is hard to understand and more pretty way to use external command grep like this:
:let found=0
:bufdo let found=found+(system('grep "<p>" '.expand('%:p') . '| wc -l'))
:echo found

How to restrict operations to certain lines?

I have to work on some relatively huge code files in vim.
How do I restrict some operations like find-next normal-n and others to a certain function / block?
How would I visually know if I'm within that block or outside it?
Looking and line numbers seems awkward, specially that the line numbers I need to work with are generally 5 digits long!
You can set marks at the beginning and end of your block using m (e.g. ma, mb) and then refer to them in the range of a ex command as :'a,'b.
Like bignose said you can use a visual block to create an implicit region for a command, which can be passed to an ex command using :'<,'>
You can also use regexes to delimit a block (e.g. for all the lines between start and end use :/start/,/end/
For example, to make a substitution in a range of lines:
:'<,'>s/foo/bar/g
:'a,'bs/baz/quux/g
:/harpo/,/chico/s/zeppo/groucho/g
The last visually selected range is remembered so you can reuse it without reselecting it.
For more on ranges, see :help range
You can further restrict yourself within a range by using g//. For example, if you wanted to replace foo with bar only on lines containing baz in the selected range:
:'<,'>g/baz/s/foo/bar/g
When you define a new ex command, you can pass the range given to the ex-command using as <line1>,<line2>. See :help user-commands for more on defining ex-commands.
Within a vimscript function, you can access an implicitly passed range using a:firstline and a:lastline. You can detect your current linenumber using line('.'), and detect whether you're inside the block using normal boolean logic (a:firstline <= line('.') && line('.') <= a:lastline). See :help functions for more on vimscript functions.
Another approach, is to use vim's inner i and single a selectors. For example, to delete the entirety of a double quoted string, use da" in normal mode. To leave the quotes, use di". See :help object-select for more.
Vimtips has exactly what you were looking for:
Search in current function
See also :help pattern-atoms
Vim uses Shift-v to select by lines. Having selected a series of lines, many commands will then be restricted to the selection.
I don't think the search commands (/, n, etc.) are restricted that way though.
For commands like search and replace, you can easily limit yourself to a couple of lines:
:.,+3s/foo/bar/cg
replaces every occurrence of "foo" in the current line and the following 3 lines with "bar". I don't think you can do that for search, though.

Resources