Best way to add per-line information visually in emacs?

浪尽此生 提交于 2019-12-13 12:29:13

问题


I'm writing a minor mode for emacs which, at the very least, will calculate a numeric value for each line in a buffer. I want to display this visually, preferable neatly before each line.

I know some minor modes draw to the fringe, and I know overlays are an option too (are these related?), but I can't find a good example of what I want anywhere.

Basically, I want to have something like the line numbers from linum-mode, but they will need to change every time the buffer is modified (actually, only whenever the line they're on changes). Something like a character counter for each line would be a good example. And I'd like it to not break linum-mode, but not depend on it, etc, if possible.


回答1:


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; M-x char-count-mode

(defvar char-count-p nil
"When `char-count-p` is non-`nil`, the overlays are present.")
(make-variable-buffer-local 'char-count-p)

(defvar char-count-this-command nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'char-count-this-command)

(defvar char-count-overlay-list nil
"List used to store overlays until they are removed.")
(make-variable-buffer-local 'char-count-overlay-list)

(defun char-count-post-command-hook ()
"Doc-string."
  (setq char-count-this-command this-command)
  (character-count-function))

(defun character-count-window-scroll-functions (win _start)
"Doc-string."
  (character-count-function))

(defun equal-including-properties--remove-overlays (beg end name val)
  "Remove the overlays using `equal`, instead of `eq`."
  (when (and beg end name val)
    (overlay-recenter end)
    (dolist (o (overlays-in beg end))
      (when (equal-including-properties (overlay-get o name) val)
        (delete-overlay o)))))

(defun character-count-function ()
"Doc-string for the character-count-function."
  (when
      (and
        char-count-mode
        char-count-this-command
        (window-live-p (get-buffer-window (current-buffer)))
        (not (minibufferp))
        (pos-visible-in-window-p (point)
          (get-buffer-window (current-buffer) (selected-frame)) t) )
    (remove-char-count-overlays)
    (save-excursion
      (let* (
          counter
          (selected-window (selected-window))
          (window-start (window-start selected-window))
          (window-end (window-end selected-window t)) )
        (goto-char window-end)
        (catch 'done
          (while t
            (when counter
              (re-search-backward "\n" window-start t))
            (when (not counter)
              (setq counter t))
          (let* (
              (pbol (point-at-bol))
              (peol (point-at-eol))
              (raw-char-count (abs (- peol pbol)))
              (starting-column
                (propertize (char-to-string ?\uE001)
                  'display
                  `((space :align-to 1) (space :width 0))))
              (colored-char-count
                (propertize (number-to-string raw-char-count)
                  'face '(:background "gray50" :foreground "black")))
              (one-spacer
                (propertize (char-to-string ?\uE001)
                  'display
                  `((space :width 1))))
              (two-spacers
                (propertize (char-to-string ?\uE001)
                  'display
                  `((space :width 2))))
              (final-char-count
                (cond
                  ((and
                      (< raw-char-count 100)
                      (> raw-char-count 9))
                    (concat one-spacer colored-char-count))
                  ((< raw-char-count 10)
                    (concat two-spacers colored-char-count))
                  (t colored-char-count)))
              (ov-string (concat starting-column final-char-count two-spacers)) )
            (push ov-string char-count-overlay-list)
            (overlay-put (make-overlay pbol pbol) 'before-string ov-string) 
            (when (<= pbol window-start)
              (throw 'done nil)) )))
        (setq char-count-p t)))
     (setq char-count-this-command nil) ))

(defun remove-char-count-overlays ()
  (when char-count-p
    (require 'cl)
    (setq char-count-overlay-list
      (remove-duplicates char-count-overlay-list
        :test (lambda (x y) (or (null y) (equal-including-properties x y)))
        :from-end t))
    (dolist (description char-count-overlay-list)
      (equal-including-properties--remove-overlays (point-min) (point-max) 'before-string description))
    (setq char-count-p nil) ))

(defun turn-off-char-count-mode ()
  (char-count-mode -1))

(define-minor-mode char-count-mode
"A minor-mode that places the character count at the beginning of the line."
  :init-value nil
  :lighter " Char-Count"
  :keymap nil
  :global nil
  :group nil
  (cond
    (char-count-mode
      (setq scroll-conservatively 101)
      (add-hook 'post-command-hook 'char-count-post-command-hook t t)
      (add-hook 'window-scroll-functions
        'character-count-window-scroll-functions t t)
      (add-hook 'change-major-mode-hook 'turn-off-char-count-mode nil t)
      (message "Turned ON `char-count-mode`."))
    (t
      (remove-char-count-overlays)
      (remove-hook 'post-command-hook 'char-count-post-command-hook t)
      (remove-hook 'window-scroll-functions
        'character-count-window-scroll-functions t)
      (remove-hook 'change-major-mode-hook 'turn-off-char-count-mode t)
      (kill-local-variable 'scroll-conservatively)
      (message "Turned OFF `char-count-mode`.") )))

(provide 'char-count)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



回答2:


Here is a quick example of one way to put an overlay after linum-mode numbers and before the line of text. I will need to give some thought about right-alignment of the character count.

NOTE:  This method contemplates that the linum-mode numbers are generated before the code that follows in this example. If the post-command-hook or the widow-scroll-functions hook is used to implement this proposed method, then those additions to the hooks would need to follow in time subsequently to the linum-mode functions attached to those same hooks.

The following example could be implemented with the post-command-hook and the window-scroll-functions hook. See the following link for an example of how to determine window-start and window-end before a redisplay occurs: https://stackoverflow.com/a/24216247/2112489

EDIT:  Added right-alignment of character count -- contemplates a maximum of three digits (i.e., up to 999 characters per line). The text after the character count overlays are now left-aligned.

(save-excursion
  (let* (
      (window-start (window-start))
      (window-end (window-end)))
    (goto-char window-end)
    (while (re-search-backward "\n" window-start t)
      (let* (
          (pbol (point-at-bol))
          (peol (point-at-eol))
          (raw-char-count (abs (- peol pbol)))
          (starting-column
            (propertize (char-to-string ?\uE001)
              'display
              `((space :align-to 1)
                (space :width 0))))
          (colored-char-count
            (propertize (number-to-string raw-char-count)
              'face '(:background "gray50" :foreground "black")
              'cursor t))
          (one-spacer
            (propertize (char-to-string ?\uE001)
              'display
              `((space :width 1))))
          (two-spacers
            (propertize (char-to-string ?\uE001)
              'display
              `((space :width 2))))
          (final-char-count
            (cond
              ((and
                  (< raw-char-count 100)
                  (> raw-char-count 9))
                (concat one-spacer colored-char-count))
              ((< raw-char-count 10)
                (concat two-spacers colored-char-count))
              (t colored-char-count))) )
        (overlay-put (make-overlay pbol pbol)
          'before-string
          (concat starting-column final-char-count two-spacers) )))))


来源:https://stackoverflow.com/questions/24724913/best-way-to-add-per-line-information-visually-in-emacs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!