Emacs use variable if set in grep-find - emacs

I'm looking for a way to always launch my grep-find from a specific location if a variable is set. I'm thinking something like:
Launch emacs from /home/user/mycode/
Open file(s) in e.g. /home/user/mycode/a/b/c/
Run grep-find from /home/user/mycode/
I know that it works if I first open the /home/user/mycode/ directory and then runs grep-find but I want this to be stored as a variable instead. (If the variable is not set it would be fine to simply do the search from the current location)
My grep-find looks like this:
(setq grep-find-command "find . '(' -type f ')' -print0 | xargs -0 -e grep -n -e ")

It seems to me that you would benefit from projectile.
It does just what you need. It has a recursive grep command that it launches from parent directory (it can use also git-grep or ag, a lot faster than grep), and it offers a lot more. You can work with multiple projects at once (projectile is smart that way).
You can look at projectile here: https://github.com/bbatsov/projectile
It is available via MELPA
If you do not want to use it (or similar alternatives), I would advise wrapping your recursive grep command in a custom function like this:
(defun rgrep-at-repo-base (...)
(interactive)
(with-temp-buffer
(while (and (not (file-exists-p ".git/"))
(not (equal "/" default-directory)))
(cd ".."))
(rgrep ...)))
Where you should change the logic for finding your code base from current directory (example works for git repos) and set the arguments you need (if any)

Related

How can I use M-x rgrep with the git grep command in Emacs?

I want to be able to use the normal M-x rgrep workflow (entering a path, a pattern and displaying the linked results in a *grep* buffer) but using git grep instead of the normal find command:
find . -type f -exec grep -nH -e {} +
I tried directly setting the grep-find-command variable:
(setq grep-find-command "git grep")
and using grep-apply-setting
(grep-apply-setting 'grep-find-command "git grep")
but neither seems to work. When I run M-x rgrep it just uses the same find command as before.
In fact, I'm pretty sure now that rgrep doesn't even use the grep-find-command variable, but I can't figure out where it's command is stored.
What about M-x vc-git-grep (C-x v f). Doesn't that do what you need?
It prompts you for:
search pattern (default: token at point, or region)
filename pattern (default: current file suffix)
base search directory (default, current dir)
Works nicely for me.
Turns out the relevant variable is actually grep-find-template. This takes a command with a few additional parameters:
<D> for the base directory
<X> for the find options to restrict directory list
<F> for the find options to limit the files matched
<C> for the place to put -i if the search is case-insensitive
<R> for the regular expression to search for
The default template looks like this:
find . <X> -type f <F> -exec grep <C> -nH -e <R> {} +
To make the command work with git grep, I had to pass in a few options to make sure git doesn't use a pager and outputs things in the right format. I also ignored a few of the template options because git grep already restricts the files searched in a natural way. However, it probably makes sense to add them back in somehow.
My new value for grep-find-template is
git --no-pager grep --no-color --line-number <C> <R>
After some cursory testing, it seems to work.
Note that you should set this variable using grep-apply-setting rather than modifying it directly:
(grep-apply-setting 'grep-find-template "git --no-pager grep --no-color --line-number <C> <R>")
Since I don't use two of the inputs to rgrep, I wrote my own git-grep command which temporarily stashes the old grep-find-template and replaces it with mine. This feels a bit hacky, but also seems to work.
(defcustom git-grep-command "git --no-pager grep --no-color --line-number <C> <R>"
"The command to run with M-x git-grep.")
(defun git-grep (regexp)
"Search for the given regexp using `git grep' in the current directory."
(interactive "sRegexp: ")
(unless (boundp 'grep-find-template) (grep-compute-defaults))
(let ((old-command grep-find-template))
(grep-apply-setting 'grep-find-template git-grep-command)
(rgrep regexp "*" "")
(grep-apply-setting 'grep-find-template old-command)))
With Emacs for Windows and Git Bash make sure PATH finds git.exe or vc-git-grep won't work:
(let ((dir "C:/Program Files/Tools/Git/bin"))
(setenv "PATH" (concat (getenv "PATH") ";" dir))
(setq exec-path (append exec-path '(dir))))
exec-path is not enough... resaons are explained here: Using git with emacs
Since vc-git-grep uses the directory of the buffer from which you run the function I also found a wrapper convenient:
(global-set-key [(control f8)]
(lambda() (interactive)
(with-current-buffer ROOT (call-interactively #'vc-git-grep))))
Here ROOT is a buffer (or function that evaluates a buffer) from whose directory the search begins.

ansi-term won't find file under current directory? [duplicate]

This question already has answers here:
How can I have term.el (ansi-term) track directories if using anyhting other than bash
(2 answers)
Closed 8 years ago.
It's a very strange problem. I think it must caused my incorrect configuration of ansi-term, but i still can't find out where it is.
The issue is: when i in ansi-term and press M-x find-file, the prompt isn't current directory but the path i entered in my previous find file action. So when i change directory, it still display the same directory. So i have to enter the current directory every time. But it works very well in M-x shell and M-x eshell
Does the same thing happen when you start Emacs without your init file, i.e., emacs -Q? If so, that's the designed behavior or (especially if you use a development snapshot) perhaps an Emacs bug.
If not, then bisect your init file recursively to find out which part of it causes this behavior. To do that, use, e.g., command comment-region (see prefix arg in doc) to comment and uncomment a block of text. Comment out 1/2 of your init file, then 3/4, then 7/8,...,
each time testing whether the uncommented portion causes or removes the problematic behavior. You will very quickly identify what causes the behavior.
Because the path of emacs is different from that of term, it can only be changed by use the emacs command "cd".
So to solve this problem, I add the following code to my emacs configure file. The method is
find the pid of current term
find current working directory(cwd) of this pid.
I use multi-term, I think the method will be similar on ansi-term.
(defadvice term-send-input (after update-cwd)
(let* ((pid (process-id (get-buffer-process (current-buffer))))
(cwd (shell-command-to-string
(format "lsof -p %d -Fn | awk 'NR==2{print}' | sed \"s/n\\//\\//\" | tr -d '\n'" pid))))
(cd cwd)
(message (concat "change emacs path to: " cwd))))
(ad-activate 'term-send-input)
Then you can bound the key of term-send-input to <enter>. When you press <enter> in term, the emacs will change to the same path with the current path of term.
BTW, I use Mac Os. If you are on Linux, you can use the following code to find cwd.
(cwd (file-truename (format "/proc/%d/cwd" pid)))

Open shell in emacs with a given working directory

I want to have a make-shells command in emacs that will open a number of emacs-shell buffers, each with its own working directory. The idea is that for each project I'm working on, I have a shell that starts out in that project's directory, so I can easily switch between them.
Currently I have this code:
(defun shell-dir (name dir)
(interactive "sShell name: \nDDirectory: ")
(shell name)
(switch-to-buffer name)
(comint-send-string (current-buffer) (concat "cd " dir "\r"))
(sleep-for 0 10)
(dirs))
(defun make-shells ()
(interactive)
(shell-dir "project1" "~/proj/project1")
(shell-dir "project2" "~/proj/project2")
(shell-dir "project3" "~/proj/project3")
(delete-window))
This is pretty ugly, though, and half the time (dirs) doesn't pick up the correct path, so tab completion breaks until I re-run it manually. Is there a built-in way to set the current working directory of the emacs shell? Or would something like CEDET (plus less reliance on the shell vs. emacs modes) be a much better solution to this?
I experienced similar problems with the current directory tracking provided by Emacs, so I wrote one which solves the problem once and forever.
Check it out here.
The short version of what it does is that you modify your shell prompt to include a full path to the current directory (only when running inside Emacs), and the Emacs shell buffer will use that.
This means you never have to do M-x dirs again.
There's also the package dirtrack (shipped with Emacs) which does the same thing.
I like my version better because it removes the path from the prompt. I don't want to see the entire path in my prompt as my current directory is often very long.
Once you use one of the above two solutions, you can simplify your shell-dir routine to be:
(defun shell-dir (name dir)
(interactive "sShell name: \nDDirectory: ")
(let ((default-directory dir))
(shell name)))
One more answer... I found there was a way (on Linux) to make Emacs figure out the current directory properly, by using the /proc filesystem.
http://www.emacswiki.org/emacs/ShellDirtrackByProcfs
That way, you just have to start up the shell in whatever directory and Emacs will automatically figure it out and get the tab-completion etc. right.

Can I use ediff if I have a file and a diff, rather than two versions of the same file?

With tf.exe diff , I can get a diff.
Can I use this with ediff to visualize the diff in emacs?
I'm under the impression that ediff normally takes 2 or 3 files. I just have the one file, and a diff.
An option you might get to work is to use
M-x ediff-patch-buffer
It will prompt you for the patch file (or buffer if you have it open already), and the buffer to be patched. It then will march you through the differences.
Because the diff shows changes from the repository version to the current version, the patch is wrong direction. I'd write a command that generated the proper diff and use that - if you really want to use a diff.
Personally, I'd probably try to plug some code in to get 'ediff-revision (which I have bound to C-x v -) to get it to work.
Or just write some lisp which follows this pseudo code (since I don't have tf to do actual testing):
(defun ediff-tf-file-with-previous-version (file &optional version)
"DTRT and call ediff with the previous version of this file"
(interactive)
(ediff-files (progn
(unless version
(setq version (<parse-to-get-version> (shell-command (concat "tf.exe properties " file)))))
(shell-command (concat "tf.exe view " file (<munge-itemspec-version> version) " > " file ".older")))
file))
thanks R Berg for the fix
It looks as though someone has written a rudimentary Team Foundation mode, which you can grab from the wiki page here. It doesn't look like it has plugged anything into ediff though.
Here's a script I wrote that does it (I think!):
#!/bin/bash
# quit on error
set -e
# create a temporary file for the patched output
# note that the mkfifo command is optional - it will save disc space
patched_file=/tmp/diff_$RANDOM
mkfifo $patched_file
patch -o $patched_file "$1" "$2" &
vimdiff "$1" $patched_file
rm $patched_file

Dired copy asynchronously

Is there a way to modify/tell dired to copy files asynchronously? If you mark multiple files in dired and then use 'C' to copy them, emacs locks up until every file is copied. I instead want this copy to be started, and for me to continue editing as it goes on in the background. Is there a way to get this behaviour?
EDIT: Actually, C calls 'dired-do-copy' in dired-aux, not in dired itself. Sorry for any confusion.
I think emacs is mostly limited to a single thread - so this may not be directly possible through standard dired commands such as 'C' copy.
However, there is a dired command "dired-do-shell-command" which calls out to a shell to do the work in the background. If you select the files you want to copy and then use key '!' (this runs dired-do-shell-command) then type 'cp ? [destination]' (possibly can use 'copy' if you are on windows). I haven't tested this - so see help on "dired-do-shell-command" for full details.
See also the Emacs function dired-do-async-shell-command.
For an even more generic solution see https://github.com/jwiegley/emacs-async with which you also can evaluate arbitrary Emacs Lisp code through call to a separate Emacs process (which of course incurs a bit of extra latency). More specifically regard file operations see the file dired-async.el in this repo.
Also note that there is work on threading in Emacs under the working name Concurrent Emacs but it's not there yet. See http://www.emacswiki.org/emacs/ConcurrentEmacs for details.
I found this answer quite helpful: https://emacs.stackexchange.com/a/13802/10761. Reading that answer shows that you can make it so that dired will copy with the scp method instead of the ssh method (the latter initially encodes the file with gzip and that can be quite slow). The scp method will only copy with the scp program when the file is larger than tramp-copy-size-limit (which is 10240 by default). Using this scp method in conjunction with dired-async-mode is very nice, as it will not only copy quickly with scp, but it will also do it asynchronously and out of your way.
Also, I think this is useful: https://oremacs.com/2016/02/24/dired-rsync/. It provides this snippet of code to use rsync to copy files in dired:
;;;###autoload
(defun ora-dired-rsync (dest)
(interactive
(list
(expand-file-name
(read-file-name
"Rsync to:"
(dired-dwim-target-directory)))))
;; store all selected files into "files" list
(let ((files (dired-get-marked-files
nil current-prefix-arg))
;; the rsync command
(tmtxt/rsync-command
"rsync -arvz --progress "))
;; add all selected file names as arguments
;; to the rsync command
(dolist (file files)
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument file)
" ")))
;; append the destination
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument dest)))
;; run the async shell command
(async-shell-command tmtxt/rsync-command "*rsync*")
;; finally, switch to that window
(other-window 1)))
(define-key dired-mode-map "Y" 'ora-dired-rsync)