1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-23 07:19:15 +00:00

Speed up tall rectangular selections (bug#72830)

Instead of setting the highlight overlay on every line in the selection,
only do so on the window-height worth of lines closest to point because
the rest aren't likely to be visible.

This makes a massive difference for tall rectangular selections which
previously were so slow as to be unusable.

(Tall selections are still slow if `select-active-regions` is non-nil,
but that is something that users can actually do something about.)

* lisp/rect.el (rectangle--highlight-for-redisplay)
(rectangle--unhighlight-for-redisplay):
Replace call to `apply-on-rectangle`, which operates on every line,
with a loop over an approximate screenful.
Extend the `rectangle` overlay list structure with a value for point,
because `exchange-point-and-mark` must trigger a recomputation of
highlight overlays despite the selection not actually changing.
This commit is contained in:
Mattias Engdegård 2024-08-29 14:14:22 +02:00
parent cbaf7edbb4
commit 38c7516827

View File

@ -857,102 +857,130 @@ Ignores `line-move-visual'."
(eq (nth 1 rol) (buffer-chars-modified-tick))
(eq start (nth 2 rol))
(eq end (nth 3 rol))
(equal (rectangle--crutches) (nth 4 rol)))
(equal (rectangle--crutches) (nth 4 rol))
;; Check point explicitly so that `exchange-point-and-mark'
;; triggers overlay recomputation.
(eq (nth 5 rol) (point)))
rol)
(t
(save-excursion
(let* ((nrol nil)
(let* ((pt (point))
(nrol nil)
(old (if (eq 'rectangle (car-safe rol))
(nthcdr 5 rol)
(nthcdr 6 rol)
(funcall redisplay-unhighlight-region-function rol)
nil)))
(cl-assert (eq (window-buffer window) (current-buffer)))
;; `rectangle--pos-cols' looks up the `selected-window's parameter!
(with-selected-window window
(apply-on-rectangle
(lambda (leftcol rightcol)
(let* ((mleft (move-to-column leftcol))
(left (point))
;; BEWARE: In the presence of other overlays with
;; before/after/display-strings, this happens to move to
;; the column "as if the overlays were not applied", which
;; is sometimes what we want, tho it can be
;; considered a bug in move-to-column (it should arguably
;; pay attention to the before/after-string/display
;; properties when computing the column).
(mright (move-to-column rightcol))
(right (point))
(ol
(if (not old)
(let ((ol (make-overlay left right)))
(overlay-put ol 'window window)
(overlay-put ol 'face 'region)
ol)
(let ((ol (pop old)))
(move-overlay ol left right (current-buffer))
ol))))
;; `move-to-column' may stop before the column (if bumping into
;; EOL) or overshoot it a little, when column is in the middle
;; of a char.
(cond
((< mleft leftcol) ;`leftcol' is past EOL.
(overlay-put ol 'before-string (rectangle--space-to leftcol))
(setq mright (max mright leftcol)))
((and (> mleft leftcol) ;`leftcol' is in the middle of a char.
(eq (char-before left) ?\t))
(setq left (1- left))
(move-overlay ol left right)
(goto-char left)
(overlay-put ol 'before-string (rectangle--space-to leftcol)))
((overlay-get ol 'before-string)
(overlay-put ol 'before-string nil)))
(cond
;; While doing rectangle--string-preview, the two sets of
;; overlays steps on the other's toes. I fixed some of the
;; problems, but others remain. The main one is the two
;; (rectangle--space-to rightcol) below which try to virtually
;; insert missing text, but during "preview", the text is not
;; missing (it's provided by preview's own overlay).
(rectangle--string-preview-state
(if (overlay-get ol 'after-string)
(overlay-put ol 'after-string nil)))
((< mright rightcol) ;`rightcol' is past EOL.
(let ((str (rectangle--space-to rightcol)))
(put-text-property 0 (length str) 'face 'region str)
;; If cursor happens to be here, draw it at the right place.
(rectangle--place-cursor leftcol left str)
(overlay-put ol 'after-string str)))
((and (> mright rightcol) ;`rightcol's in the middle of a char.
(eq (char-before right) ?\t))
(setq right (1- right))
(move-overlay ol left right)
(if (= rightcol leftcol)
(overlay-put ol 'after-string nil)
(goto-char right)
(let ((str (rectangle--space-to rightcol)))
(put-text-property 0 (length str) 'face 'region str)
(when (= left right)
(rectangle--place-cursor leftcol left str))
(overlay-put ol 'after-string str))))
((overlay-get ol 'after-string)
(overlay-put ol 'after-string nil)))
(when (and (= leftcol rightcol) (display-graphic-p))
;; Make zero-width rectangles visible!
(overlay-put ol 'after-string
(concat (propertize " "
'face '(region (:height 0.2)))
(overlay-get ol 'after-string))))
(push ol nrol)))
start end))
(let* ((cols (rectangle--pos-cols start end))
(startcol (car cols))
(endcol (cdr cols))
(leftcol (min startcol endcol))
(rightcol (max startcol endcol))
;; We don't know what lines will actually be displayed,
;; so add highlight overlays on lines within the window
;; height from point.
(height (window-height))
(start-pt (max start (progn (forward-line (- height))
(point))))
(end-pt (min end (progn (goto-char pt)
(forward-line height)
(point)))))
(goto-char start-pt)
(beginning-of-line)
(while
(let* ((mleft (move-to-column leftcol))
(left (point))
;; BEWARE: In the presence of other overlays with
;; before/after/display-strings, this happens to move to
;; the column "as if the overlays were not applied",
;; which is sometimes what we want, tho it can be
;; considered a bug in move-to-column (it should
;; arguably pay attention to the
;; before/after-string/display properties when computing
;; the column).
(mright (move-to-column rightcol))
(right (point))
(ol
(if (not old)
(let ((ol (make-overlay left right)))
(overlay-put ol 'window window)
(overlay-put ol 'face 'region)
ol)
(let ((ol (pop old)))
(move-overlay ol left right (current-buffer))
ol))))
;; `move-to-column' may stop before the column (if bumping
;; into EOL) or overshoot it a little, when column is in the
;; middle of a char.
(cond
((< mleft leftcol) ;`leftcol' is past EOL.
(overlay-put ol 'before-string
(rectangle--space-to leftcol))
(setq mright (max mright leftcol)))
((and (> mleft leftcol) ;`leftcol' is in the middle of a char
(eq (char-before left) ?\t))
(setq left (1- left))
(move-overlay ol left right)
(goto-char left)
(overlay-put ol 'before-string
(rectangle--space-to leftcol)))
((overlay-get ol 'before-string)
(overlay-put ol 'before-string nil)))
(cond
;; While doing rectangle--string-preview, the two sets of
;; overlays steps on the other's toes. I fixed some of the
;; problems, but others remain. The main one is the two
;; (rectangle--space-to rightcol) below which try to
;; virtually insert missing text, but during "preview", the
;; text is not missing (it's provided by preview's own
;; overlay).
(rectangle--string-preview-state
(if (overlay-get ol 'after-string)
(overlay-put ol 'after-string nil)))
((< mright rightcol) ;`rightcol' is past EOL.
(let ((str (rectangle--space-to rightcol)))
(put-text-property 0 (length str) 'face 'region str)
;; If cursor happens to be here, draw it at the right
;; place.
(rectangle--place-cursor leftcol left str)
(overlay-put ol 'after-string str)))
((and (> mright rightcol) ;`rightcol' in the middle of a char
(eq (char-before right) ?\t))
(setq right (1- right))
(move-overlay ol left right)
(if (= rightcol leftcol)
(overlay-put ol 'after-string nil)
(goto-char right)
(let ((str (rectangle--space-to rightcol)))
(put-text-property 0 (length str) 'face 'region str)
(when (= left right)
(rectangle--place-cursor leftcol left str))
(overlay-put ol 'after-string str))))
((overlay-get ol 'after-string)
(overlay-put ol 'after-string nil)))
(when (and (= leftcol rightcol) (display-graphic-p))
;; Make zero-width rectangles visible!
(overlay-put ol 'after-string
(concat (propertize
" " 'face '(region (:height 0.2)))
(overlay-get ol 'after-string))))
(push ol nrol)
(and (zerop (forward-line 1))
(bolp)
(<= (point) end-pt))))
)
)
(mapc #'delete-overlay old)
`(rectangle ,(buffer-chars-modified-tick)
,start ,end ,(rectangle--crutches)
,start ,end ,(rectangle--crutches) ,pt
,@nrol))))))
(defun rectangle--unhighlight-for-redisplay (orig rol)
(if (not (eq 'rectangle (car-safe rol)))
(funcall orig rol)
(mapc #'delete-overlay (nthcdr 5 rol))
(mapc #'delete-overlay (nthcdr 6 rol))
(setcar (cdr rol) nil)))
(defun rectangle--duplicate-right (n displacement)