Open the same file with emacs from two user accounts - emacs

I have two user accounts on my Mac.
I would like to open the same text file on both accounts with emacs.
I can put the file in a directory where both users have read and write access.
When I add text to one process I would like it to show up in the other buffer and vice verse automatically.
I have tried combinations of auto-save-mode and auto-revert-mode to try to auto save an auto revert, but that does not seem to work quite right.
Is there some normal way to do this with emacs?

I think you should learn about collaborative editing. There is a page on the emacs wiki about it link

Not a full answer. Just some thoughts. Nevertheless, this is formulated as answer since I want to supplement it with elisp code and maybe refine it later on. This is not possible with comments.
I assume that you are actually working with two emacsen at the two accounts. Furthermore, I assume that the problem with auto-revert-buffer is the timing, i.e. that auto-revert-buffer works with polling and not on demand.
If you want to update on demand you have to setup both emacsen as servers and give them the possibility to communicate bidirectionally.
You could do something like that via emacsclient with the option -e over ssh.
If you are in a private network you can also let the emacs servers directly communicate.
I had no luck with emacsclient and the default server-setup as sockets since emacsclient checks the ownership for the sockets. (Note: First, I used emacsclient for testing. Afterwards, I verified that the communication also works between two emacsen.)
But the code below shows a possibility with the emacs server communicating over tcp/ip.
Both emacsen should put their server files into the same directory. Naturally, you need to name the servers differently. Then you can communicate via the command server-eval-at.
Note, this stuff is security relevant and should only be used on a secure private network.
The below elisp code is not like a package but more like a collection of useful commands.
Nevertheless, on one server you can use the commands as they are. On the other one you need small modifications. E.g.: changing server-name.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Communication over tcp:
(setq server-use-tcp t)
(setq server-auth-dir "/tmp/eserv/")
(if (file-directory-p server-auth-dir)
(progn
(unless (file-accessible-directory-p server-auth-dir)
(error "Cannot access server-auth-dir"))
(unless (file-writable-p server-auth-dir)
(error "Cannot write to server-auth-dir"))
)
(mkdir server-auth-dir t)
(unless (shell-command (concat "chgrp users \"" server-auth-dir "\""))
(error "Cannot change group of server file directory to users."))
(chmod server-auth-dir (file-modes-symbolic-to-number "g+xr" (file-modes server-auth-dir))))
;;
(defadvice server-ensure-safe-dir (around unsafe activate)
"We are on a private network and do not fear intrusion.
Furthermore, the directory is already set up.
This is a SECURITY HOLE if you do not know what you are doing!"
(let ((dir (ad-get-arg 0)))
(unless (file-exists-p dir)
(error "File %s does not exist." dir))
(unless (file-directory-p dir)
(error "File %s is not a directory." dir))))
(server-force-delete)
(server-start)
(defvar server-file nil
"Full name of server file.")
(setq server-file (concat server-auth-dir server-name))
(unless (shell-command (concat "chgrp users \"" server-file "\""))
(error "Cannot change group of server file to users."))
(chmod server-file (file-modes-symbolic-to-number "g+r" (file-modes server-file)))
Afterwards, I discovered that communication of two emacsen is also possible via sockets. One only needs to get around the security and to soft-link the server sockets in the auth-directories.
So, you have the choice.

Related

How to get emacs to automatically load and save desktop from initial directory?

I usually have 3-4 different projects that I work on at once. So I am trying to figure out how to get emacs to load the desktop from the folder that I open emacs from and also save to that file when I exit from that emacs instance.
All of the docs I have seen either describe how to get emacs to automatically open and save from a default location (which makes multiple desktops impossible), or to manually load and save the desktop to a specific directory (which I am doing now).
Thanks!
Put this to your .emacs:
(setq your-own-path default-directory)
(if (file-exists-p
(concat your-own-path ".emacs.desktop"))
(desktop-read your-own-path))
(add-hook 'kill-emacs-hook
`(lambda ()
(desktop-save ,your-own-path t)))
Upd.: v. 2, ignore on demand.
(setq your-own-path default-directory)
(if (file-exists-p
(concat your-own-path ".emacs.desktop"))
(if (y-or-n-p "Read .emacs.desktop and add hook?")
(progn
(desktop-read your-own-path)
(add-hook 'kill-emacs-hook
`(lambda ()
(desktop-save ,your-own-path t))))))
I have developed a small set of functions to manage multiple desktops: desktop+
You might want to check it out. My workflow is not exactly the same as yours, though:
I always run emacs from the same directory (I run it from a key binding in my window manager), meaning that I can not rely on the starting directory to know which desktop I want to work with
the first time I work on a new project, I call M-xdesktop-create and provide a name. The desktop is then saved to a central location (under "~/.emacs.d/desktops" by default)
each subsequent time I want to work with a saved desktop, I run M-xdesktop-load, and am provided with a list of saved sessions in which I can quickly retrieve the name of the desired session.
Sessions are always saved when emacs exits or you load another session.
For a really simple answer I put this at the end of my .emacs file. It works fine if you save the desktop in the project folder and start emacs from the project folder.
(desktop-change-dir default-directory)

Create buffer and immediately hide it?

I'm looking for a way to create a buffer and immediately hide it. It is a buffer for technical information, not interesting to the user, and it is used with shell-command to process the output.
kill-buffer - is not what I need, because I need that buffer to be live.
delete-window - doesn't do it either because there's no way to make sure how exactly the buffer will open (it may create a new window or may take over another window).
It doesn't help if I create the buffer before supplying it to shell-command Regardless of whether it existed before, it will bring it to front and, if there was only one window at the time it did it, it will create an additional window, but if there were more windows, then it basically does something random. Some times it will create a new window... other times it won't.
EDIT:
The example below illustrates the problem:
(defun haxe-start-waiting-server (&optional compiler host port)
"Starts Haxe `haxe-compiler' on `haxe-server-host':`haxe-server-port'
with \"--wait\" for the future requests made by autocompletion
or flymake.
This function is bound to \\[haxe-start-waiting-server]"
(interactive
(let ((compiler-i
(read-string "Haxe compiler: "
haxe-compiler t haxe-compiler))
(host-i
(read-string "Haxe server host: "
haxe-server-host t haxe-server-host))
(port-i
(read-number "Haxe server port: " haxe-server-port)))
(list compiler-i host-i port-i)))
(unless (called-interactively-p 'interactive)
(unless compiler (setq compiler haxe-compiler))
(unless host (setq compiler haxe-server-host))
(unless port (setq compiler haxe-server-port)))
(save-excursion
(let ((new-buffer
(get-buffer-create
(generate-new-buffer-name
" *haxe-waiting-server*"))))
(async-shell-command
(concat compiler " --wait "
host ":" (number-to-string port))
new-buffer)
(bury-buffer new-buffer))))
If you want everything to happen in the background, you may need save-window-excursion instead of save-excursion.
From the Emacs manual:
Buffers that are ephemeral and generally uninteresting to the user
have names starting with a space, so that the list-buffers and
buffer-menu commands don't mention them (but if such a buffer visits a
file, it is mentioned). A name starting with space also initially
disables recording undo information; see Undo.
For finer control on buffer behavior, you might want to use start-process instead of async-shell-command. From its documentation:
In Elisp, you will often be better served by calling `start-process'
directly, since it offers more control and does not impose the use of
a shell (with its need to quote arguments).

Emacs term-mode: stop setting remote default-directory on some hosts

I use term-mode to run Bash shells within Emacs. On remote hosts, term-mode's directory tracking feature helpfully sets default-directory to have the host name in it, so that tab completion and file access is done remotely via Tramp. Sometimes, however, I use remote hosts that mostly share the same filesystems as my workstation, as they load the same directories from NFS. In these cases, Tramp slows me down too much. I would like, when using these systems, for Emacs to set the default-directory locally. To do this I have copied term-handle-ansi-terminal-messages from the system term.el to a new file loaded by my .emacs. I replace this part:
((= command-code ?h)
(setq term-ansi-at-host argument))
with this:
((= command-code ?h)
(setq term-ansi-at-host-real argument)
(setq term-ansi-at-host
;; if it has an equivalent filesystem group, set to system-name
(if (term-equivalent-filesystem-host-group-p argument)
(system-name)
argument)))
This calls a term-equivalent-filesystem-host-group-p function that tells whether a host should be treated as having an equivalent filesystem.
This method has the desired effect but copying and modifying system Lisp code isn't robust to any future changes in the code. I don't think advising the function would be possible without duplicating half its functionality (either the message loop or the setting of default-directory and ange-ftp-… variables).
Is there a better way to do this?
I think you can get darn close with advice. I'd define advice as follows:
(defadvice term-handle-ansi-terminal-messages
(before rewrite-remote-paths-to-local (message) activate)
(when (and (string-match ".*\eAnSiTh.\\([^\r\n]+\\)\r?\n" message)
(term-equivalent-filesystem-host-group-p (match-string 1 message)))
(setq term-ansi-at-host-real (match-string 1 message))
(setq message (replace-match (system-name) t t message 1))))
All this does is look for a substring of the input to term-handle-ansi-terminal-messages that's going to fall into the case in question, and proactively rewrite the path. Now there's no need to fiddle with the internals of term-handle-ansi-terminal-messages, since it'll never see the remote path.
Is this different than modifying the function? I'd say yes, but this is open to interpretation. In particular, I think that the only knowledge the above code depends on is the format of the submessage term-long-function-name-you-already-know is going to look for, which is really an attribute of the terminal protocol, not the internals of term-now-i'm-just-being-silly. There are some potential problems, such as new code that changes behavior based on the 7th character in the match. (This is the lone . in the string-match above.)
Needless to say, I didn't test this code at all.

load .emacs from url

I guess the real quesion is : how to evaluate a file via it's url?
Of course it wouldn't be the actual .emacs file, but it it would serve that purpose, so you can run your personal settings on any PC that has emacs installed.
I'm aware of the fact that loading a buffer in certain situations (mainly 'toggling stuff) is not the same as evaluating a buffer afterwards, but that's OK.
browse-url-emacs from the standard url library makes this simple. It displays the resulting buffer, which you probably don't want in this case, but we can wrap it with save-window-excursion to circumvent that issue.
(save-window-excursion
(eval-buffer (browse-url-emacs "http://foo/bar.el")))
or with some rudimentary error handling:
(condition-case e
(save-window-excursion
(eval-buffer
(browse-url-emacs "http://foo/bar.el")))
(error (message "Could not load remote library: %s" (cadr e))))
(url-handler-mode 1) after which you can (load "http://foo/bar/baz").
I recommend you don't do that, tho, since you're exposing yourself to some serious security hole if you do that (e.g., an intermediary could substitute the file with another one that sends a nasty email to your boss/husband/...). It also come with a bunch of other annoying side-effects such as "slow start" or "hangs at startup when the remote server is unreachable", ...
What I do instead is to keep my .emacs in a Bazaar repository sync'd with some remote repository.

How can I use Emacs tramp to ssh to a remote host and edit a file as another user on an ad-hoc basis?

/multi used to work for me, now it's gone and I'm frustrated.
What I want to do is, in my dream world:
/myuser#remotehost:sudo:anotheruser:/some/path/to/file
...and have ido-mode work.
The key thing here is that 'myuser', 'remotehost' and 'anotheruser' are all very ad-hoc, I use a huge array of remote hosts, often with different users and sudo-ing to a wide range of different users.
What do I need to add and how can I test it without reloading emacs over and over?
As of this commit, TRAMP supports ad-hoc multiple hops again.
Roughly speaking, you use it like this:
/ssh:transituser#remotehost|sudo:user#remotehost:/some/file
I haven't got it to work reliably with ido-mode yet, which is a shame, but it's a lot better than nothing! :-)
The following code may help:
(defun find-file-as-root ()
"Find a file as root."
(interactive)
(let* ((parsed (when (tramp-tramp-file-p default-directory)
(coerce (tramp-dissect-file-name default-directory)
'list)))
(default-directory
(if parsed
(apply 'tramp-make-tramp-file-name
(append '("sudo" "root") (cddr parsed)))
(tramp-make-tramp-file-name "sudo" "root" "localhost"
default-directory))))
(call-interactively 'find-file)))
I had it in my .emacs file, and it seems to come from here: http://atomized.org/2011/01/toggle-between-root-non-root-in-emacs-with-tramp/
I haven't used it extensively but it seems like that is a step in the right direction.