Skip to content

Commit

Permalink
Fixes a bug where the position is shifted when using undo during coll…
Browse files Browse the repository at this point in the history
…aborative editing.
  • Loading branch information
cxxxr committed Dec 31, 2024
1 parent 4499b6c commit b3fb68c
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 17 deletions.
45 changes: 28 additions & 17 deletions src/buffer/internal/buffer-insert.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -186,24 +186,35 @@
(defmethod insert-string/point :around (point string)
(call-before-change-functions point string)
(let ((buffer (point-buffer point)))
(if (not (buffer-enable-undo-p buffer))
(insert/after-change-function point (length string) #'call-next-method)
(let ((position (position-at-point point)))
(prog1 (insert/after-change-function point (length string) #'call-next-method)
(let ((edit (make-edit :insert-string position string)))
(if (inhibit-undo-p)
(recompute-undo-position-offset buffer edit)
(push-undo buffer edit))))))))
(cond ((buffer-enable-undo-p buffer)
(let ((position (position-at-point point)))
(prog1 (insert/after-change-function point (length string) #'call-next-method)
(let ((edit (make-edit :insert-string position string)))
(if (inhibit-undo-p)
(recompute-undo-position-offset buffer edit)
(push-undo buffer edit))))))
(t
(prog1 (insert/after-change-function point (length string) #'call-next-method)
(when (inhibit-undo-p)
(let ((edit (make-edit :insert-string (position-at-point point) string)))
(recompute-undo-position-offset buffer edit))))))))

(defmethod delete-char/point :around (point remaining-deletions)
(call-before-change-functions point remaining-deletions)
(let ((buffer (point-buffer point)))
(if (not (buffer-enable-undo-p buffer))
(delete/after-change-function point #'call-next-method)
(let* ((position (position-at-point point))
(string (delete/after-change-function point #'call-next-method))
(edit (make-edit :delete-string position string)))
(if (inhibit-undo-p)
(recompute-undo-position-offset buffer edit)
(push-undo buffer edit))
string))))
(cond ((buffer-enable-undo-p buffer)
(let* ((position (position-at-point point))
(string (delete/after-change-function point #'call-next-method))
(edit (make-edit :delete-string position string)))
(if (inhibit-undo-p)
(recompute-undo-position-offset buffer edit)
(push-undo buffer edit))
string))
(t
(let ((string (delete/after-change-function point #'call-next-method)))
(when (inhibit-undo-p)
(let ((edit (make-edit :delete-string
(position-at-point point)
string)))
(recompute-undo-position-offset buffer edit)))
string)))))
112 changes: 112 additions & 0 deletions tests/buffer/internal.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,115 @@ qrstuvwxyz"
(ok (= 1 (lem:position-at-point start)))
(ok (= 2 (lem:position-at-point end)))
(ok (= 0 old-len))))))

(defun print-buffer (point &key (cursor #'cl-ansi-text:red) (stream *standard-output*))
(let* ((buffer (lem:point-buffer point))
(text (str:concat (lem:buffer-text buffer) " "))
(pos (1- (lem:position-at-point point))))
(format stream
"~A~A~A~%"
(subseq text 0 pos)
(funcall cursor (string (char text pos)) :style :background)
(subseq text (1+ pos)))))

(defun on-before-change (name)
(lambda (point arg)
(etypecase arg
(string
(format t "~A inserts ~S into position ~S~%" name arg (lem:position-at-point point)))
(integer
(format t
"~A deletes ~S letters from position ~S~%"
name
arg
(lem:position-at-point point))))))

(defun on-after-change (cursor)
(lambda (start end old-len)
(declare (ignore end old-len))
(let* ((buffer (lem:point-buffer start))
(point (lem:buffer-point buffer)))
(print-buffer point :cursor cursor))))

(deftest multiuser-undo
;; Arrange
(let* ((alice-buffer (lem:make-buffer "Alice's buffer" :temporary t))
(bob-buffer (lem:make-buffer "Bob's buffer" :temporary t)))

(lem:add-hook (lem:variable-value 'lem:before-change-functions :buffer alice-buffer)
(on-before-change "Alice"))
(lem:add-hook (lem:variable-value 'lem:after-change-functions :buffer alice-buffer)
(on-after-change #'cl-ansi-text:red))

(lem:add-hook (lem:variable-value 'lem:before-change-functions :buffer bob-buffer)
(on-before-change "Bob"))
(lem:add-hook (lem:variable-value 'lem:after-change-functions :buffer bob-buffer)
(on-after-change #'cl-ansi-text:green))

(format t "~%## Arrange~%")

(lem:with-point ((alice-point (lem:buffer-point alice-buffer) :right-inserting)
(alice-temporary-point (lem:buffer-point alice-buffer) :left-inserting)
(bob-point (lem:buffer-point bob-buffer) :right-inserting)
(bob-temporary-point (lem:buffer-point bob-buffer) :left-inserting))


(lem:buffer-disable-undo alice-buffer)
(lem:buffer-disable-undo bob-buffer)

(lem:insert-string alice-point "___")
(lem:buffer-start alice-point)

(lem:insert-string bob-point "___")
(lem:buffer-start bob-point)
(lem:character-offset bob-point 2)

(lem:buffer-enable-undo alice-buffer)
(lem:buffer-enable-undo bob-buffer)

;; Act
(format t "~%## Act~%")

(write-line "### Alice inserts \"a\"")
(lem:insert-string alice-point "a")
(lem:with-inhibit-undo ()
(lem:insert-string (lem:move-to-position bob-temporary-point
(lem:position-at-point alice-point))
"a"))

(terpri)

(write-line "### Bob inserts \"b\"")
(lem:insert-string bob-point "b")
(lem:with-inhibit-undo ()
(lem:insert-string (lem:move-to-position alice-temporary-point
(lem:position-at-point bob-point))
"b"))

(terpri)

(write-line "### Alice undo")
(lem:buffer-undo alice-point)
(lem:with-inhibit-undo ()
(lem:delete-character (lem:move-to-position bob-temporary-point
(lem:position-at-point alice-point))
1))

(terpri)

(write-line "### Bob undo")
(lem:buffer-undo bob-point)
(lem:with-inhibit-undo ()
(lem:delete-character (lem:move-to-position alice-temporary-point
(lem:position-at-point bob-point))
1))

(terpri)
(format t "Alice: ")
(print-buffer alice-point :cursor #'cl-ansi-text:red)
(format t "Bob: ")
(print-buffer bob-point :cursor #'cl-ansi-text:green)

(terpri)
(ok (equal "___" (lem:buffer-text alice-buffer)))
(ok (equal "___" (lem:buffer-text bob-buffer))))))

0 comments on commit b3fb68c

Please sign in to comment.