how grep results into separate buffer or split window - vim

i try to grep current file and then the results to be in separate buffer or split window , NOT in the current buffer im in
i need it to work with simple vim not using quickfix methods (vimgrep and such)
i did try this :
:r!grep foo %
VIM: How to store the grep results in a buffer
but it replaces the current buffer and only doing undo returns the previous file content
so my question is:
how to redirect the results or separate buffer or split window

Open a new buffer (here in a vertical window):
:vnew
Fill it with the output of a grep of the alternate file:
:0r!grep foo #
In one go:
:vnew | 0r!grep foo #

I'm not sure if this works perfectly in all scenarios, but give this a shot:
:execute "vnew" "tmp_buffer_" . winbufnr(winnr()) | execute "0r !grep foo" bufname(winbufnr(winnr()+1))
The first part should create a new buffer in a vertical split. I gave it a subscript with the buffer number it is associated with to avoid possible clashes with running this on multiple buffers. The second performs the read operation and figures which buffer to run the grep on by assuming the buffer to be grepped is in the next window over (which I believe should hold true since we've done a fresh split)

Related

Referencing contents of buffers in external (n)vim commands

So far I have only found the :%! <cmd> way to pipe the current buffer through an external command.
But I am wondering if there is a way to have two or three viewports open and reference their respective buffers, for both input parameters and results of the external command.
Simple example:
I would like to have an open viewport with a file, a viewport with a awk (or jq or anything) script, and a third "result" viewport.
Then I would like to run awk (or jq, etc.) to use the script from the second viewport, running on the file in the first viewport and write the result to the third viewport.
Similar to what vim-jqplay does (unfortunately it is not compatible with nvim)
Thank you!
if there is a way to have two or three viewports open
In Vim slang it's called "windows". Please, don't use inappropriate terms anymore.
So far I have only found the :%! way to pipe the current buffer through an external command.
Actually, there are also :r !{cmd} (read from cmd, i.e. redirect command's stdout into the buffer) and :w !{cmd} (write to cmd, i.e. redirect command's stdin to read from the buffer).
Then I would like to run awk (or jq, etc.) to use the script from the second viewport, running on the file in the first viewport and write the result to the third viewport.
Well, you realize that you can't have two stdin redirections at the same time, do you? In principle, you can do something like
call appendbufline(3, '$', systemlist('gawk -f '..bufname(1), 2))
Here 1 is a script buffer number, 2 is datafile bufnr, and 3 is a target bufnr. But note that buffer 1 is passed simply as a filename (no redirection), so it must be saved before the command to run.
But I don't think it's so important to redirect stdin at all, as you can pass datafile name too. So, assuming the target buffer (3) is active, you can do this
:r !gawk -f #1 #2
Of course, both script (buffer 1) and data (buffer 2) must be saved to disk before running this.

Copy specific line from less

How to copy a specific line from less ? Lets say I am opening a man ( which is by default opened by less ) and want to select and copy it to clipboard and after that lets say paste it to file opened in vim ? I don't want to use the mouse wheel to paste. I am looking for a simple Ctrl-c , Ctrl-v method as in windows.
When opening a man page I can't switch to my default editor (which is vim ) with 'v' key because less shouts with "Cannot edit standard input" error.
Thanks a lot and sorry if this question is silly.
tl;dr, use m and |.
Example:
Within the man page of less, by running man less:
7g
mx
6g
|x
xclip (Linux) or pbcopy (macOS), to copy to clipboard.
cat > file, to save to file, or cat >> file for append mode.
We would get:
less - opposite of more
 
The key things to learn are just two less commands: m (mark), and | (pipe).
Command m (mark)
Followed by any lowercase letter, marks the current position with that letter.
The marker we used above is x, as in step 2, it marked line 7 with x.
Command | (pipe)
| <m> shell-command
<m> represents any mark letter. Pipes a section of the input file to the given shell command.
The section of the file to be piped is between the first line on the current screen and the position marked by the letter.
<m> may also be ^ or $ to indicate beginning or end of file respectively. If <m> is . or <newline>, the current screen is piped.
Using |xpbcopy, we pipe the line-range [7, 6] into pbcopy, as line 6 is currently the first line on the screen, and line 7 is the one we marked as x, and pbcopy is the command to put text into the macOS clipboard.
Alternatively, use xclip on Linux, or even dd of=/path/to/file to save as a file.
Note
The text range is boundary inclusive, so both the beginning and the ending lines of the range, or at least 2 lines are copied.
We marked the range in the backward way, namely from bottom to top, otherwise, less might behave awkwardly, and throw the whole screen through the pipe.
I think I found the solution: it is using tmux. Tmux provides it's own clipboard ( correct me if I am wrong ). From tmux I can enter the copy-mode wherever I am ( in MAN pages, less, console output ) and let me to copy the content.
The current accepted answer is based on setting a mark, navigating one line up, piping the current screen up to that mark into the clipboard.
Copy 1 line into clipboard
Navigate to the line (using 1g to go the first line)
|
Press RETURN
head -1 | clip (or xclip et. al)
head -1 | tr '\n' '\' | clip
Explanation
| pipes the whole screen into the command, so navigate to where we want that to start.
2-3. | <m> shell-command ​if <m> is . or newline, the current screen is piped.
head -1 just the first line
| tr '\n' '\' replace the the carriage return with \ (for pasting to shell)
| clip pipe to clipboard (or /dev/clipboard, xclip et. al)
Short answer: Ctrl+C and Ctrl+V are associated with other actions. For instance Ctrl+C sends an interrupt signal to the foreground process. Usually you need to use Ctrl+Shift+C and Ctrl+Shift+V in order to copy and paste from a terminal.
Long answer: This very good thread from superuser.

How to store grep results in a buffer in Vim?

I want to debug a process, hence I attached strace to the process and redirected the output to a file and then performed the operation. During the process it has created a lot of processes. So here is what I want to do, I want to select all the system calls executed by a process. To do that I used grep command with pattern as pid:
:grep pid %
It shows the result but I am not able to traverse through the result, it promts
Press ENTER or type command to continue
and returns to the file. What I would like to do is store the result in a buffer and then have a look into it and if required save it into a file or else discard it and return to the original file. Is there a way to do this with out exiting from the vim editor? Thanks in advance.
I would like to search with the result and store that in a buffer.
Over the years of reading all kind of logs, I learned this little trick:
:%!grep pattern
It simply replaces the current buffer contents with grep output (so to go back to the original logs you have to simply press u).
You can also use it with other tools:
:%!ack pattern
:%!ag pattern
:%!rg pattern
Note that you can also run these commands on other files then the current one. The following 2 commands would replace the current buffer with results of grepping over the current file (second % character, which would be redundant for grep in this case) and otherfile.txt respectively:
:%!grep pattern %
:%!grep pattern otherfile.txt
For me it's the simplest and the best solution for fast grepping of big files in Vim and I'm pretty surprised no one ever mentioned it.
You can go to older searches, and back easily:
:copen
:colder " goes to older
:cnewer " newer
You can have another search using lvimgrep (uses location window):
:lopen
:lnext
etc...
It also has history:
:lolder
:lnewer
You can read into any buffer:
:r!grep bla **/*.cs
Finally, any command that gives output, can be redirected with the redir command:
:redir >> file
:grep bla **/*.cs
:redir END
See :he redir for the many ways to use it (redirect into registers or variables).
I thought that :grep results were stored by default in the quickfix window.
Try to use :copen after running a grep command. I expect that you'll find your results there.
( :cclose to close the quickfix window)
It is not really a buffer, but as long as you are not starting another search your result list will stay intact.
You can "yank" the content of the quickfix window to a new buffer.
Go into quickfix with :copen
Yank its content with yG
open a new buffer with :new
Paste the content with p
Save it with :w Process1.txt
Repeat and rinse for multiple search/process.
#romainl 's answer on how grep results into separate buffer or split window gives a much cleaner answer than quickfix/location lists.
:vnew | 0r!grep foo #
:tabe | 0r!grep foo #
but Quickfix lists are sort of intended for what you are doing but not the way you are doing it; however, it's tedious to set up unless it's a recurrent task.

How do you do an incremental search across multiple files in VIM?

Vim already does incremental search within the currently open file but can you do
an incremental search across multiple files?
AFAIK this is not possible. However you can start to type a word that is in an opened buffer and hit ctrl-xctrl-n to start searching for such a word in all opened buffers.
from :help grepadd
*:grepa* *:grepadd*
:grepa[dd][!] [arguments]
Just like ":grep", but instead of making a new list of
errors the matches are appended to the current list.
Example: >
:call setqflist([])
:bufdo grepadd! something %
The first command makes a new error list which is
empty. The second command executes "grepadd" for each
listed buffer. Note the use of ! to avoid that
":grepadd" jumps to the first error, which is not
allowed with |:bufdo|.
An example that uses the argument list and avoids
errors for files without matches: >
:silent argdo try
\ | grepadd! something %
\ | catch /E480:/
\ | endtry"

vim -- How to read range of lines from a file into current buffer

I want to read line n1->n2 from file foo.c into the current buffer.
I tried: 147,227r /path/to/foo/foo.c
But I get: "E16: Invalid range", though I am certain that foo.c contains more than 1000 lines.
:r! sed -n 147,227p /path/to/foo/foo.c
You can do it in pure Vimscript, without having to use an external tool like sed:
:put =readfile('/path/to/foo/foo.c')[146:226]
Note that we must decrement one from the line numbers because arrays start from 0 while line numbers start from 1.
Disadvantages: This solution is 7 characters longer than the accepted answer. It will temporarily read the entire file into memory, which could be a concern if that file is huge.
The {range} refers to the destination in the current file, not the range of lines in the source file.
After some experimentation, it seems
:147,227r /path/to/foo/foo.c
means insert the contents of /path/to/foo/foo.c after line 227 in this file. i.e.: it ignores the 147.
Other solutions posted are great for specific line numbers. It's often the case that you want to read from top or bottom of another file. In that case, reading the output of head or tail is very fast. For example -
:r !head -20 xyz.xml
Will read first 20 lines from xyz.xml into current buffer where the cursor is
:r !tail -10 xyz.xml
Will read last 10 lines from xyz.xml into current buffer where the cursor is
The head and tail commands are extremely fast, therefore even combining them can be much faster than other approaches for very large files.
:r !head -700030 xyz.xml| tail -30
Will read line numbers from 700000 to 700030 from file xyz.xml into current buffer
This operation should complete instantly even for fairly large files.
I just had to do this in a code project of mine and did it this way:
In buffer with /path/to/foo/foo.c open:
:147,227w export.txt
In buffer I'm working with:
:r export.txt
Much easier in my book... It requires having both files open, but if I'm importing a set of lines, I usually have them both open anyway. This method is more general and easier to remember for me, especially if I'm trying to export/import a trickier set of lines using g/<search_criteria/:.w >> export.txt or some other more complicated way of selecting lines.
You will need to:
:r /path/to/foo/foo.c
:d 228,$
:d 1,146
Three steps, but it will get it done...
A range permits a command to be applied to a group of lines in the current buffer.
So, the range of read instruction means where to insert the content in the current file, but not the range of file that you want to read.

Resources