Skip to content

Commit 3bbdd75

Browse files
committed
Add 'i"' and 'a"'.
1 parent b3e075e commit 3bbdd75

5 files changed

Lines changed: 164 additions & 116 deletions

File tree

extensions/vi-mode/binds.lisp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
(define-key *outer-text-objects-keymap* "w" 'vi-a-word)
135135
(define-key *inner-text-objects-keymap* "w" 'vi-inner-word)
136136
(define-key *outer-text-objects-keymap* "\"" 'vi-a-double-quote)
137+
(define-key *inner-text-objects-keymap* "\"" 'vi-inner-double-quote)
137138

138139
(setf (gethash (lem:make-key :sym "a") (keymap-table *operator-keymap*))
139140
(keymap-table *outer-text-objects-keymap*))

extensions/vi-mode/commands.lisp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
:vi-jump-next
8989
:vi-a-word
9090
:vi-inner-word
91+
:vi-a-double-quote
92+
:vi-inner-double-quote
9193
:vi-repeat
9294
:vi-normal
9395
:vi-keyboard-quit))
@@ -712,6 +714,12 @@
712714
(define-vi-text-object vi-inner-word (count) ("p")
713715
(inner-range-of 'word-object (current-state) count))
714716

717+
(define-vi-text-object vi-a-double-quote () ()
718+
(a-range-of 'double-quoted-object (current-state) 1))
719+
720+
(define-vi-text-object vi-inner-double-quote () ()
721+
(inner-range-of 'double-quoted-object (current-state) 1))
722+
715723
(define-command vi-normal () ()
716724
(change-state 'normal))
717725

extensions/vi-mode/tests/operator.lisp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@
7676
(ok (buf= "[b]ar")))
7777
(with-vi-buffer (#?"[]\n foo bar\n")
7878
(cmd "diw")
79-
(ok (buf= #?"[]\n foo bar\n"))))))
79+
(ok (buf= #?"[]\n foo bar\n"))))
80+
(testing "di\""
81+
(with-vi-buffer (" \"f[o]o\" ")
82+
(cmd "di\"")
83+
(ok (buf= " \"[\"] "))))))
8084

8185
(deftest vi-join-line
8286
(with-fake-interface ()

extensions/vi-mode/tests/visual.lisp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,20 @@
7878
(ok (buf= #?"<fo[o]> bar\n")))
7979
(with-vi-buffer (#?"f[o]o bar\n")
8080
(cmd "v3iw")
81-
(ok (buf= #?"<foo ba[r]>\n")))))))
81+
(ok (buf= #?"<foo ba[r]>\n"))))
82+
(testing "va\""
83+
(with-vi-buffer (#?' "f[o]o" "bar" ')
84+
(cmd "va\"")
85+
(ok (buf= #?' <"foo"[ ]>"bar" '))
86+
(cmd "a\"")
87+
(ok (buf= #?' <"foo" "bar"[ ]>')))
88+
(with-vi-buffer (#?' <"f[o]>o" ')
89+
(cmd "a\"")
90+
(ok (buf= #?' <"foo"[ ]>')))
91+
(with-vi-buffer (#?' "f<[o]o"> ')
92+
(cmd "a\"")
93+
(ok (buf= #?'<[ ]"foo"> '))))
94+
(testing "vi\""
95+
(with-vi-buffer (#?' "f[o]o" ')
96+
(cmd "vi\"")
97+
(ok (buf= #?' "<fo[o]>" ')))))))

extensions/vi-mode/text-objects.lisp

Lines changed: 133 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
:lem)
44
(:import-from :lem-vi-mode/core
55
:make-range
6+
:range-beginning
7+
:range-end
68
:text-object-abort)
79
(:import-from :lem-vi-mode/visual
810
:visual
@@ -19,7 +21,8 @@
1921
:a-range-of
2022
:inner-range-of
2123

22-
:word-object))
24+
:word-object
25+
:double-quoted-object))
2326
(in-package :lem-vi-mode/text-objects)
2427

2528
(defclass text-object () ())
@@ -52,12 +55,6 @@
5255
(:method ((object symbol) state count)
5356
(inner-range-of (make-instance object) state count)))
5457

55-
(defun target-region ()
56-
(if (visual-p)
57-
(visual-range)
58-
(list (copy-point (current-point))
59-
(copy-point (current-point)))))
60-
6158
(defmethod slurp-object ((object function-text-object) point direction)
6259
(check-type direction (member :forward :backward))
6360
(with-slots (function) object
@@ -80,7 +77,8 @@
8077
p))
8178
(if (eq direction :forward)
8279
(move-forward point)
83-
(move-backward point))))))
80+
(move-backward point)))))
81+
point)
8482

8583
(defun a-range-with-direction (object count beg end direction)
8684
(check-type direction (member :forward :backward))
@@ -114,10 +112,9 @@
114112
(when (or (null direction)
115113
(eq direction :backward))
116114
(skip-chars-backward beg '(#\Space #\Tab))))
117-
((not (member (character-at beg) '(#\Space #\Tab)))
118-
(slurp-object object beg (if (eq direction :backward)
119-
:forward
120-
:backward))))
115+
((and (null direction)
116+
(not (member (character-at beg) '(#\Space #\Tab))))
117+
(slurp-object object beg :backward)))
121118
(prog1
122119
(a-range-with-direction object count beg end (or direction :forward))
123120
(unless initial-blank
@@ -128,7 +125,8 @@
128125
(eq direction :forward))
129126
(or (point= end (buffer-end-point (point-buffer end)))
130127
(char= (character-at end) #\Newline))))
131-
(skip-chars-backward beg '(#\Space #\Tab))
128+
(unless direction
129+
(skip-chars-backward beg '(#\Space #\Tab)))
132130
(skip-chars-forward end '(#\Space #\Tab))))))))
133131

134132
(defmethod a-range-of (object state count)
@@ -149,119 +147,140 @@
149147
(skip-chars-backward beg '(#\Space #\Tab))
150148
(skip-chars-forward end '(#\Space #\Tab))))))))
151149

152-
(defmethod inner-range-of ((object function-text-object) state count)
150+
(defmethod inner-range-of (object state count)
153151
(declare (ignore state))
154-
(with-slots (function) object
155-
(destructuring-bind (beg end)
156-
(target-region)
157-
(let* ((direction (cond
158-
((point< beg end) :forward)
159-
((point< end beg) :backward)))
160-
(char-type (funcall function (character-at end)))
161-
(check-fn (lambda (c) (eq (funcall function c) char-type)))
162-
(buffer (point-buffer beg)))
163-
(flet ((move-forward (p)
164-
(loop with buffer-end = (buffer-end-point (point-buffer p))
165-
while (and (point/= p buffer-end)
166-
(char/= (character-at p) #\Newline)
167-
(funcall check-fn (character-at p)))
168-
do (character-offset p 1))
169-
p)
170-
(move-backward (p)
171-
(loop while (and (< 0 (point-charpos p))
172-
(funcall check-fn (character-at p -1)))
173-
do (character-offset p -1))
174-
p))
175-
(if (or (null direction)
176-
(eq direction :forward))
177-
(progn
178-
(move-backward beg)
179-
(dotimes (i count)
180-
(when (or (point= end (buffer-end-point buffer))
181-
(char= (character-at end) #\Newline))
182-
(error 'text-object-abort
183-
:range (make-range beg end)))
184-
(move-forward end)
185-
(setf char-type (funcall function (character-at end)))))
186-
(progn
187-
(move-forward beg)
188-
(dotimes (i count)
189-
(when (or (point= end (buffer-start-point buffer))
190-
(char= (character-at end -1) #\Newline))
191-
(error 'text-object-abort
192-
:range (make-range beg end)))
193-
(move-backward end)
194-
(setf char-type (funcall function (character-at end -1))))))
195-
(make-range beg end))))))
152+
(with-point ((beg (current-point))
153+
(end (current-point)))
154+
(if (member (character-at beg) '(#\Space #\Tab #\Newline))
155+
(skip-chars-backward beg '(#\Space #\Tab #\Newline))
156+
(slurp-object object beg :backward))
157+
(dotimes (i count)
158+
(when (or (point= end (buffer-end-point (point-buffer end)))
159+
(char= (character-at end) #\Newline))
160+
(error 'text-object-abort
161+
:range (make-range beg end)))
162+
(if (member (character-at end) '(#\Space #\Tab #\Newline))
163+
(skip-chars-forward end '(#\Space #\Tab))
164+
(slurp-object object end :forward)))
165+
(make-range beg end)))
196166

197-
(defmethod a-range-of ((object quoted-text-object) state count)
198-
(declare (ignore state count))
167+
(defmethod inner-range-of (object (state visual) count)
168+
(destructuring-bind (beg end)
169+
(visual-range)
170+
(let ((direction (cond
171+
((point< beg end) :forward)
172+
((point< end beg) :backward)))
173+
(buffer (point-buffer end)))
174+
(when (null direction)
175+
(if (member (character-at beg) '(#\Space #\Tab #\Newline))
176+
(skip-chars-backward beg '(#\Space #\Tab #\Newline))
177+
(slurp-object object beg :backward)))
178+
(if (or (null direction)
179+
(eq direction :forward))
180+
(progn
181+
(dotimes (i count)
182+
(when (or (point= end (buffer-end-point buffer))
183+
(char= (character-at end) #\Newline))
184+
(error 'text-object-abort
185+
:range (make-range beg end)))
186+
(slurp-object object end :forward)))
187+
(progn
188+
(slurp-object object beg :forward)
189+
(dotimes (i count)
190+
(when (or (point= end (buffer-start-point buffer))
191+
(char= (character-at end -1) #\Newline))
192+
(error 'text-object-abort
193+
:range (make-range beg end)))
194+
(slurp-object object end :backward)))))
195+
(make-range beg end)))
196+
197+
(defmethod slurp-object ((object quoted-text-object) point direction)
199198
(with-slots (quote-char escape-char) object
200-
(destructuring-bind (beg end)
201-
(target-region)
202-
(let ((direction (cond
203-
((point< beg end) :forward)
204-
((point< end beg) :backward))))
205-
(loop
206-
(skip-chars-backward beg (lambda (c) (char/= c quote-char)))
207-
(let ((prev-char (character-at beg -1)))
208-
(cond
209-
;; No quote-char found
210-
((null prev-char)
211-
(keyboard-quit))
212-
;; Skip escaped quote-char
213-
((and escape-char
214-
(char= prev-char escape-char)))
215-
;; Successfully found
216-
(t
217-
(character-offset beg -1)
218-
(return)))))
219-
(loop
220-
(skip-chars-forward end (lambda (c) (char/= c quote-char)))
221-
(let ((next-char (character-at end)))
222-
(cond
223-
;; No quote-char found
224-
((null next-char)
225-
(keyboard-quit))
226-
;; Skip escaped quote-char
227-
((and escape-char
228-
(char= (character-at end -1) escape-char)))
229-
;; Successfully found
230-
(t
231-
(character-offset end 1)
232-
(return)))))
233-
(if (member (character-at end) '(#\Space #\Tab))
234-
(skip-chars-forward end '(#\Space #\Tab))
235-
(skip-chars-backward beg '(#\Space #\Tab))))
236-
(make-range beg end))))
199+
(ecase direction
200+
(:backward
201+
(when (char= (character-at point) quote-char)
202+
(character-offset point -1))
203+
(loop
204+
(skip-chars-backward point (lambda (c) (char/= c quote-char)))
205+
(let ((prev-char (character-at point -1)))
206+
(cond
207+
;; No quote-char found
208+
((null prev-char)
209+
(keyboard-quit))
210+
;; Skip escaped quote-char
211+
((and escape-char
212+
(char= prev-char escape-char)))
213+
;; Successfully found
214+
(t
215+
(character-offset point -1)
216+
(return))))))
217+
(:forward
218+
(when (char= (character-at point) quote-char)
219+
(character-offset point 1))
220+
(loop
221+
(skip-chars-forward point (lambda (c) (char/= c quote-char)))
222+
(let ((next-char (character-at point)))
223+
(cond
224+
;; No quote-char found
225+
((null next-char)
226+
(keyboard-quit))
227+
;; Skip escaped quote-char
228+
((and escape-char
229+
(char= (character-at point -1) escape-char)))
230+
;; Successfully found
231+
(t
232+
(character-offset point 1)
233+
(return)))))))))
237234

238235
(defmethod a-range-of ((object quoted-text-object) (state visual) count)
239-
(declare (ignore count))
240-
(with-slots (open-char escape-char) object
236+
(with-slots (quote-char escape-char) object
241237
(destructuring-bind (beg end)
242238
(visual-range)
243-
(let ((direction (cond
244-
((point< beg end) :forward)
245-
((point< end beg) :backward))))
246-
(loop
247-
(skip-chars-backward beg (lambda (c) (char/= c open-char)))
248-
(unless (char= (character-at beg -1) escape-char)
249-
(character-offset beg -1)
250-
(return)))
251-
(loop
252-
(skip-chars-forward end (lambda (c) (char/= c open-char)))
253-
(unless (char= (character-at end -1) escape-char)
254-
(character-offset end 1)
255-
(return)))
256-
(if (member (character-at end) '(#\Space #\Tab))
257-
(skip-chars-forward end '(#\Space #\Tab))
258-
(skip-chars-backward beg '(#\Space #\Tab))))
259-
(make-range beg end))))
239+
(let* ((region-string (points-to-string beg end))
240+
(len (length region-string))
241+
(quote-count 0)
242+
(direction (cond
243+
((point< beg end) :forward)
244+
((point< end beg) :backward))))
245+
(when (/= len 0)
246+
(do ((i 0 (1+ i)))
247+
((<= len i))
248+
(let ((char (aref region-string i)))
249+
(cond
250+
((char= char quote-char)
251+
(incf quote-count))
252+
((char= char escape-char)
253+
(incf i))))))
254+
(if (= (mod quote-count 2) 1)
255+
;; Incomplete object in selected region
256+
(progn
257+
(if (eq direction :backward)
258+
(progn
259+
(skip-chars-backward end (lambda (c) (char/= c quote-char)))
260+
(character-offset end -1)
261+
(skip-chars-backward end '(#\Space #\Tab)))
262+
(progn
263+
(skip-chars-forward end (lambda (c) (char/= c quote-char)))
264+
(character-offset end 1)
265+
(skip-chars-forward end '(#\Space #\Tab))))
266+
(make-range beg end))
267+
(call-next-method))))))
268+
269+
(defmethod inner-range-of ((object quoted-text-object) state count)
270+
(declare (ignore state count))
271+
(let ((range (call-next-method)))
272+
(character-offset (range-beginning range) 1)
273+
(character-offset (range-end range) -1)
274+
range))
260275

261276
(defclass word-object (function-text-object) ()
262277
(:default-initargs
263278
:function #'word-char-type))
264279

280+
(defclass double-quoted-object (quoted-text-object) ()
281+
(:default-initargs
282+
:quote-char #\"))
283+
265284
(defmethod a-range-of :before ((object word-object) (state visual) count)
266285
(unless (visual-char-p)
267286
(vi-visual-char)))

0 commit comments

Comments
 (0)