VIM - Hotkey for VIMGREP on Visual Selection in current buffer - regex

How would I go about setting up a hotkey (eg: CTRL+g) to perform a VIMGREP operation on the current visual selection in the current buffer? My intent is to show a line-numbered list in the "quickfix" window of all matching search results.
Right now, if I want to get a list of results for a regex search, I could do a command-mode query like so:
:vimgrep /foo/ %
However, there are two problems with this:
I don't want to have to type out the entire query. I could always do a visual selection, then use CTRL+r, CTRL+w, to paste the current visual selection into the command buffer, but I'd like something simpler than this.
The above approach requires that the current buffer is already saved to a file. I'd like to be able to work on a temporary buffer I've pasted into VIM rather than having to save a file buffer each time I want to do this.
Thank you.

A low-level solution
Try [I and the :ilist command:
[I " lists every occurrence of the word under the cursor
" in the current buffer (and includes)
:ilist /foo<CR> " lists every occurrence of foo in the current buffer
" (and includes)
Press : followed by a line number and <CR> to jump to that line.
You can use them on the visual selection with a simple mapping:
xnoremap <key> "vy:<C-u>ilist /<C-r>v<CR>:
You'll probably need to sanitize the register upon insertion, though.
See :help :ilist.
Another even lower-level solution
Since we are at it, let's dig even deeper and find the amazingly simple and elegant:
:g/foo/#
that you could use in the same way as :ilist above:
xnoremap <key> "vy:<C-u>g/<C-r>v/#<CR>:
Limitations
The solutions above don't use the quickfix window, obviously, but they allow you to:
see their result as a list,
use line numbers to actually get to where you want.
They have limitations, though:
the list is not cached so you must perform the search again if you want to get to a different occurrence,
the list is not transient like the quickfix list so you can't use navigation commands like :cnext or :clast to move around the result.
A higher-level solution
If those limitations are a showstopper, the function below, adapted from justinmk's answer in this /r/vim thread, gives you an almost complete solution:
press [I in normal mode to search for the word under the cursor in the whole buffer,
press ]I in normal mode to search for the word under the cursor after the current line,
press [I in visual mode to search for the selected text in the whole buffer,
press ]I in visual mode to search for the selected text after the current line.
The function below uses the quickfix list/window when the buffer is associated to a file and falls back to the regular behavior of [I and ]I otherwise. It could probably be modified to be used as part of an :Ilist command.
" Show ]I and [I results in the quickfix window.
" See :help include-search.
function! Ilist_qf(selection, start_at_cursor)
" there's a file associated with this buffer
if len(expand('%')) > 0
" we are working with visually selected text
if a:selection
" we build a clean search pattern from the visual selection
let old_reg = #v
normal! gv"vy
let search_pattern = substitute(escape(#v, '\/.*$^~[]'), '\\n', '\\n', 'g')
let #v = old_reg
" and we redirect the output of our command for later use
redir => output
silent! execute (a:start_at_cursor ? '+,$' : '') . 'ilist /' . search_pattern
redir END
" we are working with the word under the cursor
else
" we redirect the output of our command for later use
redir => output
silent! execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . "I"
redir END
endif
let lines = split(output, '\n')
" better safe than sorry
if lines[0] =~ '^Error detected'
echomsg 'Could not find "' . (a:selection ? search_pattern : expand("<cword>")) . '".'
return
endif
" we retrieve the filename
let [filename, line_info] = [lines[0], lines[1:-1]]
" we turn the :ilist output into a quickfix dictionary
let qf_entries = map(line_info, "{
\ 'filename': filename,
\ 'lnum': split(v:val)[1],
\ 'text': getline(split(v:val)[1])
\ }")
call setqflist(qf_entries)
" and we finally open the quickfix window if there's something to show
cwindow
" there's no file associated with this buffer
else
" we are working with visually selected text
if a:selection
" we build a clean search pattern from the visual selection
let old_reg = #v
normal! gv"vy
let search_pattern = substitute(escape(#v, '\/.*$^~[]'), '\\n', '\\n', 'g')
let #v = old_reg
" and we try to perform the search
try
execute (a:start_at_cursor ? '+,$' : '') . 'ilist /' . search_pattern . '<CR>:'
catch
echomsg 'Could not find "' . search_pattern . '".'
return
endtry
" we are working with the word under the cursor
else
" we try to perform the search
try
execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . "I"
catch
echomsg 'Could not find "' . expand("<cword>") . '".'
return
endtry
endif
endif
endfunction
nnoremap <silent> [I :call Ilist_qf(0, 0)<CR>
nnoremap <silent> ]I :call Ilist_qf(0, 1)<CR>
xnoremap <silent> [I :<C-u>call Ilist_qf(1, 0)<CR>
xnoremap <silent> ]I :<C-u>call Ilist_qf(1, 1)<CR>
NB: <C-r><C-w> inserts the word under the cursor, not the visual selection for which there's unfortunately no such shortcut. We have no choice but to yank.

Grepping a scratch buffer
You can use the :global command combined with :caddexpr to add entries to the current quickfix list. Here is the example from :h :caddexpr:
:g/mypattern/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
There are a few issues with this:
This approach only does one match per line
Does not start a new quickfix list
Really long to type out
Assumes the default global 'errorformat' hasn't been changed
To overcome these issues (all but the multiple matches per line) put the following command in your ~/.vimrc file:
command! -nargs=1 -bar Cgrep
\ let s:errorformat = &errorformat |
\ try |
\ let &errorformat='%f:%l:%m' |
\ cexpr [] |
\ execute 'g'.<q-args>.'caddexpr expand("%").":".line(".").":".getline(".")' |
\ cc |
\ finally |
\ let &errorformat = s:errorformat |
\ endtry
Now you can use :Cgrep/foo/ to grep the current buffer.
A visual mapping
To make it so you can do a visual version of this you need to yank in the selected text and pass it to our :Cgrep command via <c-r>. Here is an example visual mapping g/ to do just that:
xnoremap g/ y:<c-u>Cgrep/<c-r>"/<cr>
There are some issues with this mapping too:
This clobber the unnamed register, #"
This assumes the visually selected text will be a valid pattern
The following mapping fixes the mapping by using the expression register via <c-r>= and very no magic, \V:
xnoremap g/ :<c-u>let #/=#"<cr>gvy:let [#/,#"]=[#",#/]<cr>Cgrep/\V<cr>=substitute(escape(#/,'/\'),'\n','\\n','g')<cr>/<cr>
Conclusion
Personally I would forgo the mapping and get a visual star plugin (there are few out there). There is a nice Vimcast about this: Search for the selected text. I would then use that with the :Cgrep command we just created via :Cgrep// or better yet xmap g/ *:Cgrep//<cr>
For more help see:
:h :caddexpr
:h :cexpr
:h :g
:h 'efm
:h registers
:h /\V

To search for the visual selected text in files *.c via the hotkey CTRL+g do:
:vmap <silent> <unique> <c-g> y:vimgrep "<c-r>"" *.c<CR>
Two problems remain:
You want to search in a buffer, not in a file.
You want line numbers in the quickfix.
To 1:
As far as I know vimgrep can only search in files. The solution would be to write the buffer in a temporary file and search in this file and delete the temporary file when it is no longer needed. This solution requires a script which is called via the hotkey.
Just a hint: To get a suitable filename the VIM function tempname() can be used. Example:
let s:tmpfile = tempname()
(Sorry, I currently have no time to show a script solution here, maybe I add one later. Maybe somebody else has a better solution or can provide a script?)
To 2:
This command will enable line number in the current buffer:
:set number

Related

How to merge two list and make a dictionary using vim in vscode?

Imagine we have two list in two files,
file 1: contact_list_names.txt like:
Ali
James
Amir
Jones
Sarah
file 2 :contact_phones.txt like:
12344
43211
09876
67890
12309
I know that we can select all using gg then v then Shift + g to select all , but when you paste using p key its not pasting every number in front of its contact name ...
So how to paste the numbers in front of its related contact name ?
The problem using the clipboard to paste columns side by side is the type of selection of it, see here:
:echo getregtype('+')
if it returns a lowercase v, it means the type is characterwise.
if it returns an uppercase V, it means the type is linewise.
if it returns ^V{some number}, it means the type is blockwise, and the width of the block is {some number}.
But we can change it this way (making the clipboard register blockwise):
:call setreg('+',#+,'b')
Now if you try to paste a new column before or after the first one
you will succeed!
Here a function to get the content of the clipboard and paste after
the current column
fun! GiveItaNameYouWant()
call setreg('+',#+,'b')
normal gg
execute "normal! A \<esc>"
execute "normal! \"+p"
endfun
If you copy this function to the clipboard and load it into the memory just run:
:#+
Now put the cursor at the first column and run:
:call GiveItaNameYouWant()
Note: I am referring only to the clipboard register but you can also make these changes on the primary register selection "*", in the yank register "0" and default register '"' and a-z ones.
References: https://stackoverflow.com/a/48678100/2571881

Preserve indentation in C++ comments in vim

Is it possible to configure vim and cindent to not alter indentation in c++ comments when reindenting the file (gg=G) ?
I have some formated lists in comments aligned with 4 spaces but vim interpret this as bad indent and realign everything.
For example:
/**
my list:
* item 1
* item 2
*/
becomes:
/**
my list:
* item 1
* item 2
*/
I want a way to tell vim: "Don't touch to comments content but indent everything else."
It is important because our project use doxygen with a markdown like parser to generate documentation and indentation is used by list levels.
How about writing like this so in-comment indentation is independent of comment indentation:
/**
* my list:
* * item 1
* * item 2
*/
As suggested by review, I repost an answer with answer from vi stackexchange community here:
I don't believe it's possible to achieve this with 'cinoptions'.
The correct solution is probably to write a new indentexpr that applies C-indenting (accessible via the cindent() function) only to lines that aren't within comments.
However, here's a couple of quick and dirty solutions:
I skipped first solution which I don't use and is therefore not the answer. You can still see it on the original post.
Using a Function
function! IndentIgnoringComments()
let in_comment = 0
for i in range(1, line('$'))
if !in_comment
" Check if this line starts a comment
if getline(i) =~# '^\s*/\*\*'
let in_comment = 1
else
" Indent line 'i'
execute i . "normal =="
endif
else
" Check if this line ends the comment
if getline(i) =~# '\*\/\s*$'
let in_comment = 0
endif
endif
endfor
endfunction
You can run this with :call IndentIgnoringComments() or you could set up a command or a mapping. e.g.:
nnoremap <leader>= :call IndentIgnoringComments()<CR>
I personaly defined a command which call this function and combine it with another reformating I apply often on files in this project (:%s/\s*$//g).
Thank to Rich on https://vi.stackexchange.com
Original post: https://vi.stackexchange.com/a/13962/13084

Search while selection enabled in macro VS2015

On VAX/VMS (or OpenVMS Alpha and its other names) there was an editor called TPU. In TPU you could enable selection of text independently of holding a key down. You pressed SELECT and then any cursor movement you made selected text between the editing point and the new cursor location.
You could also record macros. So you could use this text selection feature to create macros like:
find "abc"
select
find "xyz"
cut
stop recording
So this macro would find any line with "abc" in it and then cut all text between "abc" and "xyz". Massive time saver.
Making sense? How can I do that in VS2015? I can't find a macro extension that provides the selection behaviour I need to do this.
Cheers,
.pd.
EDIT
It occurred to me this could be done with a regex but it seems like a pretty big ask.
#Html.DropDownListFor(m => m.Property, Model.SelectListProperty, htmlAttributes: new { #class="whatever" })
// the regex would replace this with
#Html.MyDropDownListFor(m => m.Property, Model.SelectListProperty, Model.Property, htmlAttributes: new { #class="whatever"})
So I would be looking for a regex to
- find #Html.DropDownList
- replace the token 1 of that line split by ',' with token 1 of token 0 split by '.' and prefixed with "Model."
Assuming Model.Property comes from m => m.Property.
Search for
#Html\.DropDownListFor\(((\w+)\s*=>\s*\2\.(\w+)),\s*(Model\.\w+)(,(?:[^(){}]|\{[^{}]*\})*)?\)
Replace with
#Html.MyDropDownListFor($1, $4, Model.$3$5)
Demo: http://regexr.com/3f3io

Retrieve the coding amino-acid when there is certain pattern in a DNA sequence

I would like to retrieve the coding amino-acid when there is certain pattern in a DNA sequence. For example, the pattern could be: ATAGTA. So, when having:
Input file:
>sequence1
ATGGCGCATAGTAATGC
>sequence2
ATGATAGTAATGCGCGC
The ideal output would be a table having for each amino-acid the number of times is coded by the pattern. Here in sequence1, pattern codes only for one amino-acid, but in sequence2 it codes for two. I would like to have this tool working to scale to thousands of sequences. I've been thinking about how to get this done, but I only thought to: replace all nucleotides different than the pattern, translate what remains and get summary of the coded amino-acids.
Please let me know if this task can be performed by an already available tool.
Thanks for your help. All the best, Bernardo
Edit (due to the confusion generated with my post):
Please forget the original post and sequence1 and sequence2 too.
Hi all, and sorry for the confusion. The input fasta file is a *.ffn file derived from a GenBank file using 'FeatureExtract' tool (http://www.cbs.dtu.dk/services/FeatureExtract/download.php), so a can imagine they are already in frame (+1) and there is no need to get amino-acids coded in a frame different than +1.
I would like to know for which amino-acid the following sequences are coding for:
AGAGAG
GAGAGA
CTCTCT
TCTCTC
The unique strings I want to get coding amino-acids are repeats of three AG, GA, CT or TC, that is (AG)3, (GA)3, (CT)3 and (TC)3, respectively. I don't want the program to retrieve coding amino-acids for repeats of four or more.
Thanks again, Bernardo
Here's some code that should at least get you started. For example, you can run like:
./retrieve_coding_aa.pl file.fa ATAGTA
Contents of retrieve_coding_aa.pl:
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
use Bio::SeqIO;
use Bio::Tools::CodonTable;
use Data::Dumper;
my $pattern = $ARGV[1];
my $fasta = Bio::SeqIO->new ( -file => $ARGV[0], -format => 'fasta');
while (my $seq = $fasta->next_seq ) {
my $pos = 0;
my %counts;
for (split /($pattern)/ => $seq->seq) {
if ($_ eq $pattern) {
my $dist = $pos % 3;
unless ($dist == 0) {
my $num = 3 - $dist;
s/.{$num}//;
chop until length () % 3 == 0;
}
my $table = Bio::Tools::CodonTable->new();
$counts{$_}++ for split (//, $table->translate($_));
}
$pos += length;
}
print $seq->display_id() . ":\n";
map {
print "$_ => $counts{$_}\n"
}
sort {
$counts{$a} <=> $counts{$b}
}
keys %counts;
print "\n";
}
Here are the results using the sample input:
sequence1:
S => 1
sequence2:
V => 1
I => 1
The Bio::Tools::CodonTable class also supports non-standard codon usage tables. You can change the table using the id pointer. For example:
$table = Bio::Tools::CodonTable->new( -id => 5 );
or:
$table->id(5);
For more information, including how to examine these tables, please see the documentation here: http://metacpan.org/pod/Bio::Tools::CodonTable
I will stick to that first version of what you wanted cause the addendum only confused me even more. (frame?)
I only found ATAGTA once in sequence2 but I assume you want the mirror images/reverse sequence as well, which would be ATGATA in this case. Well my script doesn't do that so you would have to write it up twice in the input_sequences file but that should be no problem I would think.
I work with a file like yours which I call "dna.txt" and a input sequences file called "input_seq.txt". The result file is a listing of patterns and their occurences in the dna.txt file (including overlap-results but it can be set to non-overlap as explained in the awk).
input_seq.txt:
GC
ATA
ATAGTA
ATGATA
dna.txt:
>sequence1
ATGGCGCATAGTAATGC
>sequence2
ATGATAGTAATGCGCGC
results.txt:
GC,6
ATA,2
ATAGTA,2
ATGATA,1
Code is awk calling another awk (but one of them is simple). You have to run
"./match_patterns.awk input_seq.txt" to get the results file generated.:
*match_patterns.awk:*
#! /bin/awk -f
{return_value= system("awk -vsubval="$1" -f test.awk dna.txt")}
test.awk:
#! /bin/awk -f
{string=$0
do
{
where = match(string, subval)
# code is for overlapping matches (i.e ATA matches twice in ATATAC)
# for non-overlapping replace +1 by +RLENGTH in following line
if (RSTART!=0){count++; string=substr(string,RSTART+1)}
}
while (RSTART != 0)
}
END{print subval","count >> "results.txt"}
Files have to be all in the same directory.
Good luck!

Move to the middle of a block code

Using Vim I'm trying to move the cursor in the middle of a code block, but I can't figure out how to do this :
//cursor is for instance here.
{
//or here
//some code
// .... **** move cursor here ****
//some more code
}
The final idea is to have a shortcut that saves the current position, moves the cursor in the middle of the code block, sets the current line to the middle of the screen (with the shortcut "zz"), and then moves back to the saved position.
I'd prefer a built-in vim function, but a plugin works too.
EDIT: This is for c++ and so I want it for brackets {}.
I gave it a (quick and dirty) go:
function! Middleize()
" use ]M to jump to either the end of the current method if we are in it
" or the start of the next method if we are above the method
normal! ]M
" we record the current line number
let first_line = line('.')
" we go to the other end of the method
normal! %
" we record the current line number
let second_line = line('.')
" we started either from the top or from the bottom of the method
" so we have to take that into account when calculating the number
" of the line we want to jump to
if first_line < second_line
let middle_line = first_line + ((second_line - first_line) / 2)
else
let middle_line = ((first_line - second_line) / 2) + second_line
endif
" let's go!
execute "normal! " . middle_line . "Gzz"
endfunction
nnoremap <F5> :call Middleize()<CR>
More of a general solution but might be useful - the easy-motion plugin allows you to jump all over the place with great precision.
For example:
<Leader><Leader>w (default) - 'word motion'
g
Then to jump back, you'd just do the same thing backwards (in this case, <Leader><Leader>b g.
This doesn't set the current line to the middle of the screen, although you can :set scrolloff=9999 to have the middle of the screen follow your cursor around.
This won't give you exactly what you want, but it'll get text of the function on the screen (assuming it's not too long).
ma - set a mark at the current cursor position.
Repeatedly press } (jump ahead by a paragraph) until you can see the code you want.
`a - return to the mark you set.
A "paragraph" in vim terms is a group of consecutive non-blank lines. It's a nice approximation for a block of code. Also note that you can use any letter for the mark command, so there can be up to 52 of them active at once.