Open shell in emacs with a given working directory - emacs

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.

Related

eshell TRAMP find remote file with relative path (or at least less than the full Tramp path)?

I love eshell's TRAMP integration. With it I can do cd /ssh:foo:/etc to
ssh into a remote machine and visit its /etc/ directory. I can also do
find-file motd to open this file in my local emacs. However, what if I need to use sudo to change the file? I know I can give the
full path, like so:
find-file /sudo:foo:/etc/motd
but is there a way to open the file via TRAMPs sudo support, without having to type the full path?
I managed to came up with the following eshell alias that works for me:
alias sff 'find-file "${pwd}/$1"(:s/ssh/sudo/)'
It should be fairly obvious what it does. It prepends the working directory
path, but with the string ssh replaced by sudo. Thus it only works for
remote files accessed over ssh. I rarely edit files using sudo locally, so
that's not a problem for me. However, we can make it work for local files too, at the cost of complexity:
alias sff 'find-file "${pwd}/$1"(:s,^,/sudo::,:s,::/ssh:,:,)'
That is, prepend /sudo:: (which is how to sudo for local files) and
subsequently replace any ocurrence of ::/ssh: with :. (I would have just removed :/ssh:, but eshell's :s/// construct didn't accept an empty
replacement.)
I found an alternative answer that works very well over at EmacsWiki.
Using that you'd still open the file with find-file as usual, but then
invoke M-x sudo-edit-current-file (shown below) to re-open the file as root
using Tramp. I think this is a very elegant solution, because often I
initially just want to look at a file, then later find that I need to edit it.
Here's the function, in case it disappears from the page above:
(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))
(require 'tramp)
(defun sudo-edit-current-file ()
(interactive)
(let ((position (point)))
(find-alternate-file
(if (file-remote-p (buffer-file-name))
(let ((vec (tramp-dissect-file-name (buffer-file-name))))
(tramp-make-tramp-file-name
"sudo"
(tramp-file-name-user vec)
(tramp-file-name-host vec)
(tramp-file-name-localname vec)))
(concat "/sudo:root#localhost:" (buffer-file-name))))
(goto-char position)))

How to open terminal in current directory?

When i use M-x shell to open a new terminal, it will sometimes set the current directory to the file in. But sometimes it won't. So is there a function to always open a new terminal in current directory?
There's the package shell-here available in ELPA: M-x list-packages, look for shell-here, mark for install (i) and execute (x).
An excerpt of the readme:
Open a shell buffer in (or relative to) default-directory,
e.g. whatever directory the current buffer is in. If you have
find-file-in-project installed, you can also move around relative
to the root of the current project.
I use Emacs shell buffers for everything, and shell-here is great
for getting where you need to quickly. The =find-file-in-project=
integration makes it very easy to manage multiple shells and
maintain your path / history / scrollback when switching between
projects.
github home: https://github.com/ieure/shell-here
And I like shell-pop too, to pop up and pop out a shell buffer window with one easily. And there's maybe more in ELPA !
M-x shell will switch to an existing shell if there's already one running, which may be your problem. If you don't mind creating lots of shell buffers, the command below will generate new buffers whenever it can't find one visiting the given directory:
(require 'cl-lib)
(defun shell-at-dir (dir)
"Open a shell at DIR.
If a shell buffer visiting DIR already exists, show that one."
(interactive (list default-directory))
(let ((buf (car (cl-remove-if-not
(lambda (it)
(with-current-buffer it
(and (derived-mode-p 'shell-mode)
(equal default-directory dir))))
(buffer-list)))))
(if buf
(switch-to-buffer buf)
(shell (generate-new-buffer-name "*shell*")))))

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)))

How to have Emacs save file at multiple locations?

Is there an easy way to have emacs save current buffer in two locations?
I could in the 'after-save-hook' programmatically copy the current file to a second location, but writing lisp code for that might take some time.
For those that are curious why I want this:
I want the changes I make to my JSP immediately be deployed in tomcat's webapps/myapp directory.
So everytime I save a JSP file I want it saved in both my current version controlled source location as well as in the directory where my Tomcat application is deployed.
I can't use symlinks because I use a windows machine and the destination location is a directory in Linux box that is exported through Samba.
Something like this should work:
(add-hook 'local-write-file-hooks 'my-save-hook)
(defun my-save-hook ()
"write the file in two places"
(let ((orig (buffer-file-name)))
(write-file (concat "/some/other/path" (file-name-nondirectory orig)) nil)
(write-file orig nil)))
For more on local-write-file-hooks see this answer.
Obviously customize the file name created in the first call to 'write-file.
Given the problem you are trying to solve is to deploy changes immediately, I would suggest writing a script (in your case a batch file) that invokes rsync with the appropriate options. You could either run this in the after-save-hook (which is probably overkill) or assign a hotkey to run it for you when you have made a set of changes that you want to test. Something like:
(global-set-key 'f11 (shell-command "c:/dev/deploy_to_test.bat"))
where the script would look like this:
rsync -avz --del c:/dev/mywebapp z:/srv/tomcat/mywebapp
This is probably better than saving the same file in multiple places, as it ensures the deployment directory always matches what you have in your source repository.
Probably a more general solution is a hook similar to this Gist: https://gist.github.com/howardabrams/67d60458858f407a13bd :
(defun ha/folder-action-save-hook ()
"A file save hook that will look for a script in the same directory, called .on-save. It will then execute that script asynchronously."
(let* ((filename (buffer-file-name))
(dir (file-name-directory filename))
(script (concat dir ".on-save"))
(cmd (concat script " " filename)))
(write-file filename nil)
(when (file-exists-p script)
(async-shell-command cmd))))
(add-hook 'local-write-file-hooks 'ha/folder-action-save-hook)
Essentially, on any file save, if the directory where the file is being saved has an executable script called .on-save, it will execute that script with the name of the file being saved.
This allows you to specify an rsync command (or one or more other commands) on a per-directory basis. Granted, this could be expanded to walk up a directory tree looking for this sort of pattern.

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)