Is there a way in Vim (or a plugin) to search for a term and iterate through the search results (as per n in Vim), by column, rather than row? So if my file was this:
foo1 bar bar
baz baz foo3
baz baz foo4
foo2 bar bar
If I search for foo I want to iterate through the results in order 1,2,3,4. Normally n would move me through them in order 1,3,4,2.
I want this for example to browse through search results in a huge fixed-width data file or CSV file, where columns represent fields.
(I'll also settle for a way to do it in Emacs instead. :)
After seeing this question I decided to try and write a vim plugin to handle it. It's my first vim plugin, so it's probably very bad. However, it does work for the example you gave.
You can download the plugin from vim.org: SearchCols.vim
Note that it also requires the multvals plugin from the same site: multvals.vim
It creates a command :SearchCols <string>, which can be used to search for the <string> in fixed-width columns so that the search looks in the first column first, then the second column, etc. rather than the row-by-row search which is standard for vim. Repeat the command via #: and then ## since it's a colon-command.
Assumptions:
Assumes that your file is formatted with whitespace-delimited fixed width columns, i.e. there are no spaces inside the data items, only in between them.
Assumes that the first line of your file is formatted so that the column widths can be deduced from it.
Obvious improvements that I can think of would be to make searching for the next item easier than using #: and ##, including search highlighting (the code is there but it's commented out because it uses :match and you would have to use :match none to turn it off), and eliminating the dependency on multvals.vim.
EDIT: I just found an overview of using vim regular expressions for tables here. That's a lot cleaner than my amateur attempt (although this works too ;)):
searching for ^foo will search the first column, while searching for .*\ foo will search the second column, and .*\ .*\ foo will search the third.
This is not complete of course since it matches a whole block from the start of the line, so it will find foo3 and foo4 but puts the cursor at position 0.
You can however automate this with the recording function. Type exactly the following in normal mode:
qa/.*\ .*\ foo
/foo
q
(where I have used new lines to indicate you should hit the return key).
This puts two searches under the a register (qa starts recording under a, qb under b, etc). Now hit #a and you should search only the third column....
I'm sure you can turn this into a command that takes a column number, but I'm off now, sorry :)
Related
How can I find the first occurrence of bar after the next occurence of Foo using one command/search pattern in Vim?
Ex: Suppose I have some text like:
Bar and bar and bar, I'm such a barbarian.
...
Foo: That bar serves German beer.
Bar bar and bar, so much bar.
Foo: Unrelated.
...bla
but then next bar.
How do I jump between the bolded bars?
The first way that came to mind (easy but not repeatable)
You can do something like this:
/Foo/;/bar
but the "last pattern" will be bar so this is not repeatable with n, N, etc. which means that you will have to do it once and then do /<Up><CR>, which may or may not satisfy your needs.
Note that, once you have jumped to the second match, you can do `` to jump back to the first one and `` to jump back to the second one, and so on.
See :help :; and :help ``.
The second way that came to mind (more complicated but repeatable)
With a single pattern, and thus repeatable with n, N, etc., you can do:
/Foo\_.\{-}\zsbar
where…
/Foo matches the next Foo,
\_.\{-} matches any character including EOL, as few as possible,
\zs starts the actual match, discarding what comes before,
which we want to be bar.
See :help \_., :help \{, and :help \zs.
I haven't figured out a way to combine the following two methods, but they answer your two requests independently.
How to find the first occurrence using one command::norm /Foo^M/bar^M
where ^M is not typed directly, you must press ctrl (or in my case ctrl+shift), then press v, then m. ^M means enter/return button, so you're basically doing two separate searches in one line. Because they're separate, doing n or N to move forwards or backwards only moves forward and backwards between bar, whether or not they come after a Foo. Which leads to:
How to move to the next bar that comes after Foo. You can do this by recording a macro. Press q then the letter to store the macro to start recording, then you can do a /Foo search, then a /bar search. Press q again to stop recording. Now you can just do #a where a is the letter you chose, and that will get you to the next bar after the next Foo. You can repeat the command to keep searching forward, or you can create another macro for doing the search backwards.
Let's say I have a numbered list like this in markdown:
1. This is a line of text.
2. This is a line of text.
3. This is a line of text.
4. This is a line of text.
Is there an easy way to insert a new line for number one and add one to all the other lines using vim? This is what I want to achieve:
1. This is a new line of text.
2. This is a line of text.
3. This is a line of text.
4. This is a line of text.
5. This is a line of text.
I want to be able to do this quickly - not having to record a macro for instance.
A completely different approach would be not to worry about manually numbering at all. Markdown doesn't care what numbers are in your source file, so
1. Foo
1. Bar
1. Baz
Renders as
Foo
Bar
Baz
I normally number all of my ordered lists with 1. and let the Markdown processor take care of actual numbering.
There's a couple ways you could do this. Probably the simplest way is to add a newline starting with a '0.'. Then, when you are happy with this list you can increment every number with vG<C-a>. (Of course, it's not the whole buffer you could use a different movement such as ip or })
If you are going to add a line many times, it might be easier to add all of the numbers at the end when you are done editing. This could be done by visually selecting the lines you want and doing g<C-a>.
I don't remember exactly when these feature's were added, but this requires a relatively new version of vim. It works for me in 8, and I remember this working for 7.4 with the right patches.
If you are on an older version of vim, you could select the lines you want and use a substitute command to add the appropriate numbers. For example:
:s/^/\=(line('.') - line("'<") + 1).'. '
This: You can use the command below after selecting those lines by V4j starting from first line:
:'<,'>s/\d/\=submatch(0)+1/ | normal! goO1. This is a new line of text.
Or: Put the cursor on first line and type:
O0. This is a new line of text.Esc0Ctrl-v4jCtrl-a
O : letter o in capital case (shift+o)
0 : digit 0
I have a list of products to place on a rails seed and I would like to instead of put brackets one by one on the list with a command place the brackets on the whole list?
for example:
1. Dakine
2. Dale of Norway
3. Dan Post
1. ["Dakine"],
2. ["Dale of Norway"],
3. ["Dan Post"],
I searched on the help but did not find any about. Thanks.
You can record a macro in Vim and repeat that.
If you are on number 1, you can do following:
qqf a["Esc$a"],Esc0jq
Explanation:
qq: Start recording macro in register q
f: Go to first space character
a: : Insert after (the space character from above)
\[": Insert those characters
Esc: Back to normal mode
$: Go to end of line
a: Insert after (end of line)
"],: Insert the characters
Esc: Back to normal mode
0: Jump to start of line
j: Go down one line
If you have 100 such lines, you can do 100#q to achieve your result.
With vim substitute command:
:%s/.*/["&"]/
If you don't want to operate on all lines, then select the ones you want to transform or note the related line numbers, and then type :s/..... without the %. You'll see actually :'<,'>s this range represent the visually selected lines, and vim adds it automatically in visual mode.
On Atom you can enable the find to use Regex in the search(there is a button next to the search field)
Then you can search for something like (^.*$) to get every line separated by groups and in the Replace field you use ["$1"],. The $1 represents the value matched by the Regex.
Then just do a Replace All and remove the last comma in your list if needed.
I have a line that looks like the following, which I am viewing in vim.
0,0,0,1.791759,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5.278115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
It is from a feature vector file, each row is an instance and each column is the feature value for that feature number. I would like to figure out which feature number 5.27 corresponds to. I know the
s/,//gn
will count the number of commas in the line, but how do I restrict the command to count the number of commas in the line up to the columns with the number 5.27?
I have seen these two posts that seem relevant but cannot seem to piece them together: How to compute the number of times word appeared in a file or in some range and Search and replace in a range of line and column
s/,\ze.*5\.27//gn
The interesting part is the \ze which sets the end of the match. See :h /\ze for more information
Select the wanted area with visual mode and do
:s/\v%V%(,)//gn
\v enables us to escape less operators with \
%V limits the search to matches that start inside the visual selection
%() keeps the search together if you include alternations with |
It's not pretty but it works. See help files for /\v, \%V and \%(
There are also several versions of a plugin called vis.vim, which offers easier commands that aim to do just the above. However I haven't gotten any of them to work so I'll not comment on that further.
try this
s/,.\{-}5.27//gn
it should work.
I’m a fan of Visual mode in Vim, as it allows to insert text before any given column.
For example, insertion of spaces after the quotation leaders below:
> one
> two
> three
can be done via <Ctrl-V>jjI <Esc>:
> one
> two
> three
as follows:
Start Visual mode with Ctrl-V.
Extend visual selection with jj.
Insert some spaces with I__.
Propagate the change to all the lines of the block selection with Esc.
Now I have a text file that needs some formatting. This is what it looks like:
start() -- xxx
initialize() -- xxx
go() -- xxx
Now I want to align part of this text to arrange it into columns like this:
start() -- xxx
initialize() -- xxx
go() -- xxx
The problem I have is that I cannot insert a different amount of indentation into each line and merely indenting a fixed amount of spaces/tabs is insufficient.
How can you do an indentation where all indented text will have to be aligned at the same column?
Update
I only figured out a rather verbose and unwieldy method:
Find the string position to indent from: \--.
Insert n (let's say 20) spaces before that: 20i <Esc>.
Delete a part of those spaces back to a certain column (let's say 15): d|15.
Save those steps as a macro and repeat the macro as often as necessary.
But this approach is very ugly, though!
I'm much better off without any vim plugins.
Here is my solution:
<Shift-V>jj:!column -ts --
Then insert -- into multiple lines just as you wrote in the question.
You can also append a number of comments at insertion time.
:set virtualedit=all
<Ctrl-V>jjA-- xxx<Esc>
You have to use a specific plugin, you can use either Tabular or Align plugin in this case.
They both allow you to align text on specific characters, like -- in your example. Their syntax is a bit different though. Pick the one that suit you the most.
Without plugin and if you have already entered your comments without emix's solution:
:,+2 s/--/ &
This will ensure all comments are to be shifted leftwise in order to align them properly.
Then blockwise select the column to which you want to align the text, and : 100<
An easy way to align text in columns is to use the Tabular or
Align plugin. If neither of these is ready at hand, one can use
the following somewhat tricky (and a little cumbersome looking) yet
perfectly working (for the case in question) commands.1,2
:let m=0|g/\ze -- /let m=max([m,searchpos(#/,'c')[1]])
:%s//\=repeat(' ',m-col('.'))
The purpose of the first command is to determine the width of the
column to the left of the separator (which I assume to be --
here). The width is calculated as a maximum of the lengths of the text
in the first column among all the lines. The :global command is used
to enumerate the lines containing the separator (the other lines do
not require aligning). The \ze atom located just after the beginning
of the pattern, sets the end of the match at the same position where
it starts (see :help \ze). Changing the borders of the match does
not affect the way :global command works, the pattern is written in
such a manner just to match the needs of the next substitution
command: Since these two commands could share the same pattern, it can
be omitted in the second one.
The command that is run on the matched lines,
:let m=max([m,searchpos(#/,'c')[1]])
calls the searchpos() function to search for the pattern used in the
parent :global command, and to get the column position of the match.
The pattern is referred to as #/ using the last search pattern
register (see :help "/). This takes advantage of the fact that the
:global command updates the / register as soon as it starts
executing. The c flag passed as the second argument in the
searchpos() call allows the match at the first character of a line
(:global positions the cursor at the very beginning of the line to
execute a command on), because it could be that there is no text to
the left of the separator. The searchpos() function returns a list,
the first element of which is the line number of the matched position,
and the second one is the column position. If the command is run on
a line, the line matches the pattern of the containing :global
command. As searchpos() is to look for the same pattern, there is
definitely a match on that line. Therefore, only the column starting
the match is in interest, so it gets extracted from the returning list
by the [1] subscript. This very position equals to the width of the
text in the first column of the line, plus one. Hence, the m variable
is set to the maximum of its current value and that column position.
The second command,
:%s//\=repeat(' ',m-col('.'))
pads the first occurrence of the separator on all of the lines that
contain it, with the number of spaces that is missing to make the text
before the separator to take m characters, minus one. This command
is a global substitution replacing an empty interval just before the
separator (see the comment about the :global command above) with the
result of evaluation of the expression (see :help sub-replace-\=)
repeat(' ',m-col('.'))
The repeat() function repeats its first argument (as string) the
number of times given in the second argument. Since on every
substitution the cursor is moved to the start of the pattern match,
m-col('.') equals exactly to the number of spaces needed to shift
the separator to the right to align columns (col('.') returns the
current column position of the cursor).
1 Below is a one-line version of this pair of commands.
:let m=0|exe'g/\ze -- /let m=max([m,searchpos(#/,"c")[1]])'|%s//\=repeat(' ',m-col('.'))
2 In previous revisions of the answer the commands used
to be as follows.
:let p=[0]|%s/^\ze\(.*\) -- /\=map(p,'max([v:val,len(submatch(1))+1])')[1:0]/
:exe'%s/\ze\%<'.p[0].'c -- /\=repeat(" ",'.p[0].'-col("."))'
Those who are interested in these particular commands can find their
detailed description in this answer’s edit history.
This is a modification on Benoit's answer that has two steps.
First step, block select text search and replace -- with lots of spaces.
'<,'>s/--/ --/
Now all the comments should have lots of spaces, but still be uneven.
Second step, block select the text again and use another regex to match all the characters you want to keep (say the first 20 characters or so) plus all the spaces following, and to replace it with a copy of those first 20 characters:
'<,'>s/\(.\{20}\)\s*/\1/
Not quite as easy as Benoit's, but I couldn't figure out how to make his second step work.