How to use a cons cell to define and later remove

2019-08-14 17:49发布

问题:

I am looking for some guidance, please, to reduce the time needed to perform my custom overlay removal function. The delay of up to 0.1 seconds is caused because a plethora of variables all have values, however, not every variable is necessarily used.

My goal is to attach a second variable that can be set to non-nil whenever the first variable is used, but I am unsure how to set this up and how to incorporate that into the overlay removal function.

Perhaps something that looks like this would be useful:

  • if (variable-one . t), then (remove-overlays (point-min) (point-max) 'display character)

In the following example, M-x sub-char-mode will place an overlay over the characters 1, 2 or 3 whenever the cursor is visiting any of those characters:

  • 1 will become |1

  • 2 will become |2

  • 3 will become |3

(defvar variable-one (concat
  (propertize (char-to-string ?\u007C)
    'face 'font-lock-warning-face
    'cursor t)
  (propertize "1" 'face 'highlight 'cursor t) ))

(defvar variable-one-p (cons variable-one nil))

(defvar variable-two (concat
  (propertize (char-to-string ?\u007C)
    'face 'font-lock-warning-face
    'cursor t)
  (propertize "2" 'face 'highlight 'cursor t) ))

(defvar variable-two-p (cons variable-two nil))

(defvar variable-three (concat
  (propertize (char-to-string ?\u007C)
    'face 'font-lock-warning-face
    'cursor t)
  (propertize "3" 'face 'highlight 'cursor t) ))

(defvar variable-three-p (cons variable-three nil))

(defun substitute-character ()
  (cond
    ((eq (char-after (point)) 49)
      (setq variable-one-p (cons variable-one t))
      (overlay-put (make-overlay (point) (1+ (point))) 'display variable-one))
    ((eq (char-after (point)) 50)
      (setq variable-two-p (cons variable-two t))
      (overlay-put (make-overlay (point) (1+ (point))) 'display variable-two))
    ((eq (char-after (point)) 51)
      (setq variable-three-p (cons variable-three t))
      (overlay-put (make-overlay (point) (1+ (point))) 'display variable-three))))

(defun remove-sub-char ()
  (dolist (character `(
      ,variable-one
      ,variable-two
      ,variable-three))
    (remove-overlays (point-min) (point-max) 'display character))
 (dolist (my-variable `(
      ,variable-one-p
      ,variable-two-p
      ,variable-three-p))
    (setq my-variable nil)) )

(defun sub-char-post-command-hook ()
  (remove-sub-char)
  (substitute-character))

(define-minor-mode sub-char-mode
"A minor-mode for testing overlay-removal with cons cells."
  :init-value nil
  :lighter " OV-REMOVE"
  :keymap nil
  :global nil
  :group 'lawlist
  (cond
    (sub-char-mode
      (add-hook 'post-command-hook 'sub-char-post-command-hook nil t)
      (message "Turned ON `sub-char-mode`."))
    (t
      (remove-hook 'post-command-hook 'sub-char-post-command-hook t)
      (remove-sub-char)
      (message "Turned OFF `sub-char-mode`."))))

Apologies for pasting this image here - feel free to remove it. But I couldn't paste it into a comment, to reply to your comment asking for the appearance. This is vline-style = compose and col-highlight-vline-face-flag = nil:

回答1:

First Draft (August 24, 2014):  The first draft answer defines the variables as a cons cell -- the car is a predetermined overlay string, and the cdr is nil. When the cursor visits the characters 1, 2 or 3, an overlay is placed on top of those characters and the cdr of the applicable cons cell is set to t by using setcdr. The overlay removal function contains a list of variable names and the corresponding cons cells -- when the cdr of the cons cell is non-nil (i.e., t), the overlay is removed, and the cdr of the applicable cons cell is set back to nil using setcdr. The advantage of this type of setup is that the overlay removal function will quickly look at and then skip over variables whose cons cell cdr is nil -- thus saving a substantial amount of time when dealing with large quantities of variables with predetermined overlay strings.

EDIT (August 26, 2014):  Modified code to permit using the same variable names in different buffers and set buffer-local values. Related threads are: How to use `setcdr` with buffer-local variables and Incorporate variable name into `dolist` cycle and change its value .

(defvar variable-one
  (cons
    (concat
      (propertize (char-to-string ?\u007C)
        'face 'font-lock-warning-face
        'cursor t)
      (propertize "1" 'face 'highlight 'cursor t) )
    nil))

(make-variable-buffer-local 'variable-one)

(defvar variable-two
  (cons
    (concat
     (propertize (char-to-string ?\u007C)
       'face 'font-lock-warning-face
       'cursor t)
     (propertize "2" 'face 'highlight 'cursor t) )
    nil))

(make-variable-buffer-local 'variable-two)

(defvar variable-three
  (cons
    (concat
     (propertize (char-to-string ?\u007C)
       'face 'font-lock-warning-face
       'cursor t)
     (propertize "3" 'face 'highlight 'cursor t) )
  nil))

(make-variable-buffer-local 'variable-three)

(defun sub-char ()
  (cond
    ((eq (char-after (point)) 49)
      (let ((newlist (copy-list variable-one)))
        (setcdr newlist t)
        (setq-local variable-one newlist)
        (overlay-put (make-overlay (point) (1+ (point))) 'display (car variable-one))))
    ((eq (char-after (point)) 50)
      (let ((newlist (copy-list variable-two)))
        (setcdr newlist t)
        (setq-local variable-two newlist)
        (overlay-put (make-overlay (point) (1+ (point))) 'display (car variable-two))))
    ((eq (char-after (point)) 51)
      (let ((newlist (copy-list variable-three)))
        (setcdr newlist t)
        (setq-local variable-three newlist)
        (overlay-put (make-overlay (point) (1+ (point))) 'display (car variable-three))))))

(defun remove-sub-char ()
  (dolist (character `(
      (variable-one ,variable-one)
      (variable-two ,variable-two)
      (variable-three ,variable-three)))
    (when (cdr (car (cdr character)))
      (let* (
          (var (car character))
          (newlist (copy-list (car (cdr character)))) )
        (remove-overlays (point-min) (point-max) 'display (car (car (cdr character))))
        (setcdr newlist nil)
        (set (car character) newlist)
        (message "var1: %s | var2: %s | var3: %s" variable-one variable-two variable-three) ))))

(defun sub-char-post-command-hook ()
  (remove-sub-char)
  (sub-char))

(define-minor-mode sub-char-mode
"A minor-mode for testing overlay-removal with cons cells."
  :init-value nil
  :lighter " OV-REMOVE"
  :keymap nil
  :global nil
  :group 'lawlist
  (cond
    (sub-char-mode
      (add-hook 'post-command-hook 'sub-char-post-command-hook nil t)
      (message "Turned ON `sub-char-mode`."))
    (t
      (remove-hook 'post-command-hook 'sub-char-post-command-hook t)
      (remove-sub-char)
      (message "Turned OFF `sub-char-mode`."))))


回答2:

I suggest you start by getting rid of your variable-FOO-p vars: not only their names are wrong (the "-p" suffix is meant for use with predicates which are necessarily functions, and not variables) but they're unneeded. Rather than tell remove-overlays to remove all overlays with a particular display property, just add a property of your own (e.g. (overlay-put <youroverlay> 'vline t)) so you can then do a single (remove-overlays (point-min) (point-max) 'vline t). Of course, another approach which will work at least as well is to keep a single buffer-local variable (better yet, window-local) which holds a list of all the overlays you currently have placed. This way, you can remove those overlays with a single (mapc #'delete-overlay vline--overlays), which is more efficient. It can be made even more efficient by moving/reusing those overlays rather than deleting them and then creating new ones instead.



标签: emacs elisp