首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
273 阅读
2
从零开始配置 vim(15)——状态栏配置
161 阅读
3
nvim番外之将配置的插件管理器更新为lazy
145 阅读
4
2018总结与2019规划
138 阅读
5
PDF标准详解(五)——图形状态
117 阅读
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
登录
Search
标签搜索
c++
c
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
emacs
win32
elisp
读书笔记
VimScript
linux
文本编辑器
投资理财
Java
反汇编
OLEDB
数据库编程
Masimaro
累计撰写
403
篇文章
累计收到
32
条评论
首页
栏目
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2026-08-14
《elisp 每日一练》—— overlay 机制
在之前的文章中,我们利用 walk-windows 和 completing-read 实现了一个可以跳转到任意窗口的程序。但是我们通过 buffer + window 的方式进行显示,如果buffer不重名还好,用户可以根据buffer名称来选择,而当buffer有重名,用户没办法直观的看出哪个项对应哪个窗口。如果读者尝试过配置自己的Emacs,在管理窗口方面一定绕不开 ace-window这个插件,它提供了一个非常优雅高效的方式。今天将介绍overlay机制,并且利用该机制实现一个类似的跳转功能。背景知识介绍对于buffer的显示效果,Emacs提供了两种改变的机制,一种是通过设置 Text properties 来改变文本的字号、字体大小等属性,是 buffer 文本的一部分,会随文本一起复制、保存。另一个就是 Overlay 机制,它只影响显示,不进入 undo 列表,也不随文本复制。对于 Overlay 官方给它的定位是:You can use overlays to alter the appearance of a buffer’s text on the screen, for the sake of presentation features. An overlay is an object that belongs to a particular buffer, and has a specified beginning and end.你可以将它想象成一个贴在buffer上的便利贴。如果要使用的话,我们首先要创建一个便利贴、然后决定便利贴的位置,最后在不需要时销毁它。并且Emacs还提供了一些操作它的函数。下面我们来列举一些重要的函数。创建可以使用函数 make-overlay,它的原形如下:(make-overlay BEG END &optional BUFFER FRONT-ADVANCE REAR-ADVANCE)函数的相关参数如下:BEG 和 END : 指定overlay起始位置和终点位置BUFFER : 指定叠加层属于哪个缓冲区, 默认为当前缓冲区FRONT-ADVANCE 和 REAR-ADVANCE 表示当开头或者结尾被插入新字符时,overlay 该如何变化我们来了解一下最后两个参数的含义。我们还是将它想象成一个便利贴。假设便利贴我们从头到尾写满了文字,这个时候我们想要在便利贴开始位置或者结束位置写一个新的内容,该怎么办? FRONT-ADVANCE 和 REAR-ADVANCE 就是来处理这种情况的。一种办法是丢失这些内容,纸张的大小不变,这个时候对应着 FRONT-ADVANCE 为非 nil 而 REAR-ADVANCE 为 nil。还有一种办法就是纸张大小改变,我们在前面和后面插入内容,此时对应着 FRONT-ADVANCE 为 nil 而 REAR-ADVANCE 为非 nil关于属性,我们有 overlay-put 和 overlay-get 来设置或者获取属性。面对一个陌生的,不是我们创建的 overlay,我们可以通过 overlay-start 和 overlay-end 来获取它的起始和结束位置另外我们可以通过 overlays-at 和 overlays-in 来获取区域内的所有 overlay。我们常用的属性有:face 设置样式,包括背景色、前景色、字体等display : 替换overlay中的的显示内容priority : 重叠时的 z序window : 限定只在指定窗口生效overlay 实际是对于 buffer 的修改,所以默认情况下,如果多个窗口都显示同一个buffer,那么每个窗口都会显示这样的内容,我们可以通过 window 属性,限定只在指定的窗口中显示例如我们可以尝试写下如下代码,来创造一个显示内容为 "标记" 的黄低红色的标记,并且通过 window 属性指定只在当前窗口显示(let ((overlay (make-overlay (point-min) (+ 10 (point-min))))) (overlay-put overlay 'window (selected-window)) (overlay-put overlay 'face '(:background "red" :foreground "yellow")) (overlay-put overlay 'display "A"))练习到此我们差不多已经掌握了 overlay 的绝大部分用法了。可以开始我们的改造行动了。我们的思路仍然是先遍历,然后给每个窗口打上标签,并且记录到一个列表中,列表中每项的内容为 (LABLE . window) 后续根据用户输入的字母或者数字来找到窗口并跳转。(defvar my/all-window-list '() "保存窗口字典的列表") (defun my/show-overlay(label win) "在左上角创建显示字母的 overlay" (with-selected-window win (let* ((pos (window-start)) (ol (make-overlay pos (+ 10 pos) (window-buffer win)))) (overlay-put ol 'display label) (overlay-put ol 'face '(:foreground "red" :weight bold :background "yellow")) (overlay-put ol 'window win) ol))) ;; 返回 ol 用于清理 (defun my/jump-to-selected-window() "根据用户按下的字母标签进行跳转" (interactive) (let*((chars (mapcar #'identity "abcdefghijklmnopqrstuvwxyz")) ;; 构建一个字母的字典 (overlays '()) ;; 保存所有的overlay 方便后面删除 (index 0)) ;; 存储当前使用的字母的索引 (walk-windows (lambda(win) (when (< index (length chars)) (let* ((ch (nth index chars)) (ol (my/show-overlay (string ch) win))) (when ol (push ol overlays) (push (cons ch win) my/all-window-list))) (setq index (1+ index))))) ;; 读取用户按键并跳转 (unwind-protect (let* ((char (read-char "选择窗口: ")) (win (cdr (assoc char my/all-window-list)))) (if win (select-window win) (message "没有选择对应的窗口"))) ;;清理资源 (mapc #'delete-overlay overlays) (setq overlays nil))))上述代码中使用了一个新的宏 unwind-protect。它的作用与try...catch 中的 finally 类似,它的定义如下:(unwind-protect body-form cleanup-forms...)它主要执行body-form 中的内容,并且保证会执行 cleanup-forms 中的清理代码。总结本节我们学习了如何给buffer 加上 overlay ,用来展示一些信息。本节介绍的函数有make-overlayoverlay-put 和 overlay-getoverlay-start 和 overlay-endoverlays-at 和 overlays-in
2026年08月14日
0 阅读
0 评论
0 点赞