Skip to content

Commit

Permalink
Merge pull request lem-project#1050 from fukamachi/vi-yank-move-pos-c…
Browse files Browse the repository at this point in the history
…harwise

Fix the cursor position after yanked
  • Loading branch information
cxxxr authored and fukamachi committed Sep 3, 2023
2 parents 8ef9430 + a0be350 commit f61c733
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 34 deletions.
50 changes: 29 additions & 21 deletions extensions/vi-mode/commands.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
:lem-vi-mode/visual
:lem-vi-mode/jump-motions
:lem-vi-mode/text-objects
:lem-vi-mode/registers
:lem-vi-mode/commands/utils)
(:import-from :lem-vi-mode/states
:*motion-keymap*
Expand All @@ -16,8 +17,6 @@
:replace-state)
(:import-from :lem-vi-mode/commands/utils
:visual-region)
(:import-from :lem/common/killring
:peek-killring-item)
(:import-from :lem-vi-mode/utils
:kill-region-without-appending)
(:export :vi-move-to-beginning-of-line/universal-argument-0
Expand Down Expand Up @@ -275,11 +274,17 @@

(define-operator vi-delete (start end type) ("<R>")
(:move-point nil)
(let ((pos (point-charpos (current-point))))
(let ((pos (point-charpos (current-point)))
(small-deletion (and (member type '(:exclusive :inclusive)) t)))
(if (visual-p)
(visual-kill)
(with-killring-context (:options (when (eq type :line) :vi-line))
(kill-region-without-appending start end)))
(apply-visual-range
(lambda (start end)
(delete-region start end
:type type
:small small-deletion)))
(delete-region start end
:type type
:small small-deletion))
(when (and (eq type :line)
(eq 'vi-delete (command-name (this-command))))
(if (last-line-p (current-point))
Expand Down Expand Up @@ -368,24 +373,27 @@

(define-operator vi-yank (start end type) ("<R>")
(:move-point nil)
(if (eq type :block)
(visual-yank)
(with-killring-context (:options (when (eq type :line) :vi-line))
(copy-region start end))))
(case type
(:block
(apply-visual-range
(lambda (start end)
(yank-region start end :type type)))
(move-point (current-point) (first (visual-range))))
(:line
(yank-region start end :type type)
(move-to-column start (point-charpos (current-point)))
(move-point (current-point) start))
(otherwise
(yank-region start end :type type)
(move-point (current-point) start))))

(define-operator vi-yank-line (start end type) ("<R>")
(:motion vi-move-to-end-of-line)
(vi-yank start end type))

(defun vi-yank-from-clipboard-or-killring ()
(multiple-value-bind (str options) (peek-killring-item (current-killring) 0)
(if str
(values str options)
(and (enable-clipboard-p) (get-clipboard-data)))))

(define-command vi-paste-after () ()
(multiple-value-bind (string type)
(vi-yank-from-clipboard-or-killring)
(get-register #\")
(cond
((visual-p)
(let ((visual-line (visual-line-p)))
Expand All @@ -403,7 +411,7 @@
(line-end (current-point))
(insert-character (current-point) #\Newline))
(character-offset (current-point) 1))
(yank)
(insert-string (current-point) string)
(if (member :vi-line type)
(progn
(line-start (current-point))
Expand All @@ -412,7 +420,7 @@

(define-command vi-paste-before () ()
(multiple-value-bind (string type)
(vi-yank-from-clipboard-or-killring)
(get-register #\")
(cond
((visual-p)
(multiple-value-bind (beg end type)
Expand All @@ -427,10 +435,10 @@
(cond
((member :vi-line type)
(line-start (current-point))
(yank)
(insert-string (current-point) string)
(insert-character (current-point) #\Newline))
(t
(yank)))
(insert-string (current-point) string)))
(move-point (current-point) p))))))

(defun read-key-to-replace ()
Expand Down
4 changes: 3 additions & 1 deletion extensions/vi-mode/lem-vi-mode.asd
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"cl-package-locks"
"alexandria"
"split-sequence"
"lem-lisp-mode")
"lem-lisp-mode"
"trivial-types")
:components ((:file "core")
(:file "options" :depends-on ("utils"))
(:file "word" :depends-on ("options"))
Expand All @@ -16,6 +17,7 @@
(:file "visual" :depends-on ("core" "states"))
(:file "text-objects" :depends-on ("core" "visual" "word"))
(:file "jump-motions")
(:file "registers" :depends-on ("core"))
(:module "commands-utils"
:pathname "commands"
:depends-on ("core" "jump-motions" "visual" "states")
Expand Down
224 changes: 224 additions & 0 deletions extensions/vi-mode/registers.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
(defpackage :lem-vi-mode/registers
(:use :cl
:lem)
(:import-from :lem-vi-mode/core
:vi-current-window)
(:import-from :lem-core
:with-current-killring)
(:import-from :lem/common/killring
:killring
:make-killring
:killring-ring
:push-killring-item
:peek-killring-item
:make-item
:item-string
:*appending*)
(:import-from :lem/common/ring
:ring-ref
:ring-empty-p
:invalid-index-error)
(:import-from :trivial-types
:proper-list)
(:export :get-register
:set-register
:yank-region
:delete-region))
(in-package :lem-vi-mode/registers)

(deftype key-sequence ()
'(trivial-types:proper-list lem-core::key))

(deftype register () 'character)

(deftype register-designator () '(or character (string 1)))

(declaim (type hash-table *named-registers*))
(defvar *named-registers* (make-hash-table))

(declaim (type killring *yank-history*))
(defvar *yank-history* (make-killring 1))

(declaim (type killring *deletion-history*))
(defvar *deletion-history* (make-killring 9))

(defvar *unnamed-register* nil)
(defvar *small-deletion-register* nil)

(defun downcase-char (char)
(declare (type character char))
(cond
((char<= #\A char #\Z)
(code-char
(+ (char-code char)
#.(- (char-code #\a) (char-code #\A)))))
((char<= #\a char #\z))
(t char)))

(defun ensure-char (name)
(check-type name register-designator)
(if (stringp name)
(aref name 0)
name))

(defun named-register-p (name)
(declare (type character name))
(or (char<= #\a name #\z)
(char<= #\A name #\Z)))

(defun numbered-register-p (name)
(declare (type character name))
(char<= #\0 name #\9))

(defun named-register (name)
(assert (char<= #\a name #\z))
(let ((value-and-options
(gethash name *named-registers*)))
(values (car value-and-options)
(cdr value-and-options))))

(defun (setf named-register) (value name &key append)
(assert (char<= #\a name #\z))
(values-list
(setf (gethash name *named-registers*)
(if append
(destructuring-bind (existing-value &optional options)
(gethash name *named-registers*)
(cons
(etypecase existing-value
(cons
(check-type value list)
(append existing-value value))
(string
(check-type value string)
(format nil "~A~A" existing-value value))
(null value))
options))
(list value)))))

(defun numbered-register (name)
(assert (char<= #\0 name #\9))
(case name
(#\0
(peek-killring-item *yank-history* 0))
(otherwise
(if (ring-empty-p (killring-ring *deletion-history*))
(values nil nil)
(let ((n (- (char-code name) #.(char-code #\1))))
(handler-case (peek-killring-item *deletion-history* n)
(invalid-index-error ()
(values nil nil))))))))

(defun (setf numbered-register) (value name &key append)
(assert (char<= #\0 name #\9))
(check-type value string)
(case name
(#\0
(let ((*appending* append))
(push-killring-item *yank-history* value)))
(otherwise
(let ((n (- (char-code name) #.(char-code #\1)))
(ring (killring-ring *deletion-history*)))
(if append
(let ((item (ring-ref ring n)))
(if item
(setf (item-string item)
(format nil "~A~A"
(item-string item) value))
(setf (ring-ref ring n)
(make-item :string value))))
(setf (ring-ref ring n)
(make-item :string value)))))))

(defun yank-region (start end &key type)
(with-current-killring *yank-history*
(with-killring-context (:options (when (eq type :line) :vi-line)
:appending (when (eq type :block)
(continue-flag :vi-yank-block)))
(copy-region start end)))
(setf *unnamed-register* #\0)
(values))

(defun delete-region (start end &key type small)
(with-killring-context (:options (when (eq type :line) :vi-line)
:appending (when (eq type :block)
(continue-flag :vi-delete-block)))
(let ((string (lem:delete-between-points start end)))
(copy-to-clipboard-with-killring string)
(unless small
(push-killring-item *deletion-history* string))
(if small
(setf *small-deletion-register* string
*unnamed-register* #\-)
(setf *unnamed-register* #\1))))
(values))

(defun get-register (name)
(let ((name (ensure-char name)))
(declare (type register name))
(cond
((named-register-p name)
(named-register name))
((numbered-register-p name)
(numbered-register name))
(t
(ecase name
;; Unnamed register
(#\"
(etypecase *unnamed-register*
(register (get-register *unnamed-register*))
((or string
key-sequence)
*unnamed-register*)
(null nil)))
;; Small delete register
(#\-
*small-deletion-register*)
;; Most recent Ex command (read-only)
;(#\:)
;; Last inserted text (read-only)
;(#\.)
;; Current file name (read-only)
(#\%
(buffer-filename (current-buffer)))
;; Alternate file name register
(#\#
(buffer-filename (window-buffer (vi-current-window))))
;; Expression register
;(#\=)
;; Selection register
;((#\* #\+))
;; Blackhole register
(#\_
nil)
;; Last search register
;(#\/)
)))))

(defun set-register (name value)
(check-type value (or string key-sequence))
(let ((name (ensure-char name)))
(declare (type character name))
(cond
((named-register-p name)
(let ((name (downcase-char name)))
(setf (named-register name
:append (char<= #\A name #\Z))
value)))
((numbered-register-p name)
(setf (numbered-register name) value))
((char= name #\")
(setf *unnamed-register* value))
(t
(check-type value string)
(ecase name
(#\-
(check-type value string)
(setf *small-deletion-register* value))
((#\: #\. #\% #\#)
(editor-error "Register '\"~A' is read-only." name))
;(#\=)
;((#\* #\+))
(#\_ nil)
;(#\/)
)))))
Loading

0 comments on commit f61c733

Please sign in to comment.