Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

legit: write commit message in a buffer #1101

Merged
merged 2 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lem.asd
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,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
125 changes: 125 additions & 0 deletions src/ext/legit/legit-commit.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
(in-package :lem/legit)

#|
Done:

- "c" opens a commit message window on the right side, we can type a long message,
- 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 #"

|#

(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))

;; 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)

;; Navigation.
;; 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 commit ()
(let ((buffer (make-buffer "*legit-commit*")))
(setf (buffer-directory buffer) (uiop:getcwd))
(setf (buffer-read-only-p buffer) nil)
(erase-buffer buffer)
(move-to-line (buffer-point buffer) 1)
(insert-string (buffer-point buffer) "write commit message:")
(change-buffer-mode buffer 'legit-commit-mode)
;; (setf (buffer-read-only-p buffer) t)
(move-to-line (buffer-point buffer) 1)))

(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.
")))

(defun commit-continue ()
vindarel marked this conversation as resolved.
Show resolved Hide resolved
(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 () ()
(kill-buffer "*legit-commit*")
(lem-core/commands/window:previous-window))

(define-command commit-abort-yes-or-no () ()
;; TODO: prompt for confirmation.
vindarel marked this conversation as resolved.
Show resolved Hide resolved
(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