Ultimately I want the call the elisp (find-file "/abs/olute/file.name") when (file-remote-p default-directory) is non-nil and have the remote host applied to the absolute file name in the find-file call.
The actual use-case: I have a shell-mode session on a remote host where default-directory is of the form "/meth:user#host:/abs/path/". I have added a command in shell mode that opens a file in a new buffer. If I send a relative file name the remote file is opened properly; if I send an absolute file path (starts with "/" or "~") it is evaluated relative to my local context rather than the remote.
I can wrap the function so that the remote info is tacked in front of the absolute file path before it's opened but I was wondering if there is a TRAMP function or variable that I can use to force the use of the remote host for absolute file paths. Note, again, that relative paths are handled appropriately by applying the remote host and path from default-directory correctly.
There is not such a Tramp function. (find-file "/abs/olute/file.name") knows, that the file name is a local one, and it doesn't call Tramp.
So you must implement this on your own, indeed.
Here's what I use:
(defun expand-file-name-remote (file)
"A tramp-friendly version of expand-file-name. Expand file
relative to the remote part of default-directory."
(let ((file (expand-file-name file))
(dir-remote (file-remote-p default-directory)))
(if (file-remote-p file)
;; if file is already remote, return it
file
;; otherwise prepend the remote part (if any) of default-directory
(concat dir-remote file)
)))
Related
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)))
Using Emacs with ido mode enabled on Windows, Emacs tries to save a history file .ido.last when exiting. The file is located in C:/.ido.last, but it fails with a permission denied message. This is strange since I actually have access to that folder. However:
Is there a command to change the directory where the .ido.last file gets saved?
Short answer: (setq ido-save-directory-list-file "/some/file/name").
Long answer:
I keep all the little files that remember Emacs's state in a single directory under the user-emacs-directory. I'm not sure what this is on Windows, but I think it's C:\Users\<username>\Application Data\.emacs.d\. On Unix, it's ~/.emacs.d/. The variable user-emacs-directory should be defined by Emacs, no need to set it.
(setq emacs-persistence-directory (concat user-emacs-directory "persistence/"))
(unless (file-exists-p emacs-persistence-directory)
(make-directory emacs-persistence-directory t))
(setq ido-save-directory-list-file (concat emacs-persistence-directory
"ido-last"))
You may want to look at the no-littering package, which sets better default locations for files like this.
I looked in various places and finally came up with the following setup for 'auto-save' mode in Emacs:
(defvar my-auto-save-folder (concat "~/.emacs.d/auto-save")); folder for auto-saves
(setq auto-save-list-file-prefix "~/.emacs.d/auto-save/.saves-"); set prefix for auto-saves
(setq auto-save-file-name-transforms `((".*", my-auto-save-folder t))); location for all auto-save files
(setq tramp-auto-save-directory my-auto-save-folder); auto-save tramp files in local directory
After having this setup for some weeks, I visited ~/.emacs.d and found that the folder ~/.emacs.d/auto-save is empty, while ~/.emacs.d contained two auto-save files of the form #!home!<myusername>!<myfolder>!<myfile>. Why are the auto-save files not stored in ~/.emacs.d/auto-save? [the folder auto-save has rights 775, .emacs.d 700]
Your error is in:
(defvar my-auto-save-folder (concat "~/.emacs.d/auto-save")); folder for auto-saves
(the call to concat with a single argument is pointless, incidentally).
If the optional element UNIQUIFY is non-nil, the auto-save file name is
constructed by taking the directory part of the replaced file-name,
concatenated with the buffer file name with all directory separators
changed to `!' to prevent clashes.
Emacs identifies directory names by a trailing /, which means that "the directory part" of the path you've used is "~/.emacs.d/".
You want:
(defvar my-auto-save-folder "~/.emacs.d/auto-save/"); folder for auto-saves
The positioning of the comma in the following is also strange (although apparently it still works):
`((".*", my-auto-save-folder t)))
That should really be:
`((".*" ,my-auto-save-folder t)))
This is what i have in my .emacs, which works well for me:
(add-to-list 'auto-save-file-name-transforms
(list "\\(.+/\\)*\\(.*?\\)" (expand-file-name "\\2" my-auto-save-folder))
t)
I am using emacs on windows. I would like to know how to change the default "Find File:" path in emacs i.e. When we press "C-x C-f" I want the default file path to point to my Documents directory and not to "c:\emacs-**\bin/".
In a buffer that is visiting a file, the default path you see when you visit a new file (C-x C-f) is the directory that contains the current buffer's file.
In order to override the value "c:\emacs-**\bin/" with something more sensible, set the default-directory variable in your .emacs file:
(setq default-directory "/path/to/documents/directory/")
Note that the path value should end with a slash (or backslash on Windows).
However, you might also want to consider changing the value of your HOME environment variable, as by default, this is what the variable default-directory points at at startup (unless set to some other value like shown above).
This shall do it:
(global-set-key (kbd "C-x C-f") (lambda () (interactive)
(cd "somePathHere")
(call-interactively 'find-file)))
(replace somePathHere with the path to your documents directory)
Variable 'default-directory' is the "current" directory (for the current buffer). Command 'cd' changes directories, and visiting any file or directory (e.g. with Dired) changed the 'default-directory' for that buffer.
You can start Emacs in a given directory, by passing that directory on the command line. You can use a Windows shortcut to do this too. And you can have the shortcut visit that directory in Dired.
Example shortcut info:
Target: C:\Emacs\bin\runemacs.exe "C:\my\favorite\folder"
Start in: C:\my\favorite\folder
You have to redefine the environment variable HOME to your new default directory.
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.