Roife
1
我在用 VS Code / NeoVim 的时候,一直觉得他们的滚动条装饰功能非常好用(在滚动条上添加标记,例如诊断信息等):
Emacs 人习惯在 mode-line 上放各种 indicator:diagnostics,isearch,smerge,marks 用来标记这个文件有哪些 indicator 起效,但是在我看来无论在美观度还是功能性层面,都是滚动条装饰更好:
- modeline 非常拥挤,使用滚动条装饰可以缓解其压力
- 滚动条装饰能在一片区域展示多个信息,减少了信息获取的成本(只需要瞄一眼就能看到全部indicators)
- 滚动条装饰提供了更多信息:例如 滚动条的长度暗示了文件的大小,滚动条的位置暗示了所处文件的位置(由此可以删掉 modeline 上的文件大小提示和文件位置提示);isearch 的结果集中在哪片区域,Git 状态显示了文件的哪一个部分被修改……
但是目前 emacs 的 scrollbar 使用了系统默认的图形框架,不能实现这个,我想知道如果想要这个功能的话,有什么好的实现机制吗
2 个赞
Kinney
2
如果有识别滚动的hook的话,可以实现,可以参考 [POC] Emacs buffer 文本块滚动效果实现 。本质是上是在每次滚动时更新滚动条的文本属性。
Roife
3
为什么滚动条有文本属性,是用 fringer 实现的吗
Kinney
4
就是在一个空格字符上设置 display 的 space属性,再反转一下颜色。放在哪里都可以,本质都是文本。
org
5
Kinney
7
具体我没研究过,放在行尾似乎不行,你说的fringe应该可以。性能开销还好。每次只改变滚动条开始和结尾位置的文本属性(改个颜色)基本没什么开销。你可以看看etaf滚动的效果是不是你想要的,如果是的话就可以实现。
org
8
为啥你觉得emacs的侧边没有类似的显示?eglot/bookmark 其实都有的,关键看你想做什么,如果是标记就fringes,如果文本多就margin。
Roife
9
关键是这几个东西都是和“行”绑定的,每次滚动都要刷新位置
org
10
感觉minimap之类符合你的想法,不过要改造成你想要的形状。
yibie
12
利用 right-margin 不知道是否可以,这是 AI 提供的一个 MVP 代码:
;; file: scroll-decor.el
(defgroup scroll-decor nil
"Scrollbar-like decorations using right margin."
:group 'convenience)
(defface scroll-decor-viewport '((t :background "#666666"))
"Face for current viewport indicator.")
(defface scroll-decor-diagnostic '((t :background "#e57373"))
"Face for Flymake diagnostics.")
(defface scroll-decor-isearch '((t :background "#64b5f6"))
"Face for isearch hits.")
(defvar-local scroll-decor--overlays nil)
(defvar scroll-decor-margin-width 1)
(defun scroll-decor--set-margin ()
(set-window-margins (selected-window)
(car (window-margins))
scroll-decor-margin-width))
(defun scroll-decor--clear ()
(mapc #'delete-overlay scroll-decor--overlays)
(setq scroll-decor--overlays nil))
(defun scroll-decor--add (line face)
(let* ((pos (save-excursion (goto-char (point-min))
(forward-line (max 0 (1- line)))
(point)))
(ov (make-overlay pos pos)))
(overlay-put ov 'scroll-decor t)
(overlay-put ov 'priority 10000)
(overlay-put ov 'before-string
(propertize " " 'display
`(margin right-margin
,(propertize " " 'face face))))
(push ov scroll-decor--overlays)))
(defun scroll-decor--line-count ()
(max 1 (count-lines (point-min) (point-max))))
(defun scroll-decor--normalize (pos)
"Map buffer position POS to a line number along the window height."
(let* ((total (scroll-decor--line-count))
(line (line-number-at-position pos t))
(win-lines (max 1 (window-body-height)))
(y (max 1 (min win-lines
(round (* (/ (float line) total) win-lines))))))
y))
(defun scroll-decor--viewport ()
(let* ((start (window-start))
(end (window-end nil t)))
(list (scroll-decor--normalize start)
(scroll-decor--normalize end))))
(defun scroll-decor--collect-flymake ()
(when (bound-and-true-p flymake-mode)
(mapcar (lambda (d) (flymake--diag-beg d))
(flymake-diagnostics))))
(defun scroll-decor--collect-isearch ()
(when (and isearch-mode isearch-string)
(save-excursion
(goto-char (point-min))
(let (hits)
(while (search-forward isearch-string nil t)
(push (match-beginning 0) hits))
hits))))
(defun scroll-decor--render ()
(scroll-decor--clear)
(scroll-decor--set-margin)
;; viewport bar(用起止两点各画一个,视觉上是顶/底两个刻度)
(pcase-let ((`(,y1 ,y2) (scroll-decor--viewport)))
(scroll-decor--add y1 'scroll-decor-viewport)
(scroll-decor--add y2 'scroll-decor-viewport))
;; diagnostics
(dolist (pos (scroll-decor--collect-flymake))
(scroll-decor--add (scroll-decor--normalize pos) 'scroll-decor-diagnostic))
;; isearch hits(可做采样以避免太密集)
(dolist (pos (scroll-decor--collect-isearch))
(scroll-decor--add (scroll-decor--normalize pos) 'scroll-decor-isearch)))
(defun scroll-decor--update (_win _start)
(scroll-decor--render))
;;;###autoload
(define-minor-mode scroll-decor-mode
"Simulate VSCode-like scrollbar decorations in right margin."
:lighter " ⎯"
(if scroll-decor-mode
(progn
(add-hook 'window-scroll-functions #'scroll-decor--update nil t)
(add-hook 'post-command-hook #'scroll-decor--render nil t)
(add-hook 'flymake-diagnostic-functions
(lambda (&rest _) (scroll-decor--render)) nil t)
(scroll-decor--render))
(remove-hook 'window-scroll-functions #'scroll-decor--update t)
(remove-hook 'post-command-hook #'scroll-decor--render t)
(scroll-decor--clear)))
holo-layer 插件应该可以实现的比VSCode还要帅气