From 78abd49494e00f77406cae1650e5b5bdfefeb98b Mon Sep 17 00:00:00 2001 From: Book-reader Date: Fri, 6 Sep 2024 11:54:52 +1200 Subject: [PATCH 01/12] WIP: more multiple-cursor commands --- Makefile | 1 + .../multiple-cursors/lem-multiple-cursors.asd | 4 + .../multiple-cursors/multiple-cursors.lisp | 78 +++++++++++++++++++ lem.asd | 4 +- src/commands/multiple-cursors.lisp | 38 --------- src/external-packages.lisp | 1 - 6 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 extensions/multiple-cursors/lem-multiple-cursors.asd create mode 100644 extensions/multiple-cursors/multiple-cursors.lisp delete mode 100644 src/commands/multiple-cursors.lisp diff --git a/Makefile b/Makefile index 6286f18f2..78f1724b2 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,7 @@ lint: .qlot/bin/sblint extensions/lua-mode/lem-lua-mode.asd .qlot/bin/sblint extensions/makefile-mode/lem-makefile-mode.asd .qlot/bin/sblint extensions/markdown-mode/lem-markdown-mode.asd + .qlot/bin/sblint extensions/multiple-cursors/lem-multiple-cursors.asd .qlot/bin/sblint extensions/nim-mode/lem-nim-mode.asd .qlot/bin/sblint extensions/ocaml-mode/lem-ocaml-mode.asd .qlot/bin/sblint extensions/paredit-mode/lem-paredit-mode.asd diff --git a/extensions/multiple-cursors/lem-multiple-cursors.asd b/extensions/multiple-cursors/lem-multiple-cursors.asd new file mode 100644 index 000000000..a35fe1efe --- /dev/null +++ b/extensions/multiple-cursors/lem-multiple-cursors.asd @@ -0,0 +1,4 @@ +(defsystem "lem-multiple-cursors" + :depends-on (:lem) + :serial t + :components ((:file "multiple-cursors"))) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp new file mode 100644 index 000000000..0155c13dd --- /dev/null +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -0,0 +1,78 @@ +(defpackage :lem-multiple-cursors + (:use :cl :lem) + (:import-from :lem/isearch + :isearch-start + :search-next-matched + :isearch-abort + :make-add-char-callback) + (:export :add-cursors-to-next-line + :add-cursors-to-previous-line + :mark-next-like-this) + #+sbcl + (:lock t)) +(in-package :lem-multiple-cursors) + +(define-key *global-keymap* "M-C" 'add-cursors-to-next-line) + +(define-command add-cursors-to-next-line () () + "Duplicates the cursor under the currently existing cursors." + (add-cursor-to-line-with-offset 1)) + +(define-command add-cursors-to-previous-line () () + "Duplicates the cursor above the currently existing cursors." + (add-cursor-to-line-with-offset -1)) + +(define-command mark-next-like-this (start end) (:region) + "" + (mark-like-this-direction start end #'search-forward)) + +(define-command mark-previous-like-this (start end) (:region) + "" + (mark-like-this-direction start end #'search-backward)) + +(defun add-cursor-to-line-with-offset (offset) + (let ((cursors (buffer-cursors (current-buffer)))) + (loop :for (cursor next-cursor) :on cursors + :do (with-point ((p cursor)) + (when (and (line-offset p offset (point-charpos p)) + (or (null next-cursor) + (not (same-line-p p next-cursor)))) + (make-fake-cursor p)))))) + +(defun clear-duplicate-cursors (buffer) + (loop :for (cursor next-cursor) :on (buffer-cursors buffer) + :when (and next-cursor (same-line-p cursor next-cursor)) + :do (delete-fake-cursor + (if (eq cursor (buffer-point buffer)) + next-cursor + cursor)))) + +(defun mark-like-this-direction (start end direction) + (isearch-start "" + (make-add-char-callback direction) + direction + (if (equal direction #'search-forward) + #'search-backward + #'search-forward) + (points-to-string start end)) + + (dolist (point (buffer-cursors (current-buffer))) + (with-point ((point point)) + (when (search-next-matched point 1) + (setf cursor (make-fake-cursor point)) + (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) + (set-cursor-mark cursor point)))) + (isearch-abort) + ) + +(defun garbage-collection-cursors () + (clear-duplicate-cursors (current-buffer))) + +(add-hook *post-command-hook* 'garbage-collection-cursors) + +(defun clear-cursors-when-aborted () + (let ((string (merge-cursor-killrings (current-buffer)))) + (clear-cursors (current-buffer)) + (copy-to-clipboard-with-killring string))) + +(add-hook *editor-abort-hook* 'clear-cursors-when-aborted) diff --git a/lem.asd b/lem.asd index 90b12a96b..1b06f1884 100644 --- a/lem.asd +++ b/lem.asd @@ -150,7 +150,6 @@ (:file "project" :depends-on ("file")) (:file "buffer") (:file "window" :depends-on ("move")) - (:file "multiple-cursors") (:file "process") (:file "help") (:file "font") @@ -261,7 +260,8 @@ "lem-lua-mode" "lem-terminal" "lem-legit" - "lem-dashboard")) + "lem-dashboard" + "lem-multiple-cursors")) (defsystem "lem/executable" :build-operation program-op diff --git a/src/commands/multiple-cursors.lisp b/src/commands/multiple-cursors.lisp deleted file mode 100644 index 078289e6e..000000000 --- a/src/commands/multiple-cursors.lisp +++ /dev/null @@ -1,38 +0,0 @@ -(defpackage :lem-core/commands/multiple-cursors - (:use :cl :lem-core) - (:export :add-cursors-to-next-line) - #+sbcl - (:lock t)) -(in-package :lem-core/commands/multiple-cursors) - -(define-key *global-keymap* "M-C" 'add-cursors-to-next-line) - -(define-command add-cursors-to-next-line () () - "Duplicates the cursor under the currently existing cursors." - (let ((cursors (buffer-cursors (current-buffer)))) - (loop :for (cursor next-cursor) :on cursors - :do (with-point ((p cursor)) - (when (and (line-offset p 1 (point-charpos p)) - (or (null next-cursor) - (not (same-line-p p next-cursor)))) - (make-fake-cursor p)))))) - -(defun clear-duplicate-cursors (buffer) - (loop :for (cursor next-cursor) :on (buffer-cursors buffer) - :when (and next-cursor (same-line-p cursor next-cursor)) - :do (delete-fake-cursor - (if (eq cursor (buffer-point buffer)) - next-cursor - cursor)))) - -(defun garbage-collection-cursors () - (clear-duplicate-cursors (current-buffer))) - -(add-hook *post-command-hook* 'garbage-collection-cursors) - -(defun clear-cursors-when-aborted () - (let ((string (merge-cursor-killrings (current-buffer)))) - (clear-cursors (current-buffer)) - (copy-to-clipboard-with-killring string))) - -(add-hook *editor-abort-hook* 'clear-cursors-when-aborted) diff --git a/src/external-packages.lisp b/src/external-packages.lisp index 899682ab9..46cb6a336 100644 --- a/src/external-packages.lisp +++ b/src/external-packages.lisp @@ -1,7 +1,6 @@ (uiop:define-package :lem (:use :cl) (:use-reexport :lem-core) - (:use-reexport :lem-core/commands/multiple-cursors) (:use-reexport :lem-core/commands/move) (:use-reexport :lem-core/commands/edit) (:use-reexport :lem-core/commands/mark) From b5c96591942ba217ce616fd4d801dc3850c231fb Mon Sep 17 00:00:00 2001 From: Book-reader Date: Fri, 6 Sep 2024 20:08:47 +1200 Subject: [PATCH 02/12] add message when there are no more matches --- extensions/multiple-cursors/multiple-cursors.lisp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index 0155c13dd..325fb57d1 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -58,12 +58,12 @@ (dolist (point (buffer-cursors (current-buffer))) (with-point ((point point)) - (when (search-next-matched point 1) - (setf cursor (make-fake-cursor point)) - (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) - (set-cursor-mark cursor point)))) - (isearch-abort) - ) + (if (search-next-matched point 1) + (progn (setf cursor (make-fake-cursor point)) + (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) + (set-cursor-mark cursor point)) + (message "No more matches")))) + (isearch-abort)) (defun garbage-collection-cursors () (clear-duplicate-cursors (current-buffer))) From 0eac6d57eb638f8655c4715eac69c3723587493b Mon Sep 17 00:00:00 2001 From: Book-reader Date: Sat, 7 Sep 2024 11:42:22 +1200 Subject: [PATCH 03/12] make mark-next-like-this run add-cursors-to-next-line when nothing is marked --- extensions/multiple-cursors/multiple-cursors.lisp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index 325fb57d1..b5552d0ac 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -22,13 +22,21 @@ "Duplicates the cursor above the currently existing cursors." (add-cursor-to-line-with-offset -1)) -(define-command mark-next-like-this (start end) (:region) +(define-command mark-next-like-this () () "" - (mark-like-this-direction start end #'search-forward)) + (if (buffer-mark (current-buffer)) + (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) + (region-end-using-global-mode (current-global-mode)) + #'search-forward) + (add-cursors-to-next-line))) (define-command mark-previous-like-this (start end) (:region) "" - (mark-like-this-direction start end #'search-backward)) + (if (buffer-mark (current-buffer)) + (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) + (region-end-using-global-mode (current-global-mode)) + #'search-backward) + (add-cursors-to-previous-line))) (defun add-cursor-to-line-with-offset (offset) (let ((cursors (buffer-cursors (current-buffer)))) From fe0e057a461aad21632691b75c06d31d49cbc01c Mon Sep 17 00:00:00 2001 From: Book-reader Date: Sat, 7 Sep 2024 14:28:20 +1200 Subject: [PATCH 04/12] fix checking if buffer is marked --- extensions/multiple-cursors/multiple-cursors.lisp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index b5552d0ac..1654a2364 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -24,7 +24,7 @@ (define-command mark-next-like-this () () "" - (if (buffer-mark (current-buffer)) + (if (buffer-mark-p (current-buffer)) (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) (region-end-using-global-mode (current-global-mode)) #'search-forward) @@ -32,7 +32,7 @@ (define-command mark-previous-like-this (start end) (:region) "" - (if (buffer-mark (current-buffer)) + (if (buffer-mark-p (current-buffer)) (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) (region-end-using-global-mode (current-global-mode)) #'search-backward) From 4947968cc6584e2ffd31c3eeab0f6cf0ce45304f Mon Sep 17 00:00:00 2001 From: Book-reader Date: Sat, 7 Sep 2024 16:19:12 +1200 Subject: [PATCH 05/12] WIP: match-next-like-this with multiple lines --- extensions/multiple-cursors/multiple-cursors.lisp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index 1654a2364..060d47373 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -5,6 +5,8 @@ :search-next-matched :isearch-abort :make-add-char-callback) + (:import-from :lem/buffer/internal + :point-linum) (:export :add-cursors-to-next-line :add-cursors-to-previous-line :mark-next-like-this) @@ -63,13 +65,14 @@ #'search-backward #'search-forward) (points-to-string start end)) - (dolist (point (buffer-cursors (current-buffer))) (with-point ((point point)) (if (search-next-matched point 1) - (progn (setf cursor (make-fake-cursor point)) - (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) - (set-cursor-mark cursor point)) + (progn + (setf cursor (make-fake-cursor point)) + (setf (point-linum point) (- (point-linum point) (- (point-linum end) (point-linum start)))) + (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) + (set-cursor-mark cursor point)) (message "No more matches")))) (isearch-abort)) From e95d133b7ced7a69a96e53b6fd1d370aa69039c2 Mon Sep 17 00:00:00 2001 From: Book-reader Date: Sat, 7 Sep 2024 16:51:53 +1200 Subject: [PATCH 06/12] allow multiple cursors to be on the same line but not on the same space --- extensions/multiple-cursors/multiple-cursors.lisp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index 060d47373..0a8b3d6e9 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -51,7 +51,7 @@ (defun clear-duplicate-cursors (buffer) (loop :for (cursor next-cursor) :on (buffer-cursors buffer) - :when (and next-cursor (same-line-p cursor next-cursor)) + :when (and next-cursor (and (same-line-p cursor next-cursor) (eq (point-charpos cursor) (point-charpos next-cursor)))) :do (delete-fake-cursor (if (eq cursor (buffer-point buffer)) next-cursor From 9dfa554e5c9437f423b5ce5c1aef12be939dbfa3 Mon Sep 17 00:00:00 2001 From: Book-reader Date: Sat, 7 Sep 2024 17:34:14 +1200 Subject: [PATCH 07/12] fix multiple cursors on the same point by brute forcing it with pre and post command hooks --- extensions/multiple-cursors/multiple-cursors.lisp | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index 0a8b3d6e9..b8a815d00 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -80,6 +80,7 @@ (clear-duplicate-cursors (current-buffer))) (add-hook *post-command-hook* 'garbage-collection-cursors) +(add-hook *pre-command-hook* 'garbage-collection-cursors) (defun clear-cursors-when-aborted () (let ((string (merge-cursor-killrings (current-buffer)))) From e11aec8784b2f66d921005ca85af23e77d36bfcb Mon Sep 17 00:00:00 2001 From: Book-reader Date: Mon, 9 Sep 2024 14:43:35 +1200 Subject: [PATCH 08/12] fix mark-next-like-this with multiple lines --- extensions/multiple-cursors/multiple-cursors.lisp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index b8a815d00..169978cb1 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -6,7 +6,11 @@ :isearch-abort :make-add-char-callback) (:import-from :lem/buffer/internal - :point-linum) + :point-linum + :point-line + :point-change-line) + (:import-from :lem/buffer/line + :line-previous) (:export :add-cursors-to-next-line :add-cursors-to-previous-line :mark-next-like-this) @@ -70,7 +74,8 @@ (if (search-next-matched point 1) (progn (setf cursor (make-fake-cursor point)) - (setf (point-linum point) (- (point-linum point) (- (point-linum end) (point-linum start)))) + (dotimes (_ (- (point-linum end) (point-linum start))) + (point-change-line point (- (point-linum point) 1) (line-previous (point-line point)))) (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) (set-cursor-mark cursor point)) (message "No more matches")))) From 654725877831eb53f7f19f0d27e9293814ee41d6 Mon Sep 17 00:00:00 2001 From: Book-reader Date: Mon, 9 Sep 2024 16:01:59 +1200 Subject: [PATCH 09/12] remove redundant arguments --- extensions/multiple-cursors/multiple-cursors.lisp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index 169978cb1..eca1834c1 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -36,7 +36,7 @@ #'search-forward) (add-cursors-to-next-line))) -(define-command mark-previous-like-this (start end) (:region) +(define-command mark-previous-like-this () () "" (if (buffer-mark-p (current-buffer)) (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) From 44bdc5e9afe64d33e50d0bc0d81aa6a143261734 Mon Sep 17 00:00:00 2001 From: Book-reader Date: Mon, 9 Sep 2024 21:07:59 +1200 Subject: [PATCH 10/12] actually prevent multiple cursors from being on the same point --- extensions/multiple-cursors/multiple-cursors.lisp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index eca1834c1..f31df3f6f 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -82,9 +82,10 @@ (isearch-abort)) (defun garbage-collection-cursors () + ;; TODO: find a less janky method of preventing multiple cursors from overlapping and typing the same letter twice + (clear-duplicate-cursors (current-buffer)) (clear-duplicate-cursors (current-buffer))) -(add-hook *post-command-hook* 'garbage-collection-cursors) (add-hook *pre-command-hook* 'garbage-collection-cursors) (defun clear-cursors-when-aborted () From 8cbbb0dcedb31ad7f01c182cbff67dfb860c4531 Mon Sep 17 00:00:00 2001 From: Book-reader Date: Tue, 10 Sep 2024 12:26:15 +1200 Subject: [PATCH 11/12] slight improvements & initial work to fix mark-previous-like-this --- extensions/multiple-cursors/multiple-cursors.lisp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index f31df3f6f..bedabe72c 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -10,7 +10,8 @@ :point-line :point-change-line) (:import-from :lem/buffer/line - :line-previous) + :line-previous + :line-next) (:export :add-cursors-to-next-line :add-cursors-to-previous-line :mark-next-like-this) @@ -31,16 +32,14 @@ (define-command mark-next-like-this () () "" (if (buffer-mark-p (current-buffer)) - (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) - (region-end-using-global-mode (current-global-mode)) + (mark-like-this-direction (region-beginning (current-buffer)) (region-end (current-buffer)) #'search-forward) (add-cursors-to-next-line))) (define-command mark-previous-like-this () () "" (if (buffer-mark-p (current-buffer)) - (mark-like-this-direction (region-beginning-using-global-mode (current-global-mode)) - (region-end-using-global-mode (current-global-mode)) + (mark-like-this-direction (region-beginning (current-buffer)) (region-end (current-buffer)) #'search-backward) (add-cursors-to-previous-line))) @@ -75,7 +74,9 @@ (progn (setf cursor (make-fake-cursor point)) (dotimes (_ (- (point-linum end) (point-linum start))) - (point-change-line point (- (point-linum point) 1) (line-previous (point-line point)))) + (if (equal direction #'search-forward) + (point-change-line point (- (point-linum point) 1) (line-previous (point-line point))) + (point-change-line point (+ (point-linum point) 1) (line-next (point-line point))))) (setf (point-charpos point) (- (point-charpos point) (- (point-charpos end) (point-charpos start)))) (set-cursor-mark cursor point)) (message "No more matches")))) From 28890833d4bf0e3278218872027bf8278018e2df Mon Sep 17 00:00:00 2001 From: Book-reader Date: Thu, 3 Oct 2024 16:52:14 +1300 Subject: [PATCH 12/12] mostly fix cursor and mark position when the cursor is to the left of the mark --- extensions/multiple-cursors/multiple-cursors.lisp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/multiple-cursors/multiple-cursors.lisp b/extensions/multiple-cursors/multiple-cursors.lisp index bedabe72c..2f1221b38 100644 --- a/extensions/multiple-cursors/multiple-cursors.lisp +++ b/extensions/multiple-cursors/multiple-cursors.lisp @@ -32,14 +32,14 @@ (define-command mark-next-like-this () () "" (if (buffer-mark-p (current-buffer)) - (mark-like-this-direction (region-beginning (current-buffer)) (region-end (current-buffer)) + (mark-like-this-direction (buffer-mark (current-buffer)) (buffer-point (current-buffer)) #'search-forward) (add-cursors-to-next-line))) (define-command mark-previous-like-this () () "" (if (buffer-mark-p (current-buffer)) - (mark-like-this-direction (region-beginning (current-buffer)) (region-end (current-buffer)) + (mark-like-this-direction (buffer-mark (current-buffer)) (buffer-point (current-buffer)) #'search-backward) (add-cursors-to-previous-line))) @@ -72,6 +72,7 @@ (with-point ((point point)) (if (search-next-matched point 1) (progn + (setf (point-charpos point) (point-charpos end)) (setf cursor (make-fake-cursor point)) (dotimes (_ (- (point-linum end) (point-linum start))) (if (equal direction #'search-forward)