emacs: initial add of repo-transient module
Copied out of my dotfiles. Terminal mode stuff is a little WIP right
now.
BUG=none
TEST=use menus, most commands sorta working
Change-Id: I48fd8eee4a84e9370fe9a445fb71cf66866516ba
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2324820
Tested-by: Jack Rosenthal <jrosenth@chromium.org>
Reviewed-by: Sean McAllister <smcallis@google.com>
Commit-Queue: Jack Rosenthal <jrosenth@chromium.org>
diff --git a/contrib/emacs/repo-transient.el b/contrib/emacs/repo-transient.el
new file mode 100644
index 0000000..aa4aa8f
--- /dev/null
+++ b/contrib/emacs/repo-transient.el
@@ -0,0 +1,187 @@
+;; -*- lexical-binding: t -*-
+;; Copyright 2020 The Chromium OS Authors. All Rights Reserved.
+;; Use of this source code is governed by a BSD-style license that can be
+;; found in the LICENSE file.
+
+;; repo-transient.el --- Transient menus to use some repo commands within magit
+;;
+
+;; This file is not part of GNU Emacs.
+
+(require 'term)
+(require 'transient)
+(require 'magit-process)
+
+(define-infix-argument repo:current-project ()
+ :description "Current Project"
+ :class 'transient-switch
+ :key "-c"
+ :argument ".")
+
+(define-infix-argument repo:all-projects ()
+ :description "All Projects"
+ :class 'transient-switch
+ :key "-A"
+ :argument "--all")
+
+;; TODO(jrosenth): the below keybindings don't work ... might be
+;; something with evil mode
+(defun repo--term-insert-yes ()
+ (interactive)
+ (term-send-string (get-buffer-process (current-buffer))
+ "yes\n"))
+
+(defun repo--term-insert-no ()
+ (interactive)
+ (term-send-string (get-buffer-process (current-buffer))
+ "no\n"))
+
+(defvar repo-term-mode-map
+ (let ((map (copy-keymap magit-mode-map)))
+ (define-key map (kbd "y") #'repo--term-insert-yes)
+ (define-key map (kbd "n") #'repo--term-insert-no)
+ (set-keymap-parent map magit-mode-map)
+ map))
+
+(define-derived-mode repo-term-mode term-mode "Repo Output"
+ "Derived terminal mode for repo output.")
+
+(defun repo--run-interactively (&rest args)
+ (let ((buf (apply #'make-term "repo-output" "repo" nil args)))
+ (with-current-buffer buf
+ (repo-term-mode))
+ (magit-display-buffer buf)))
+
+(defun repo-sync (args)
+ "Run a repo sync command."
+ (interactive (list (transient-args 'repo-sync-menu)))
+ (apply #'repo--run-interactively "sync" args))
+
+(defun repo-rebase (args)
+ "Run a repo rebase command."
+ (interactive (list (transient-args 'repo-rebase-menu)))
+ (apply #'repo--run-interactively "rebase" args))
+
+(define-transient-command repo-sync-menu ()
+ "Transient menu for repo sync."
+ ["Project"
+ (repo:current-project)]
+ ["Commands"
+ ("y" "Sync" repo-sync)])
+
+(define-transient-command repo-rebase-menu
+ "Transient menu for repo rebase."
+ ["Project"
+ (repo:current-project)]
+ ["Commands"
+ ("r" "Rebase" repo-rebase)])
+
+(defun repo-start (branch-name args)
+ "Run a repo start command."
+ (interactive (list "sBranch name: ")
+ (transient-args 'repo-start-menu))
+ (apply #'magit-call-process "repo" "start" args))
+
+(defun repo-start-temp (args)
+ "Run a repo start command with an auto-generated branch name."
+ (interactive (transient-args 'repo-start-menu))
+ (repo-start (format-time-string "temp-%Y-%m-%dT%H:%M:%S")
+ args))
+
+(define-transient-command repo-start-menu
+ "Transient menu for repo start."
+ ["Project"
+ (repo:all-projects)]
+ ["Commands"
+ ("s" "Start new development branch" repo-start)
+ ("t" "Start temporary development branch" repo-start-temp)])
+
+(defun repo-upload (args)
+ "Run a repo upload command."
+ (cond
+ ((and (not (member "--label=Verified+1" args))
+ (not (member "--label=Verified-1" args)))
+ (repo-upload `(,@args "--label=Verified+1")))
+ ((and (not (member "--cbr" args))
+ (not (seq-some (lambda (arg)
+ (string-prefix-p "--br=" arg))
+ args)))
+ (repo-upload `(,@args "--cbr")))
+ (t (apply #'repo--run-interactively "upload" args))))
+
+(defun repo-upload-current (args)
+ "Run a repo upload command in the current project."
+ (interactive (list (transient-args 'repo-upload-menu)))
+ (repo-upload (cons "." args)))
+
+(defun repo-upload-all (args)
+ "Run a repo upload command for all projects."
+ (interactive (list (transient-args 'repo-upload-menu)))
+ (repo-upload args))
+
+(define-infix-argument repo:--re ()
+ :description "Set reviewers"
+ :class 'transient-option
+ :key "-r"
+ :argument "--re=")
+
+(define-infix-argument repo:--cc ()
+ :description "Set reviewers"
+ :class 'transient-option
+ :key "-c"
+ :argument "--cc=")
+
+(define-infix-argument repo:--br ()
+ :description "Local branch to upload"
+ :class 'transient-option
+ :key "-b"
+ :argument "--br="
+ :reader 'magit-transient-read-revision)
+
+(define-infix-argument repo:--dest ()
+ :description "Remote destination branch"
+ :class 'transient-option
+ :key "-D"
+ :argument "--dest=")
+
+(define-infix-argument repo:--hashtag ()
+ :description "Hashtags"
+ :class 'transient-option
+ :key "-h"
+ :argument "--hashtag=")
+
+(define-transient-command repo-upload-menu ()
+ "Transient menu for repo upload."
+ ["People"
+ (repo:--re)
+ (repo:--cc)
+ ("-E" "Don't send emails" "--no-emails")]
+ ["Upload Hooks"
+ ("-n" "Skip upload hooks" "--no-verify")
+ ("-i" "Ignore failures in upload hooks" "--ignore-hooks")]
+ ["Labels"
+ ("-a" "Label Auto-Submit+1" "--label=Auto-Submit+1")
+ ("-d" "Label Commit-Queue+1 (dry run)" "--label=Commit-Queue+1")
+ ("-Q" "Label Commit-Queue+2" "--label=Commit-Queue+2")
+ ("-B" "Label Verified-1 (BAD)" "--label=Verified-1")
+ ("-S" "Sticky CQ+2" "--hashtag=stickycq")]
+ ["CL Options"
+ ("-w" "Work in Progress" "--wip")
+ ("-p" "Private" "--private")
+ (repo:--hashtag)]
+ ["Branches"
+ (repo:--br)
+ (repo:--dest)]
+ ["Upload"
+ ("u" "Upload current project" repo-upload-current)
+ ("U" "Upload all projects" repo-upload-all)])
+
+(define-transient-command repo-main-menu ()
+ "Transient menu for repo commands."
+ ["Subcommands"
+ ("y" "sync" repo-sync-menu)
+ ("r" "rebase" repo-rebase-menu)
+ ("s" "start" repo-start-menu)
+ ("u" "upload" repo-upload-menu)])
+
+(provide 'repo-transient)