branch: externals/vc-jj
commit a6fab35d8a35fc8a4d014f1226c3887f73916dec
Author: Rudi Schlatte <r...@constantly.at>
Commit: john <j...@example.com>

    Make changes according to code review
    
    Thanks to Philip Kaludercic for the kind review!
---
 project-jj.el  |  19 +++---
 vc-jj-tests.el |   6 +-
 vc-jj.el       | 211 ++++++++++++++++++++++++++++++++++-----------------------
 3 files changed, 138 insertions(+), 98 deletions(-)

diff --git a/project-jj.el b/project-jj.el
index 9431a1d9df..2e55943642 100644
--- a/project-jj.el
+++ b/project-jj.el
@@ -29,17 +29,18 @@
   (cdr project))
 
 (cl-defmethod project-files ((project (head jj)) &optional dirs)
-  (mapcan
-   (lambda (dir)
-     (let ((default-directory dir))
-       (mapcar
-        #'expand-file-name
-        (process-lines "jj" "file" "list"))))
-   (or dirs
-       (list (project-root project)))))
+  "Return a list of files in directories DIRS in PROJECT."
+  ;; There is a bit of filename frobbing going on in this method.  The
+  ;; reason is that while jj reads and writes relative filenames, we
+  ;; get passed absolute filenames in DIRS and must return absolute
+  ;; (tilde-expanded) filenames.
+  (let* ((default-directory (expand-file-name (project-root project)))
+         (args (cons "--" (mapcar #'file-relative-name dirs))))
+    (mapcar (apply-partially #'file-name-concat default-directory)
+            (apply #'process-lines "jj" "file" "list" args))))
 
 (defun project-try-jj (dir)
-  (when-let ((root (locate-dominating-file dir ".jj")))
+  (when-let* ((root (locate-dominating-file dir ".jj")))
     (cons 'jj root)))
 
 (with-eval-after-load 'project
diff --git a/vc-jj-tests.el b/vc-jj-tests.el
index 09feb83465..b836a5d951 100644
--- a/vc-jj-tests.el
+++ b/vc-jj-tests.el
@@ -64,7 +64,7 @@ jj commands are executed with a fixed username and email; 
augment
 `process-environment' with `vc-jj-test-environment' if control
 over timestamps and random number seed (and thereby change ids)
 is needed."
-  (declare (indent 1))
+  (declare (indent 1) (debug (symbolp body)))
   `(ert-with-temp-directory ,name
      (let ((default-directory ,name)
            (process-environment
@@ -97,17 +97,17 @@ is needed."
     (let (branch-1 branch-2 branch-merged)
       ;; the root change id is always zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
       (shell-command "jj new zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
-      (setq branch-1 (vc-jj-working-revision "."))
       (write-region "Unconflicted" nil "unconflicted.txt")
       (write-region "Branch 1" nil "conflicted.txt")
       (make-directory "subdir")
       (write-region "Branch 1" nil "subdir/conflicted.txt")
+      (setq branch-1 (vc-jj-working-revision "unconflicted.txt"))
       (shell-command "jj new zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
-      (setq branch-2 (vc-jj-working-revision "."))
       (write-region "Unconflicted" nil "unconflicted.txt")
       (write-region "Branch 2" nil "conflicted.txt")
       (make-directory "subdir")
       (write-region "Branch 2" nil "subdir/conflicted.txt")
+      (setq branch-2 (vc-jj-working-revision "unconflicted.txt"))
       (shell-command (concat "jj new " branch-1 " " branch-2))
       (should (eq (vc-jj-state "unconflicted.txt") 'up-to-date))
       (should (eq (vc-jj-state "conflicted.txt") 'conflict))
diff --git a/vc-jj.el b/vc-jj.el
index c12ce65652..4675b40032 100644
--- a/vc-jj.el
+++ b/vc-jj.el
@@ -28,12 +28,12 @@
 
 ;;; Code:
 
+(require 'compat)
 (require 'seq)
-
-(autoload 'vc-switches "vc")
-(autoload 'ansi-color-apply-on-region "ansi-color")
-(autoload 'iso8601-parse "iso8601")
-(autoload 'decoded-time-set-defaults "time-date")
+(require 'vc)
+(require 'ansi-color)
+(require 'iso8601)
+(require 'time-date)
 
 (add-to-list 'vc-handled-backends 'JJ)
 
@@ -63,28 +63,34 @@
                 (const "builtin_log_detailed")
                 (string :tag "Custom template")))
 
+(defun vc-jj--call-jj (&rest args)
+  "Call `vc-jj-program' with ARGS.
+Return T if the process exited successfully (with exit status 0),
+NIL otherwise."
+  (= 0 (apply #'call-process vc-jj-program nil t nil args)))
+
 (defun vc-jj--file-tracked (file)
   (with-temp-buffer
-    (and (= 0 (call-process vc-jj-program nil t nil "file" "list" "--" file))
+    (and (vc-jj--call-jj "file" "list" "--" file)
          (not (= (point-min) (point-max))))))
 
 (defun vc-jj--file-modified (file)
   (with-temp-buffer
-    (and (= 0 (call-process vc-jj-program nil t nil "diff" "--summary" "--" 
file))
+    (and (vc-jj--call-jj "diff" "--summary" "--" file)
          (not (= (point-min) (point-max)))
-         (progn (goto-char (point-min)) (looking-at "M ")))))
+         (progn (goto-char (point-min)) (looking-at-p "M ")))))
 
 (defun vc-jj--file-added (file)
   (with-temp-buffer
-    (and (= 0 (call-process vc-jj-program nil t nil "diff" "--summary" "--" 
file))
+    (and (vc-jj--call-jj "diff" "--summary" "--" file)
          (not (= (point-min) (point-max)))
-         (progn (goto-char (point-min)) (looking-at "A ")))))
+         (progn (goto-char (point-min)) (looking-at-p "A ")))))
 
 (defun vc-jj--file-conflicted (file)
   (with-temp-buffer
-    (and (= 0 (call-process vc-jj-program nil t nil "resolve" "--list" "--" 
file))
+    (and (vc-jj--call-jj "resolve" "--list" "--" file)
          (not (= (point-min) (point-max)))
-         (progn (goto-char (point-min)) (looking-at file)))))
+         (progn (goto-char (point-min)) (looking-at-p file)))))
 
 ;;;###autoload (defun vc-jj-registered (file)
 ;;;###autoload   "Return non-nil if FILE is registered with jj."
@@ -105,10 +111,9 @@
 
 (defun vc-jj-state (file)
   "JJ implementation of `vc-state' for FILE."
-  (when-let ((root (vc-jj-root file)))
-    (let* ((default-directory root)
-           (relative (file-relative-name file)))
-      (cond
+  (when-let* ((default-directory (vc-jj-root file))
+              (relative (file-relative-name file)))
+    (cond
        ((vc-jj--file-conflicted relative)
         'conflict)
        ((vc-jj--file-modified relative)
@@ -117,7 +122,7 @@
         'added)
        ((vc-jj--file-tracked relative)
         'up-to-date)
-       (t nil)))))
+       (t nil))))
 
 (defun vc-jj-dir-status-files (dir _files update-function)
   "Calculate a list of (FILE STATE EXTRA) entries for DIR.
@@ -125,24 +130,30 @@ The list is passed to UPDATE-FUNCTION."
   ;; TODO: could be async!
   (let* ((dir (expand-file-name dir))
          (files (process-lines vc-jj-program "file" "list" "--" dir))
+        ;; TODO: Instead of the two `mapcan' calls, it should be more
+        ;; efficient to write the output to a buffer and then search
+        ;; for lines beginning with A or M, pushing them into a list.
          (changed-files (process-lines vc-jj-program "diff" "--summary" "--" 
dir))
-         (added (mapcar (lambda (entry) (substring entry 2))
-                        (seq-filter (lambda (file) (string-prefix-p "A " file))
-                                    changed-files)))
-         (modified (mapcar (lambda (entry) (substring entry 2))
-                        (seq-filter (lambda (file) (string-prefix-p "M " file))
-                                    changed-files)))
+         (added (mapcan (lambda (file) (and (string-prefix-p "A " file)
+                                            (list (substring file 2))))
+                        changed-files))
+         (modified (mapcan (lambda (file) (and (string-prefix-p "M " file)
+                                               (list (substring file 2))))
+                           changed-files))
          ;; The output of `jj resolve --list' is a list of file names
-         ;; plus a conflict description per line -- rather than trying
-         ;; to be fancy and parsing each line (and getting bugs with
-         ;; file names with spaces), use `string-prefix-p' later.
-         ;; Also, the command errors when there are no conflicts.
+         ;; plus a free-text conflict description per line -- rather
+         ;; than trying to be fancy and parsing each line (and getting
+         ;; bugs with file names with spaces), use `string-prefix-p'
+         ;; later.  Note that 'jj resolve' errors when there are no
+         ;; conflicts, which is harmless.
          (conflicted (process-lines-ignore-status vc-jj-program "resolve" 
"--list")))
     (let ((result
            (mapcar
             (lambda (file)
               (let ((vc-state
-                     (cond ((seq-find (lambda (e) (string-prefix-p file e)) 
conflicted) 'conflict)
+                     (cond ((seq-find
+                             (apply-partially #'string-prefix-p file) 
conflicted)
+                            'conflict)
                            ((member file added) 'added)
                            ((member file modified) 'edited)
                            (t 'up-to-date))))
@@ -175,35 +186,36 @@ self.divergent(), \"\\n\",
 self.hidden(), \"\\n\"
 )"))
                (status (concat
-                        (when (string= conflict "true") "(conflict)")
-                        (when (string= divergent "true") "(divergent)")
-                        (when (string= hidden "true") "(hidden)")))
+                        (and (string= conflict "true") "(conflict)")
+                        (and (string= divergent "true") "(divergent)")
+                        (and (string= hidden "true") "(hidden)")))
                (change-id-suffix (substring change-id (length 
change-id-short)))
                (commit-id-suffix (substring commit-id (length 
commit-id-short))))
     (cl-flet ((fmt (key value &optional prefix)
-                  (concat
-                   (propertize (format "% -11s: " key) 'face 'vc-dir-header)
-                   ;; there is no header value emphasis face, so we
-                   ;; use vc-dir-status-up-to-date for the prefix.
-                   (when prefix (propertize prefix 'face 
'vc-dir-status-up-to-date))
-                   (propertize value 'face 'vc-dir-header-value))))
+                (concat
+                 (propertize (format "% -11s: " key) 'face 'vc-dir-header)
+                 ;; there is no header value emphasis face, so we use
+                 ;; vc-dir-status-up-to-date for the prefix.
+                 (and prefix (propertize prefix 'face 
'vc-dir-status-up-to-date))
+                 (propertize value 'face 'vc-dir-header-value))))
       (string-join (seq-remove
-                        #'null
-                        (list
-                         (fmt "Description" (if (string= description "") "(no 
description set)" description))
-                         (fmt "Change ID" change-id-suffix change-id-short)
-                         (fmt "Commit" commit-id-suffix commit-id-short)
-                         (unless (string= bookmarks "") (fmt "Bookmarks" 
bookmarks))
-                         (unless (string= status "")
-                           ;; open-code this line instead of adding a
-                           ;; `face' parameter to `fmt'
-                           (concat
-                            (propertize (format "% -11s: " "Status") 'face 
'vc-dir-header)
-                            (propertize status 'face 
'vc-dir-status-warning)))))
-                       "\n"))))
+                    #'null
+                    (list
+                     (fmt "Description" (if (string= description "") "(no 
description set)" description))
+                     (fmt "Change ID" change-id-suffix change-id-short)
+                     (fmt "Commit" commit-id-suffix commit-id-short)
+                     (unless (string= bookmarks "") (fmt "Bookmarks" 
bookmarks))
+                     (unless (string= status "")
+                       ;; open-code this line instead of adding a
+                       ;; `face' parameter to `fmt'
+                       (concat
+                        (propertize (format "% -11s: " "Status") 'face 
'vc-dir-header)
+                        (propertize status 'face 'vc-dir-status-warning)))))
+                   "\n"))))
 
 (defun vc-jj-working-revision (file)
-  (when-let ((default-directory (vc-jj-root file)))
+  "Return the current change id of the repository containing FILE."
+  (when-let* ((default-directory (vc-jj-root file)))
     (car (process-lines vc-jj-program "log" "--no-graph"
                         "-r" "@"
                         "-T" "self.change_id().short() ++ \"\\n\""))))
@@ -226,36 +238,51 @@ self.hidden(), \"\\n\"
                                    " (" description ")"))))
 
 (defun vc-jj-create-repo ()
+  "Create an empty jj repository in the current directory."
   (if current-prefix-arg
       (call-process vc-jj-program nil nil nil "git" "init" "--colocate")
     (call-process vc-jj-program nil nil nil "git" "init")))
 
 (defun vc-jj-register (_files &optional _comment)
-  ;; No action needed.
-  )
+  "Make sure all changes are registered by jj.
+Note that jj auto-registers all changes (there is no staging area
+or notion of uncommitted changes), so we ignore arguments to this
+function."
+  ;; Any jj command picks up changes when run, so 'status' will do.
+  (vc-jj--call-jj "status"))
 
 (defun vc-jj-delete-file (file)
+  "Delete the file and make sure jj registers the change."
   (when (file-exists-p file)
-    (delete-file file)))
+    (delete-file file)
+    (vc-jj--call-jj "status")))
 
 (defun vc-jj-rename-file (old new)
-  (rename-file old new))
+  "Rename file OLD to NEW and make sure jj registers the change."
+  (rename-file old new)
+  (vc-jj--call-jj "status"))
 
 (defun vc-jj-checkin (files comment &optional _rev)
+  "Runs 'jj commit' with supplied FILES and COMMENT."
   (setq comment (replace-regexp-in-string "\\`Summary: " "" comment))
   (let ((args (append (vc-switches 'jj 'checkin) (list "--") files)))
     (apply #'call-process vc-jj-program nil nil nil "commit" "-m" comment 
args)))
 
 (defun vc-jj-find-revision (file rev buffer)
+  "Read REVISION of FILE into a buffer and return the buffer."
   (call-process vc-jj-program nil buffer nil "file" "show" "-r" rev "--" file))
 
 (defun vc-jj-checkout (file &optional rev)
-  (let ((args (if rev
-                  (list "--from" rev "--" file)
-                (list "--" file))))
+  "Restore the contents of FILE to be the same as in change REV.
+If REV is not specified, revert the file as with `vc-jj-revert'."
+  ;; TODO: check that this does the right thing: if REV is not
+  ;; specified, should we revert or leave FILE unchanged?
+  (let ((args (append (and rev (list "--from" rev))
+                      (list "--" file))))
     (call-process vc-jj-program nil nil nil "restore" args)))
 
 (defun vc-jj-revert (file &optional _contents-done)
+  "Restore FILE to the state from its parent(s), via 'jj restore'."
   (call-process vc-jj-program nil nil nil "restore" "--" file))
 
 (defun vc-jj-print-log (files buffer &optional _shortlog start-revision limit)
@@ -264,11 +291,11 @@ self.hidden(), \"\\n\"
   ;; print revisions between start-revision and limit
   (let ((inhibit-read-only t)
         (args (append
-               (when limit
-                 (list "-n" (number-to-string limit)))
-               (when start-revision
-                 (list "-r" (concat ".." start-revision)))
-               (when vc-jj-colorize-log (list "--color" "always"))
+               (and limit
+                    (list "-n" (number-to-string limit)))
+               (and start-revision
+                    (list "-r" (concat ".." start-revision)))
+               (and vc-jj-colorize-log (list "--color" "always"))
                (list "-T" vc-jj-log-template "--")
                files)))
     (with-current-buffer buffer (erase-buffer))
@@ -279,8 +306,12 @@ self.hidden(), \"\\n\"
   (goto-char (point-min)))
 
 (defun vc-jj-show-log-entry (revision)
+  "Move to the log entry for REVISION."
+  ;; TODO: check that this works for all forms of log output, or at
+  ;; least the predefined ones
   (goto-char (point-min))
   (when (search-forward-regexp
+         ;; TODO: reformulate using `rx'
          (concat "^[^|]\\s-+\\(" (regexp-quote revision) "\\)\\s-+")
          nil t)
     (goto-char (match-beginning 1))))
@@ -292,10 +323,13 @@ self.hidden(), \"\\n\"
 ;;   ;; TODO
 ;;   )
 
-(defun vc-jj-root (_file)
-  (with-temp-buffer
-    (when (= 0 (call-process vc-jj-program nil (list t nil) nil "root"))
-      (buffer-substring (point-min) (1- (point-max))))))
+(defun vc-jj-root (file)
+  "Return the root of the repository containing FILE.
+Return NIL if FILE is not in a jj repository."
+  (let ((default-directory (file-name-directory (expand-file-name file))))
+    (with-temp-buffer
+      (when (= 0 (call-process vc-jj-program nil (list t nil) nil "root"))
+        (buffer-substring (point-min) (1- (point-max)))))))
 
 (defalias 'vc-jj-responsible-p #'vc-jj-root)
 
@@ -335,6 +369,7 @@ For jj, modify `.gitignore' and call `jj untrack' or `jj 
track'."
 (defvar vc-jj-diff-switches '("--git"))
 
 (defun vc-jj-diff (files &optional rev1 rev2 buffer _async)
+  "Display diffs for FILES between two revisions."
   ;; TODO: handle async
   (setq buffer (get-buffer-create (or buffer "*vc-diff*")))
   (cond
@@ -354,37 +389,41 @@ For jj, modify `.gitignore' and call `jj untrack' or `jj 
track'."
       0)))
 
 (defun vc-jj-annotate-command (file buf &optional rev)
+  "Fill BUF with per-line commit history of FILE at REV."
   (with-current-buffer buf
+    (erase-buffer)
     (let ((rev (or rev "@")))
-      (call-process vc-jj-program nil t nil "file" "annotate" "-r" rev file))))
+      (vc-jj--call-jj "file" "annotate" "-r" rev file))))
 
 (defconst vc-jj--annotation-line-prefix-re
-  (rx (: bol
-         (group (+ (any "a-z")))        ; change id
-         " "
-         (group (+ (any alnum)))        ; author
-         (+ " ")
-         (group                         ; iso 8601-ish datetime
-          (= 4 digit) "-" (= 2 digit) "-" (= 2 digit) " "
-          (= 2 digit) ":" (= 2 digit) ":" (= 2 digit))
-         (+ " ")
-         (group (+ (any "0-9")))        ; line number
-         ": "))
+  (rx bol
+      (group (+ (any "a-z")))           ; change id
+      " "
+      (group (+ (any alnum)))           ; author username
+      (+ " ")
+      (group                            ; iso 8601-ish datetime
+       (= 4 digit) "-" (= 2 digit) "-" (= 2 digit) " "
+       (= 2 digit) ":" (= 2 digit) ":" (= 2 digit))
+      (+ " ")
+      (group (+ digit))                 ; line number
+      ": ")
   ;; TODO: find out if the output changes when the file got renamed
   ;; somewhere in its history
   "Regexp for the per-line prefix of the output of 'jj file annotate'.
 The regex captures four groups: change id, author, datetime, line number.")
 
 (defun vc-jj-annotate-time ()
-  (and (re-search-forward vc-jj--annotation-line-prefix-re nil t)
-       (let* ((dt (match-string 3))
-              (dt (and dt (string-replace " " "T" dt)))
-              (decoded (ignore-errors (iso8601-parse dt))))
-         (and decoded
-              (vc-annotate-convert-time
-               (encode-time (decoded-time-set-defaults decoded)))))))
+  "Return the time for the annotated line."
+  (and-let*
+      (((re-search-forward vc-jj--annotation-line-prefix-re nil t))
+       (dt (match-string 3))
+       (dt (and dt (string-replace " " "T" dt)))
+       (decoded (ignore-errors (iso8601-parse dt))))
+    (vc-annotate-convert-time
+     (encode-time (decoded-time-set-defaults decoded)))))
 
 (defun vc-jj-annotate-extract-revision-at-line ()
+  "Return the revision (change id) for the annotated line."
   (save-excursion
     (beginning-of-line)
     (when (looking-at vc-jj--annotation-line-prefix-re)

Reply via email to