Skip to content

Commit

Permalink
Merge pull request #1101 from vindarel/vindarel/lem-real-commit-message
Browse files Browse the repository at this point in the history
legit: write commit message in a buffer
  • Loading branch information
vindarel authored Oct 10, 2023
2 parents d6cdbab + cfd1d54 commit 689ec21
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 11 deletions.
3 changes: 2 additions & 1 deletion lem.asd
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@
:components ((:file "porcelain")
(:file "peek-legit")
(:file "legit")
(:file "legit-rebase")))
(:file "legit-rebase")
(:file "legit-commit")))
(:module "scripts"
:components ((:static-file "dumbrebaseeditor.sh")))))

Expand Down
1 change: 1 addition & 0 deletions src/commands/window.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:split-active-window-vertically
:split-active-window-horizontally
:next-window
:previous-window
:window-move-up
:window-move-down
:window-move-right
Expand Down
121 changes: 121 additions & 0 deletions src/ext/legit/legit-commit.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
(in-package :lem/legit)

#|
Done:
- "c" opens a commit message window on the right side, we can type a long message
- this command is defined in legit.lisp.
- lines starting by a # are ignored.
TODOs:
- add C-c C-s to sign the message
- and other shortcuts and features
Future:
- save previous messages, add C-p C-n commands to show them
- find related Github issue when writing "fixes #"
|#

;; major-mode for the commit buffer.
(define-major-mode legit-commit-mode lem-markdown-mode:markdown-mode
(:name "legit-commit-mode"
:syntax-table lem-markdown-mode::*markdown-syntax-table*
:keymap *legit-commit-mode-keymap*)
;; no syntax highlihgt in fact.
(setf (variable-value 'enable-syntax-highlight) t))

;; User parameters.
(defparameter *prompt-for-commit-abort-p* t
"If non t, abort the current commit message without asking for confirmation.")

;; Keys:
;; validate, abort.
(define-key *legit-commit-mode-keymap* "C-c C-c" 'commit-continue)
(define-key *legit-commit-mode-keymap* "C-Return" 'commit-continue)
(define-key *legit-commit-mode-keymap* "M-q" 'commit-abort)
(define-key *legit-commit-mode-keymap* "C-c C-k" 'commit-abort)

;; Nice to have: find and display the previous commit messages.
;; (define-key *legit-commit-mode-keymap* "C-n" 'next-commit)
;; (define-key *legit-commit-mode-keymap* "C-p" 'previous-commit)

;; Help.
(define-key *legit-commit-mode-keymap* "C-x ?" 'commit-help)

(defun clean-commit-message (text)
"Remove lines starting with a #."
;; We should collect meaningful data too, like a signature.
(loop for line in (str:lines text)
unless (str:starts-with-p "#" line)
collect line into result
finally (return (str:unlines result))))

;; void message:
#+(or)
(assert (str:blankp (clean-commit-message
"# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
")))

;; a message on the first line:
#+(or)
(assert (equal "test message" (clean-commit-message
"test message
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
")))

;; a few lines:
#+(or)
(assert (equal "one
two
" (clean-commit-message
"one
two
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
")))

(define-command commit-continue () ()
"If the commit message is non-empty, commit, kill the commit buffer and come back to the legit status window.
Lines starting with '#' are ignored."
(let* ((message (buffer-text (make-buffer "*legit-commit*")))
(cleaned-message (clean-commit-message message)))
(cond
((str:blankp cleaned-message)
(message "No commit message, do nothing."))
(t
(with-current-project ()
(run-function (lambda ()
(lem/porcelain::commit cleaned-message))
:message "commited")
(kill-buffer "*legit-commit*")
;; come back on the status on the left:
(lem-core/commands/window:previous-window)
;; and refresh.
(legit-status))))))

(define-command commit-abort () ()
(when (or (not *prompt-for-commit-abort-p*)
(prompt-for-y-or-n-p "Abort commit?"))
(lem-core/commands/window:previous-window)
(kill-buffer "*legit-commit*")))

(define-command commit-help () ()
"Show the important keybindings."
(with-pop-up-typeout-window (s (make-buffer "*legit-help*") :erase t)
(format s "Legit commit.~&")
(format s "~%")
(format s "Commands:~&")
(format s "Validate: C-Return, C-c C-c~&")
(format s "Stop and quit: Escape, M-q.~&")
(format s "~%")
(format s "Show this help: C-x ? or ?, M-x legit-commit-help")
))
55 changes: 45 additions & 10 deletions src/ext/legit/legit.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Done:
- branch checkout, branch create&checkout
- view commit at point
- basic Fossil support (current branch, add change, commit)
- redact a proper commit text in its own buffer, not only a one liner.
TODO:
Expand All @@ -37,7 +38,6 @@ Ongoing:
Nice to have/todo next:
- view log
- redact a proper commit text, not only one line
- other VCS support
Next:
Expand Down Expand Up @@ -161,7 +161,7 @@ Next:
(setf (buffer-read-only-p buffer) t)
(move-to-line (buffer-point buffer) 1)))

(defun make-move-function (file &key cached)
(defun make-diff-function (file &key cached)
(lambda ()
(with-current-project ()
(show-diff (lem/porcelain:file-diff file :cached cached)))))
Expand Down Expand Up @@ -349,12 +349,47 @@ Next:
point
start))))

(defparameter *commit-buffer-message*
"~%# Please enter the commit message for your changes.~%~
# Lines starting with '#' will be discarded, and an empty message does nothing.~%~
# Validate with C-c C-c, quit with M-q or C-c C-k")

(define-command legit-commit () ()
(let ((message (prompt-for-string "Commit message: ")))
(with-current-project ()
(lem/porcelain:commit message)
(legit-status)
(message "Commited."))))
"Write a commit message in its dedicated buffer.
In this buffer, use C-c to validate, M-q or C-c C-k to quit."

;; The git command accepts a commit message as argument (-m),
;; but also a simple "git commit" starts an editing process, with a pre-formatted
;; help text. As with the interactive rebase process, we would need to:
;; - start the commit process with a dummy editor,
;; - on validation kill the dummy editor and let the git process continue (at this moment git itself decides to validate or to ignore the message).
;; This is used by Magit, and we do this for the interactive rebase, but:
;; - our dummy editor script doesn't support windows (still as of <2023-09-22 Fri>).
;;
;; So we go with a simpler, cross-platform and pure Lem/Lisp workflow:
;; - create a Lem buffer, add some help text
;; - on validation, check ourselves that the message isn't void, extract other information (signature…) and run the commit, with the -m argument.

(let ((buffer (make-buffer "*legit-commit*")))
(setf (buffer-directory buffer) (buffer-directory))
(setf (buffer-read-only-p buffer) nil)
(erase-buffer buffer)
(move-to-line (buffer-point buffer) 1)
(insert-string (buffer-point buffer)
(format nil *commit-buffer-message*))
(change-buffer-mode buffer 'legit-commit-mode)
(move-to-line (buffer-point buffer) 1)

;; The Legit command, like grep, creates its own window.
;; Where is it best to show the commit buffer?
;; 1) quit the legit view altogether, open the commit buffer in full height:
;; (lem/legit::legit-quit)
;; 2) open the commit buffer on the left instead of legit status (and nice to have: show the full changes on the right)
;; (setf (not-switchable-buffer-p (current-buffer)) nil)
;; 3) open the commit buffer on the right, don't touch the ongoing legit status.
(next-window)
(switch-to-buffer buffer)))


(define-command legit-status () ()
Expand All @@ -376,7 +411,7 @@ Next:
(if untracked-files
(loop :for file :in untracked-files
:do (lem/peek-legit:with-appending-source
(point :move-function (make-move-function file)
(point :move-function (make-diff-function file)
:visit-file-function (make-visit-file-function file)
:stage-function (make-stage-function file)
:unstage-function (lambda () (message "File is not tracked, can't be unstaged.")))
Expand All @@ -389,7 +424,7 @@ Next:
(if unstaged-files
(loop :for file :in unstaged-files
:do (lem/peek-legit:with-appending-source
(point :move-function (make-move-function file)
(point :move-function (make-diff-function file)
:visit-file-function (make-visit-file-function file)
:stage-function (make-stage-function file)
:unstage-function (make-unstage-function file :already-unstaged t))
Expand All @@ -406,7 +441,7 @@ Next:
(loop :for file :in staged-files
:for i := 0 :then (incf i)
:do (lem/peek-legit:with-appending-source
(point :move-function (make-move-function file :cached t)
(point :move-function (make-diff-function file :cached t)
:visit-file-function (make-visit-file-function file)
:stage-function (make-stage-function file)
:unstage-function (make-unstage-function file))
Expand Down

0 comments on commit 689ec21

Please sign in to comment.