chtlp
chtlp

Reputation: 645

start emacsclient within emacs (like magit does)

I am looking for ways to start emacs within shell-mode, and I want the client to connect to the enclosing emacs instance so I don't need to switch to the instance running the server to edit the file.

I sense it is doable because magit is already doing something like this: the commit message is edited by emacsclient, and always opened in the enclosing instance. I don't even need to start the server explicitly!

My primary usage is to do git commit from shell-mode. I can't rely on magit because I have to use some customized git version with special command. Anyway I think it would be cool thing to do.

UPDATE:

I found the magit function magit-with-emacsclient that does the trick here: https://github.com/magit/magit/blob/master/magit.el#L1979

Any help to turn the function into a shell script that can act as the default editor of git will be greatly appreciated! I will also try and post the code if I can make it work.

UPDATE 2:

Here is my solution. It requires start the server beforehand with pid in the socket name and then set your $EDITOR to be emacsclient connecting to that server.

Put this in your init.el

; start a server with a pid
(require 'server)
(defun server-start-pid ()
  (interactive)
  (when (server-running-p server-name)
    (setq server-name (format "server%s" (emacs-pid)))
    (when (server-running-p server-name)
      (server-force-delete server-name)))
  (server-start))

and run server-start-pid before you launch the editor in your shell-mode.

put this script emacsclient-pid in your PATH. It recursively find the parent process until it finds the emacs instance and invoke emacsclient on the right socket:

#! /usr/bin/python
import os, sys
import psutil

def get_pid(name):
    pid = os.getppid()
    while True:
        proc = psutil.Process(pid)
        if name in proc.name():
            break
        pid = proc.ppid()
    return pid

if __name__ == '__main__':
    pid = get_pid("emacs")
    # pass the argument to emacsclient
    args = ' '.join(sys.argv[1:])
    cmd = "emacsclient -s server{pid} {args}".format(**globals())
    print cmd
    os.system(cmd)

And put this in your .bashrc

if [[ $EMACS"x" == tx || $TERM"x" == dumbx ]]; then
    export PAGER=/bin/cat
    export EDITOR='emacsclient-pid'
else
    export PAGER=/usr/bin/less
    export EDITOR='emacs -q -nw'
fi

Upvotes: 1

Views: 1240

Answers (1)

tarsius
tarsius

Reputation: 8577

I have split the code responsible for client-server interaction into a separate library with-editor. It's part of git-modes' next branch. Magit will start using that once I merge its next branch into master. You should study that instead of the old implementation in magit itself, because it puts all things related to this in one place and because it is also improved in many ways. E.g. it also works when using tramp.

with-editors can be used by other packages besides magit. That's one of the reasons why I have split it into a separate library/package. But I have not yet implemented the parts not actually needed by magit. E.g. the functionality it provides could also be used with shell-command, see https://github.com/magit/git-modes/pull/96.

Something similar would probably work for shell-mode too. From a very quick look I think the function that would need to be advices in this case is comint-exec. I will probably not work on this soon, but eventually I will get back to this. Meanwhile have a look at that library yourself. Understanding what it does won't be easy without good knowledge of elisp and how emacs handles child processes, though. The only advice I can give you here is to also read the issues that concern with-editor in the magit/git-modes repository on github.

Ps. Translating this to a shell script won't work, because certain things have to happen in Emacs before the child process is started, like starting the server.

Upvotes: 4

Related Questions