stgit.el: Add stgit-line-format
[stgit] / contrib / stgit.el
index 407185c..f08b007 100644 (file)
@@ -14,6 +14,7 @@
 
 (require 'git nil t)
 (require 'cl)
+(require 'comint)
 (require 'ewoc)
 (require 'easymenu)
 (require 'format-spec)
@@ -43,12 +44,30 @@ instead of \"dir/old/file -> dir/new/file\"."
 (defcustom stgit-default-show-worktree t
   "Set to non-nil to by default show the working tree in a new stgit buffer.
 
-Use \\<stgit-mode-map>\\[stgit-toggle-worktree] to toggle the
-this setting in an already-started StGit buffer."
+Use \\<stgit-mode-map>\\[stgit-toggle-worktree] to toggle this \
+setting in an already-started StGit buffer."
   :type 'boolean
   :group 'stgit
   :link '(variable-link stgit-show-worktree))
 
+(defcustom stgit-default-show-unknown nil
+  "Set to non-nil to by default show unknown files a new stgit buffer.
+
+Use \\<stgit-mode-map>\\[stgit-toggle-unknown] to toggle this \
+setting in an already-started StGit buffer."
+  :type 'boolean
+  :group 'stgit
+  :link '(variable-link stgit-show-unknown))
+
+(defcustom stgit-default-show-ignored nil
+  "Set to non-nil to by default show ignored files a new stgit buffer.
+
+Use \\<stgit-mode-map>\\[stgit-toggle-ignored] to toggle this \
+setting in an already-started StGit buffer."
+  :type 'boolean
+  :group 'stgit
+  :link '(variable-link stgit-show-ignored))
+
 (defcustom stgit-find-copies-harder nil
   "Try harder to find copied files when listing patches.
 
@@ -99,8 +118,8 @@ variable is used instead."
 (defcustom stgit-noname-patch-line-format "%s%m%e%D"
   "The alternate format string used to format patch lines.
 It has the same semantics as `stgit-patch-line-format', and the
-display can be toggled between the two formats using
-\\<stgit-mode-map>>\\[stgit-toggle-patch-names].
+display can be toggled between the two formats using \
+\\<stgit-mode-map>\\[stgit-toggle-patch-names].
 
 The alternate form is used when the patch name is hidden."
   :type 'string
@@ -110,8 +129,8 @@ The alternate form is used when the patch name is hidden."
 (defcustom stgit-default-show-patch-names t
   "If non-nil, default to showing patch names in a new stgit buffer.
 
-Use \\<stgit-mode-map>\\[stgit-toggle-patch-names] to toggle the
-this setting in an already-started StGit buffer."
+Use \\<stgit-mode-map>\\[stgit-toggle-patch-names] \
+to toggle the this setting in an already-started StGit buffer."
   :type 'boolean
   :group 'stgit
   :link '(variable-link stgit-show-patch-names))
@@ -270,14 +289,19 @@ A newline is appended."
     (error))
   (insert (match-string 1 text) ?\n))
 
+(defun stgit-line-format ()
+  "Return the current line format; one of
+`stgit-patch-line-format' and `stgit-noname-patch-line-format'"
+  (if stgit-show-patch-names
+      stgit-patch-line-format
+    stgit-noname-patch-line-format))
+
 (defun stgit-patch-pp (patch)
   (let* ((status (stgit-patch->status patch))
          (start (point))
          (name (stgit-patch->name patch))
          (face (cdr (assq status stgit-patch-status-face-alist)))
-         (fmt (if stgit-show-patch-names
-                  stgit-patch-line-format
-                stgit-noname-patch-line-format))
+         (fmt (stgit-line-format))
          (spec (format-spec-make
                 ?s (case status
                      ('applied "+")
@@ -733,10 +757,12 @@ Cf. `stgit-file-type-change-string'."
 (defun stgit-insert-patch-files (patch)
   "Expand (show modification of) the patch PATCH after the line
 at point."
-  (let* ((patchsym (stgit-patch->name patch))
-         (end      (point-marker))
-         (args     (list "-z" (stgit-find-copies-harder-diff-arg)))
-         (ewoc     (ewoc-create #'stgit-file-pp nil nil t)))
+  (let* ((patchsym     (stgit-patch->name patch))
+         (end          (point-marker))
+         (args         (list "-z" (stgit-find-copies-harder-diff-arg)))
+         (ewoc         (ewoc-create #'stgit-file-pp nil nil t))
+         (show-ignored stgit-show-ignored)
+         (show-unknown stgit-show-unknown))
     (set-marker-insertion-type end t)
     (setf (stgit-patch->files-ewoc patch) ewoc)
     (with-temp-buffer
@@ -752,9 +778,9 @@ at point."
                       `("diff-tree" ,@args "-r" ,(stgit-id patchsym)))))
 
         (when (and (eq patchsym :work))
-          (when stgit-show-ignored
+          (when show-ignored
             (stgit-insert-ls-files '("--ignored" "--others") "I"))
-          (when stgit-show-unknown
+          (when show-unknown
             (stgit-insert-ls-files '("--directory" "--no-empty-directory"
                                      "--others")
                                    "X"))
@@ -843,6 +869,8 @@ See also `stgit-expand'."
         (stgit-process-files (lambda (f)
                                (setq node (ewoc-enter-after ewoc node f))))))
 
+    (move-to-column (stgit-goal-column))
+
     (let ((inhibit-read-only t))
       (put-text-property start end 'patch-data patch))))
 
@@ -1224,7 +1252,9 @@ Commands for branches:
 
 Customization variables:
 `stgit-abbreviate-copies-and-renames'
+`stgit-default-show-ignored'
 `stgit-default-show-patch-names'
+`stgit-default-show-unknown'
 `stgit-default-show-worktree'
 `stgit-find-copies-harder'
 `stgit-show-worktree-mode'
@@ -1236,15 +1266,17 @@ See also \\[customize-group] for the \"stgit\" group."
         major-mode 'stgit-mode
         goal-column 2)
   (use-local-map stgit-mode-map)
-  (set (make-local-variable 'list-buffers-directory) default-directory)
-  (set (make-local-variable 'stgit-marked-patches) nil)
-  (set (make-local-variable 'stgit-expanded-patches) (list :work :index))
-  (set (make-local-variable 'stgit-show-patch-names)
-       stgit-default-show-patch-names)
-  (set (make-local-variable 'stgit-show-worktree) stgit-default-show-worktree)
-  (set (make-local-variable 'stgit-index-node) nil)
-  (set (make-local-variable 'stgit-worktree-node) nil)
-  (set (make-local-variable 'parse-sexp-lookup-properties) t)
+  (mapc (lambda (x) (set (make-local-variable (car x)) (cdr x)))
+        `((list-buffers-directory       . ,default-directory)
+          (parse-sexp-lookup-properties . t)
+          (stgit-expanded-patches       . (:work :index))
+          (stgit-index-node             . nil)
+          (stgit-worktree-node          . nil)
+          (stgit-marked-patches         . nil)
+          (stgit-show-ignored           . ,stgit-default-show-ignored)
+          (stgit-show-patch-names       . ,stgit-default-show-patch-names)
+          (stgit-show-unknown           . ,stgit-default-show-unknown)
+          (stgit-show-worktree          . ,stgit-default-show-worktree)))
   (set-variable 'truncate-lines 't)
   (add-hook 'after-save-hook 'stgit-update-stgit-for-buffer)
   (unless stgit-did-advise
@@ -1273,15 +1305,53 @@ refresh the stgit buffers as the git status of files change."
               (add-to-list 'after-load-alist
                            `(,feature (stgit-advise-funlist
                                        (quote ,funlist)))))))
+        ;; lists of (<feature> <function> <function> ...) to be advised
         '((vc-git vc-git-rename-file vc-git-revert vc-git-register)
-          (git    git-add-file git-checkout git-revert-file git-remove-file))))
+          (git    git-add-file git-checkout git-revert-file git-remove-file)
+          (dired  dired-delete-file))))
+
+(defvar stgit-pending-refresh-buffers nil
+  "Alist of (cons `buffer' `refresh-index') of buffers that need
+to be refreshed. `refresh-index' is non-nil if both work tree
+and index need to be refreshed.")
+
+(defun stgit-run-pending-refreshs ()
+  "Run all pending stgit buffer updates as posted by `stgit-post-refresh'."
+  (let ((buffers stgit-pending-refresh-buffers)
+        (stgit-inhibit-messages t))
+    (setq stgit-pending-refresh-buffers nil)
+    (while buffers
+      (let* ((elem (car buffers))
+             (buffer (car elem))
+             (refresh-index (cdr elem)))
+        (when (buffer-name buffer)
+          (with-current-buffer buffer
+            (stgit-refresh-worktree)
+            (when refresh-index (stgit-refresh-index)))))
+      (setq buffers (cdr buffers)))))
+
+(defun stgit-post-refresh (buffer refresh-index)
+  "Update worktree status in BUFFER when Emacs becomes idle. If
+REFRESH-INDEX is non-nil, also update the index."
+  (unless stgit-pending-refresh-buffers
+    (run-with-idle-timer 0.1 nil 'stgit-run-pending-refreshs))
+  (let ((elem (assq buffer stgit-pending-refresh-buffers)))
+    (if elem
+        ;; if buffer is already present, set its refresh-index flag if
+        ;; necessary
+        (when refresh-index
+          (setcdr elem t))
+      ;; new entry
+      (setq stgit-pending-refresh-buffers
+            (cons (cons buffer refresh-index)
+                  stgit-pending-refresh-buffers)))))
 
 (defun stgit-update-stgit-for-buffer (&optional refresh-index)
-  "Refresh worktree status in any `stgit-mode' buffer that shows
-the status of the current buffer.
+  "When Emacs becomes idle, refresh worktree status in any
+`stgit-mode' buffer that shows the status of the current buffer.
 
-If REFRESH-INDEX is not-nil, also update the index."
-  (let* ((dir (cond ((eq major-mode 'git-status-mode)
+If REFRESH-INDEX is non-nil, also update the index."
+  (let* ((dir (cond ((derived-mode-p 'stgit-status-mode 'dired-mode)
                      default-directory)
                     (buffer-file-name
                      (file-name-directory
@@ -1290,9 +1360,7 @@ If REFRESH-INDEX is not-nil, also update the index."
                             (error nil))))
         (buffer (and gitdir (stgit-find-buffer gitdir))))
     (when buffer
-      (with-current-buffer buffer
-        (stgit-refresh-worktree)
-        (when refresh-index (stgit-refresh-index))))))
+      (stgit-post-refresh buffer refresh-index))))
 
 (defun stgit-add-mark (patchsym)
   "Mark the patch PATCHSYM."
@@ -1493,9 +1561,16 @@ If ALL is not nil, also return non-stgit branches."
               ((not (string-match stgit-allowed-branch-name-re branch))
                (error "Invalid branch name"))
               ((yes-or-no-p (format "Create branch \"%s\"? " branch))
-               (stgit-capture-output nil (stgit-run "branch" "--create" "--"
-                                                    branch))
-               t))
+               (let ((branch-point (completing-read
+                                    "Branch from (default current branch): "
+                                    (stgit-available-branches))))
+                 (stgit-capture-output nil
+                   (apply 'stgit-run
+                          `("branch" "--create" "--"
+                            ,branch
+                            ,@(unless (zerop (length branch-point))
+                                (list branch-point)))))
+                 t)))
     (stgit-reload)))
 
 (defun stgit-available-refs (&optional omit-stgit)
@@ -2223,6 +2298,19 @@ deepest patch had before the squash."
            (stgit-reload)))
     (funcall old-sentinel process sentinel)))
 
+(defun stgit-execute-process-filter (process output)
+  (with-current-buffer (process-buffer process)
+    (let* ((old-point (point))
+           (pmark     (process-mark process))
+           (insert-at (marker-position pmark))
+           (at-pmark  (= insert-at old-point)))
+      (goto-char insert-at)
+      (insert-before-markers output)
+      (comint-carriage-motion insert-at (point))
+      (set-marker pmark (point))
+      (unless at-pmark
+        (goto-char old-point)))))
+
 (defun stgit-execute ()
   "Prompt for an stg command to execute in a shell.
 
@@ -2261,7 +2349,10 @@ When the command has finished, reload the stgit buffer."
                    (process-sentinel process))
               (set (make-local-variable 'stgit-buffer)
                    old-buffer)
+              (set-process-filter process 'stgit-execute-process-filter)
               (set-process-sentinel process 'stgit-execute-process-sentinel))))
+      (with-current-buffer buffer
+        (comint-carriage-motion (point-min) (point-max)))
       (shrink-window-if-larger-than-buffer (get-buffer-window buffer))
       (stgit-reload))))
 
@@ -2351,6 +2442,8 @@ work tree will show up."
   "Toggle the visibility of files ignored by git in the work
 tree. With ARG, show these files if ARG is positive.
 
+Its initial setting is controlled by `stgit-default-show-ignored'.
+
 Use \\[stgit-toggle-worktree] to show the work tree."
   (interactive)
   (stgit-assert-mode)
@@ -2364,6 +2457,8 @@ Use \\[stgit-toggle-worktree] to show the work tree."
   "Toggle the visibility of files not registered with git in the
 work tree. With ARG, show these files if ARG is positive.
 
+Its initial setting is controlled by `stgit-default-show-unknown'.
+
 Use \\[stgit-toggle-worktree] to show the work tree."
   (interactive)
   (stgit-assert-mode)