Vim open a file found through grep - bash

So I'm browsing a file in vim. I realize the function I am looking for is another file. So I do :! ack-grep "function tracking" ../ to look for it, and I see I need to examine file "../../tracking/api/ etc etc" some really long file name. what I'm doing now is trying to remember this file, type fg to get back into vim, and then :e "that really long file name". I'm wondering if there's an easier way to do this?
For instance, can I do something like vim -addtab <some file> from the command line once I've used ack-grep to find what I'm looking for, and then when I do fg to get back to vim, the file will be open in one a tab?
Awesome, lots of suggestions. I'll try them all out and in comments as I do. Then I'll pick what worked best for me once I've tried them all.
So this is the function that I settled on:
function! s:NewTabGrep(...)
let args=split(a:1)
if len(args) == 2
let dir=args[1]
else
let dir='.'
endif
echom args[0] . " " . shellescape(dir)
tabnew | execute "r ! ack-grep ". shellescape(args[0]) ." ". shellescape(dir)
endfunction
com! -nargs=? GrepTab call s:NewTabGrep('<args>')
This performs the search, and opens the results in a new vim tab. Then I can use CtrlP to open whichever file seems most interesting. Thanks to Merlin2011 for inspiration.

You can do :r ! ack-grep "function tracking" ../ to pull the output directly into the vim buffer, and then use gf to navigate to the file under the cursor.
If you want to open the file in a new tab instead, you can do Control-W, gf in normal mode.
Update: If you want the output of the command to appear in a new tab, you can do the following, which opens a new tab, and then immediately pulls the output of the command into it.
:tabnew | r!ack-grep "function tracking" ../

:vimgrep does this by default! it's the best! in fact, it's so cool, that you can even use :vim to invoke it hahaha
e.g. search all python files for "function tracking" (works best if you keep your vim working directory in your source code's root folder)
:vim /function tracking/ **/*.py
^ this will go to the first search result, and then open the quickfix window with a list of search results. I use a binding like nmap <silent> \` :QFix<CR> to quickly toggle it open and off. See... http://vim.wikia.com/wiki/Toggle_to_open_or_close_the_quickfix_window
--
however, there's a better way for navigating to the function: using CTRL-] which does an instant jump to the definition! Combined with CTRL-O and CTRL-I (jump backwards/forwards), you have an unstoppable move-around files-efficiently combo which will make you forget why you ever used IDE's =D
I googled this wiki page: http://vim.wikia.com/wiki/Browsing_programs_with_tags. One other thing you have to do is download Exuberant Ctags and build a tags file in your working directory: ctags -R ...then you're all set! (note that on Macs/BSD, it has its own ctags which wont work, you gotta use the exuberant one)

Vim has the :grep command that runs grep and stores the results into the quickfix list(you can also use :lgrep to load them to the location list). :grep also jumps to the first result, but if you don't want that you can use :grep! to just load the results to the quickfix list.
Once the results are loaded, you can use :copen to open the quickfix list(or :lopen to open the location list) and navigate between the results, or use :cfirst, :cnext, :cprevious and :clast(or :lnext, :lfirst, :lprevious and :llast) for navigation.
So, if you want to open the result in a new file, you have three options:
:grep! "function tracking" ../**/*, :copen to open the quickfix list, put cursor on the first result, ctrl-W Enter to open the result in a new window, ctrl-W T to move the window to a new tab.
:grep! "function tracking" ../**/*, :tabnew to open a new tab, :cfirst to jump to first result.
:tabnew to open a new tab, :grep "function tracking" ../**/* to perform the search and open the first result automatically.
You might find it easier to just use the quickfix list for navigation instead of opening new tabs.
:grep uses grep by default, but you can set the grepprg option to make it use ack-grep instead(you might also need to set grepformat to recognize the output). You can also use :vimgrep to do a search with Vim's regular expression without using an external program, but it will be slower.

:exec 'tab split '.fnameescape(system('ack-grep "function tracking" ../'))
should do the trick. (Make it into a function!)

I recommend you to use the ack plugin., Note that if you have vim-dispatch installed, you can search with ack.vim asynchronously(You need to write: let g:ack_use_dispatch=1 in your vimrc file). Also I recommend ag to replace ack since it's really fast.

Related

How do I edit current shell command without executing it?

There seems to be quite a lot of information on how to edit and execute a command using your editor using "edit-and-execute-command (C-x C-e)", but what I would like to achieve is take the current shell command, apply certain filtering (using a script) and then return it to prompt for further approval/manual changes before execution. Is this possible with bash?
Latest update based on my experience
The part 0"+y$dd in the following mapping is really something that you should carefully think about and tailor it to your taste/workflow/experience.
For instance, very frequently I've found myself ending up with multiple lines in the buffer, where I only want to execute the one the cursor is on; in this case I can use 0"+y$dd:%d<CR> instead of 0"+y$dd.
And this is just one of the possible scenarios.
Final answer for those who like vim
Set vim as your EDITOR/VISUAL, so that when editing a command line, you will use vim to edit it.
Put au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR> in your ~/.vimrc file to map Leaderd (which you will rarely use when editing a command) to the action "delete the current line into the + register without the trailing EOL".
you can use either the + or the * register in the mapping above; the ways to paste into the terminal will likely differ; you need the +clipboard option for these registers to be available.
When finished editing a command in the vim editor, hit EscapeLeaderd.
Paste the clipboard into the terminal (this is terminal-dependent).
Original answer
I often need to do the same, and I do it as follows. (I normally use the set -o vi in bash, so points 1 and 2 in the following are different if you use set -o emacs, the default; based on your question it looks like points 1 and 2 are unified in Ctrl+x followed by Ctrl+e, which is harder to type, imho.)
hit Escape to be in normal mode,
hit v to enter the editor to edit the command,
edit the command as I like,
(This is where you ask the question.)
hit Escape0"+y$dd:wq,
Note: 0"+y$, not simply "+yy, as the latter would copy the newline too, and this would result in executing the command upon pasting it in the command line,
paste the clipboard on the command line
how to do this depends on the terminal you are using, I guess; I hit Ctrl+Alt+v in URxvt.
proceed to approval/manual edit.
Clearly this is just a workaround, consisting in copying the edited command into the clipboard before deleting the whole command, so that nothing gets executed upon exiting the editor; however it's the best I can get for myself.
Update
As my EDITOR (and VISUAL) is equal to vim, when I edit the command, I edit it in vim.
In this respect, I have noticed that the buffer is named /tmp/bash-fc.random, where random is a 6-characters alphanumeric random string.
This gives space to a lot of possiblities, if you use vim as your editor, as you can define some mapping in your .vimrc to execute the whole sequence Escape0"+y$dd:wq. For instance, one command that you'd rarely use when editing a command line is Leaderd; therefore you can put the following mapping in your .vimrc file
au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR>
so that step 4 in the above recipe becomes
hit EscapeLeaderd
It's not possible to do that in Bash/readline but it's possible in zsh
using edit-command-line command:
darkstar% autoload edit-command-line; zle -N edit-command-line
darkstar% bindkey "^X^E" edit-command-line
Now press Control-x Control-e to open your editor, edit line, leave the editor - you will see the updated command line but it will not be executed automatically.
Now that I think about it, maybe a variation of what #kenorb suggested in a comment is the best workaround (as it seems no solution exists), if we want to stick to bash.
What you can do is prepend a # (the comment character in bash) to the command, rather than echo. Then when you exit the editor, the command will be ineffective, and you will only have to press arrow up (or k, if you use set -o vi), remove the # and confirming.
Note that this strategy adds just a few keystrokes, so it can be fairly efficient, depending on your typing level.
These pieces might get you closer:
a) replace the the normal binding for newline newline (ctrl-M)
bind -x '"\C-M":date"
b) grab the current line from the history using !#
replace date with whatever script you want.
c) edit manually
d) if necessary, somehow tie in !!:p which prints the new command to the command line but does not execute it, thus letting you manually edit it.
e) using ctrl-J submit edited command rather than a newline
or they might not ....
There is an option in bash to modify command from history without executing it. I'm not sure it it's possible to use script for this, doesn't seem to be likely. Although, you can make modifications using history modifiers.
Enable option histverify to prevent execution of modified command
Use chain of modifiers to change last command
Use "!!" to put your result to command line for final edit
Here is how it looks:
$ shopt -s histverify
$ ls *.sh
script1.sh script2.sh script3.sh script-return.sh
$ !!:s/*/script1/:p
ls script1.sh
$ !!:s/1/2/:p
ls script2.sh
$ !!
$ ls script2.sh
script2.sh
I'd like to point you to the Composure framework for Bash (I'm not affiliated with it): https://github.com/erichs/composure
It provides draft and revise functions that sound like they could help with what you're trying to do. Here's a (long) quote from the project's readme file:
Composure helps by letting you quickly draft simple shell functions,
breaking down your long pipe filters and complex commands into
readable and reusable chunks.
Draft first, ask questions later
Once you've crafted your gem of a command, don't throw it away! Use
draft () and give it a good name. This stores your last command as a
function you can reuse later. Think of it like a rough draft.
$ cat servers.txt
bashful: up
doc: down
up-arrow
$ cat servers.txt | grep down
doc: down
$ draft finddown
$ finddown | mail -s "down server(s)" admin#here.com
Revise, revise, revise!
Now that you've got a minimal shell function, you may want to make it
better through refactoring and revision. Use the revise () command
to revise your shell function in your favorite editor.
generalize functions with input parameters
add or remove functionality
add supporting metadata for documentation
$ revise finddown
finddown ()
{
about finds servers marked 'down' in text file
group admin
cat $1 | grep down
}
$ finddown servers.txt
doc: down
It does not seem possible with a keyboard shortcut, at least:
$ bind -P | grep -e command -e edit
complete-command can be found on "\e!".
edit-and-execute-command can be found on "\C-x\C-e".
emacs-editing-mode is not bound to any keys
possible-command-completions can be found on "\C-x!".
vi-editing-mode is not bound to any keys
This can be done in native bash using readline specifically READLINE_LINE and READLINE_POINT variables. I use this functionality all the time though not through vim, you would need to get the value of $selected from your vim command and if not empty it takes your original line + your input and replaces your original line with the combination without executing. output as a variable
_main() {
selected="$(__coms_select__ "$#")"
origonal_text=$READLINE_LINE READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
bind -m emacs-standard -x '"\C-e": _main '
bind -m vi-command -x '"\C-e": _main '
bind -m vi-insert -x '"\C-e": _main '
Edit
Just remembered these two utilities that will let you do this as well.
Vipe allows you to run your editor in the middle of a unix pipeline and edit the data that is being piped between programs.
vp, up, vipe, Neomux (upgrade of nvim terminal) you can do some pretty neat throwing buffers between the terminal and split window.
and Athame (full vim on the command line)
https://github.com/ardagnir/athame
careful with that one though plugins work on the cli and it can get funky if you got tons of plugins

Vim grep causes characters to temporarily disappear

I am working through Learn Vimscript the Hard Way" by Steve Losh. In chapter 32 we build a grep command that puts results in the quickfix window. It looks like this:
:nnoremap <leader>g :silent :exe "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>:copen<cr>
It basically works, but when I run it, it will cause characters to become (temporarily) invisible. If I close and re-open the file, the characters come back. Also, if I search for the invisible characters, they appear.
For example, if I run the command with my cursor on ``thedude'' on a block of text like this:
thedude#abides.org
print foo(bar)
print foo(bar)
The two ``print foo(bar)'' lines become invisible. Can anybody guess why this might be happening? I'm using the system default Vim v7.3 on OSX Mountain Lion.
This sometimes occurs when using an external command with the :silent command. You can read about it at :help :silent in the second to last paragraph. There it tells you that you can work around the problem by redrawing the screen after you execute the command or using CTRL-L to clear it manually.
:nnoremap (yadda yadda):copen<cr>:redr!<cr>

BASH file name completion

In BASH I can normally start typing a file name, then type TAB and it will give me some suggestions.
I have a list of files that look like this:
20130519114000_add_hstore.rb
20130615125517_create_sites.rb
20130616112833_create_delayed_jobs.rb
....
Is there a way I could type something like "*delayed" and then TAB (or something) to find "20130616112833_create_delayed_jobs.rb".
At the moment, sifting through those numbers at the start of the filenames is too much cognitive load :)
Yes. The details can vary depending on your keyboard, and your Readline command bindings, and how you're logged into the system, and so on, but typically you can do this by typing *delayed, and then hitting Alt+g instead of TAB. (Search for glob-complete-word in ยง8.4.8 "Some Miscellaneous [Readline] Commands" in The Bash Reference Manual.)
You can use either:
ls *delayed*
or
ls | grep delayed
to filter list of files to what you want.

Bash Ctrl + R use current text

I use the reverse-i-search in Bash a lot. But I always type in the start of the command I'm about to use before I realise that I need to search for it.
Is there a way to make Ctrl + R use the input text, so that I don't have to type it again?
By input text, I mean text that I have typed in to the terminal before pressing Enter.
An example:
cd ~/some/folder/
cd some/subfolder/
I am about to go to a subfolder with cd. Before pressing Enter, I will type "cd some/subfolder". That is the input text.
You can use the CTRL-aryr command.
It looks worse than it is: for a half typed complex command it's quite helpful.
That's a compound command:
CTRL-a: home
-r: open reverse incremental history search and copy the text after the cursor position
-y: paste the text
-r: look for matching commands
It appears you can not do this. That is CTRL-R reads directly from the keyboard (at least on Linux) so you can't even paste into the buffer.
The best solution I can recommend is that you consider using Emacs Shell mode which will replace Bash's input with Emacs buffers.

How do I select the line in bash terminal

Is it possible to select part of a string in the bash terminal and delete it at once, rather than navigating to a point in the command and backspacing it all??
thanks!
I'm not 100% sure I understand your question.
If you are at the interactive command line:
ctrl-u: Deletes everything to the left of your cursor
ctrl-k: Deletes everything to the right of your cursor
I'm using emacs bindings and my favourite command line shortcuts, which were not included in the previously linked tutorial, are the following:
^W - delete last word
meta-b - move cursor back one word
^R - find a previously used command
!$ - last attribute of the last command
!! - last command
You can also manipulate the history with regexps, although that could quickly get quite messy. See "man zshexpn" for reference, mostly the same regexp syntax works for bash also.
Example:
1) If you execute the following command:
echo first second third fourth fifth
2) Then you could execute the same command and remove "first" by:
!!:s/first//
If this was not what you were after, please clarify on your question! :)
See this. Alternatively, set -o vi to have vim-like key bindings.
Note that the list given in the link is not full. You might read man bash for a good reference. Usually, ALT-x might be replaced by ESC, x if you run bash inside a terminal that uses Alt-letter shortcuts for windowing system.

Resources