Message ID | ad7c299cb0f78ae3f36d57b67fa91e5ccaab3181.1669187053.git.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | git-jump: support Emacs | expand |
Hi Yoichi On 23/11/2022 07:04, Yoichi Nakayama via GitGitGadget wrote: > From: Yoichi Nakayama <yoichi.nakayama@gmail.com> > > It works with GIT_EDITOR="emacs", "emacsclient" or "emacsclient -t" > > Signed-off-by: Yoichi Nakayama <yoichi.nakayama@gmail.com> > --- > contrib/git-jump/git-jump | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > > diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump > index cc97b0dcf02..316e9628725 100755 > --- a/contrib/git-jump/git-jump > +++ b/contrib/git-jump/git-jump > @@ -23,7 +23,22 @@ EOF > > open_editor() { > editor=`git var GIT_EDITOR` > - eval "$editor -q \$1" > + case "$editor" in > + *emacs*) > + # Supported editor values are: > + # - emacs > + # - emacsclient > + # - emacsclient -t > + # > + # Wait for completion of the asynchronously executed process > + # to avoid race conditions in case of "emacsclient". > + eval "$editor --eval \"(prog1 (pop-to-buffer (compilation-start \\\"cat $@\\\" 'grep-mode)) (while (get-buffer-process (current-buffer)) (sleep-for 0.1)) (select-frame-set-input-focus (selected-frame)))\"" I've just tried this out and it is much nicer than v4, thank you for tweaking it. It is a little sluggish to pop up the emacs window though - are you sure we need the while loop? I've commented it out and it seems to work just fine. The documentation for pop-to-buffer says it selects the frame displaying the buffer so I don't think we need to wait before calling select-frame-set-input-focus (I'm no emacs expert though). I do think it would be better to quote the filename or better still call git-jump from compilation-start as Peff suggested. It would also be nice to stop emacsclient from printing anything in the terminal. It would be nice to be able to run git-jump from within emacs. I came up with the code below which prompts the user for the directory to run git-jump in (which only matters for grep and diff --relative I think) and then checks for modified buffers visiting files in that repository before running git-jump. Best Wishes Phillip ---- >8 ---- (require 'cl-lib) (defun git-jump (dir) "Run 'git jump', prompts for the directory to run in. Also prompts to save modified buffers visiting files in the repository containing DIR" (interactive "DDirectory:") (let* ((dir (expand-file-name dir)) (worktree (git-jump--get-worktree dir))) (unless worktree (error "Not in a git repository")) (git-jump--save-worktree-buffers worktree) ;; Use "cd" rather than "git -C" so emacs can tell which directory ;; the command is being run in. (compilation-start (concat "cd " (shell-quote-argument dir) " && git jump --stdout " (read-string "Jump command: ")) 'grep-mode (lambda (mode) "*git-jump*")))) (defun git-jump--save-worktree-buffers (worktree) "Prompt the user to save all the modified buffers in WORKTREE" (let ((ht (make-hash-table :test 'equal)) (off (length worktree)) (buffers nil)) (dolist (b (buffer-list)) (when (buffer-modified-p b) (let ((file (buffer-file-name b))) (when file (let ((path (file-truename file))) (when (string-prefix-p worktree path) (puthash (substring path off) b ht))))))) (let ((modified (hash-table-keys ht))) (when modified (git-jump--ls-files worktree modified (lambda (path) (push (gethash path ht) buffers))) (when buffers (save-some-buffers nil (lambda () (memq (current-buffer) buffers)))))))) (defun git-jump--get-worktree (dir) "Get the git worktree containing DIR. Returns nil if DIR is not in a repository" (message (concat "dir: " dir)) (let* ((toplevel "") (filter (lambda (_proc text) (setf toplevel (concat toplevel text)))) (proc (make-process :name "rev-parse--toplevel" :buffer nil :coding (or file-name-coding-system default-file-name-coding-system) :command (list "git" "-C" dir "rev-parse" "--show-toplevel") :connection-type 'pipe :filter filter))) (while (or (accept-process-output proc 120) (not (memq (process-status proc) '(exit signal))))) (prog1 (if (and (eq (process-status proc) 'exit) (zerop (process-exit-status proc))) (concat (substring toplevel 0 -1) "/") nil) (delete-process proc)))) (defun git-jump--ls-files (worktree paths func) "Run FUNC on PATHS that are tracked by worktree. NB takes paths not pathspecs" (let* ((remainder "") (filter (lambda (_proc text) (let* ((text (concat remainder text)) (len (length text)) (pos 0)) (while (< pos len) (cond ((= pos (string-match "\\([^\0]+\\)\0" text pos)) (funcall func (match-string 1 text)) (setq pos (match-end 0))) (t (setq remainder (substring text pos)) (setq pos len))))))) (proc (make-process :name "ls-files" :buffer nil :coding (or file-name-coding-system default-file-name-coding-system) :command (cl-list* "git" "-C" worktree "--literal-pathspecs" "ls-files" "-z" "--" paths) :connection-type 'pipe :filter filter))) (while (or (accept-process-output proc 120) (not (memq (process-status proc) '(exit signal))))) (delete-process proc))) (provide 'git-jump)
On Wed, Nov 23, 2022 at 11:58 PM Phillip Wood <phillip.wood123@gmail.com> wrote: > I've just tried this out and it is much nicer than v4, thank you for > tweaking it. It is a little sluggish to pop up the emacs window though - > are you sure we need the while loop? I've commented it out and it seems > to work just fine. The documentation for pop-to-buffer says it selects > the frame displaying the buffer so I don't think we need to wait before > calling select-frame-set-input-focus (I'm no emacs expert though). I do > think it would be better to quote the filename or better still call > git-jump from compilation-start as Peff suggested. It would also be nice > to stop emacsclient from printing anything in the terminal. As I wrote in the code comment, there is a race condition in editor="emacsclient" case. You can observe it by removing the while loop and insert "sleep 1 &&" before cat command. However, the while loop can be moved to the end, which reduces latency. So, I'll change its position. In editor="emacsclient" case, we can stop emacsclient from printing anything in the terminal by redirecting stdout to /dev/null. But it causes following error with editor="emacsclient -t" case: emacsclient: could not get terminal name You can use editor="emacsclient -u" to suppress output. Best Wishes,
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump index cc97b0dcf02..316e9628725 100755 --- a/contrib/git-jump/git-jump +++ b/contrib/git-jump/git-jump @@ -23,7 +23,22 @@ EOF open_editor() { editor=`git var GIT_EDITOR` - eval "$editor -q \$1" + case "$editor" in + *emacs*) + # Supported editor values are: + # - emacs + # - emacsclient + # - emacsclient -t + # + # Wait for completion of the asynchronously executed process + # to avoid race conditions in case of "emacsclient". + eval "$editor --eval \"(prog1 (pop-to-buffer (compilation-start \\\"cat $@\\\" 'grep-mode)) (while (get-buffer-process (current-buffer)) (sleep-for 0.1)) (select-frame-set-input-focus (selected-frame)))\"" + ;; + *) + # assume anything else is vi-compatible + eval "$editor -q \$1" + ;; + esac } mode_diff() {