1. 项目概述:一个为Emacs打造的现代光标库

如果你和我一样,是个深度Emacs用户,每天有超过一半的时间都泡在这个“神的编辑器”里,那你一定对那个闪烁的、小小的矩形块——光标——又爱又恨。爱它,是因为它是指引我们编辑位置的唯一灯塔;恨它,是当我们在不同模式下切换,或者长时间盯着屏幕时,那个一成不变的、刺眼的默认光标样式,实在是有些乏味,甚至会引起视觉疲劳。

今天要聊的,就是Emacs社区里一个解决这个“痛点”的利器: protesilaos/cursory 。这不仅仅是一个简单的“换皮肤”插件。在资深用户 protesilaos (社区里常称他为“Prot”)的手中,它被塑造成了一个高度可定制、模块化、且能与现代Emacs配置(尤其是 use-package 声明式配置)无缝集成的 光标样式管理框架 。它的核心价值在于,将光标从一个静态的、系统级的设定,解放为一个可以根据编辑上下文(模式、主题、甚至个人状态)动态变化的视觉元素。

简单来说, cursory 让你能轻松做到:在普通编辑时使用细竖线,在只读缓冲区换成方块,在 org-mode 的标题行使用下划线,在深夜使用深色主题时自动切换为更柔和、不闪烁的光标样式。它把光标的控制权,从零散的、写在 .emacs 文件角落里的几行 set-cursor-color blink-cursor-mode 命令,升级为一套声明式的、可维护的配置系统。对于追求极致个性化工作环境和视觉舒适度的Emacs用户来说,这绝对是一个“用了就回不去”的工具。

2. 核心设计哲学:声明式配置与预设管理

cursory 的设计思路非常清晰,它深受现代Emacs配置哲学的影响,尤其是 use-package 宏所倡导的声明式与懒加载理念。它没有重新发明轮子去绘制光标,而是作为Emacs内置光标变量(如 cursor-type , blink-cursor-mode , blink-cursor-blinks , blink-cursor-interval 等)的一个 高级、统一的配置管理层

2.1 预设(Presets)的概念

这是 cursory 最核心的抽象。一个“预设”就是一个包含了完整光标样式参数的集合。你可以把它想象成一个光标“主题包”。 cursory 自身就附带了一系列精心设计的预设,涵盖了从经典到现代的多种风格。

例如,一个预设可能包含以下定义:

(setq cursory-presets
      '((box
         :blink-cursor-interval 0.8
         :blink-cursor-blinks 10
         :cursor-type (box . 2)
         :cursor-in-non-selected-windows hollow)
        (bar
         :blink-cursor-interval 0.5
         :blink-cursor-blinks -1 ; 常亮不闪烁
         :cursor-type (bar . 2)
         :cursor-in-non-selected-windows (bar . 1))
        (underscore
         :blink-cursor-interval 0.7
         :cursor-type (hbar . 3))
        ))
  • box : 一个宽度为2像素的实心方块光标,在非选中窗口显示为空心框,以0.8秒的间隔闪烁10次后保持常亮。
  • bar : 一个宽度为2像素的竖线光标,在非选中窗口显示为1像素宽的竖线,并且完全不闪烁( -1 表示无限闪烁,但间隔为0.5秒,实际上由于 blink-cursor-mode 可能被禁用,表现为常亮)。
  • underscore : 一个高度为3像素的下划线光标。

注意 blink-cursor-blinks 参数设置为 -1 时,结合 blink-cursor-interval ,其行为取决于 blink-cursor-mode 是否启用。有时为了实现真正的“不闪烁”,更可靠的做法是直接 (blink-cursor-mode -1) cursory 的预设通常会处理好这些细节。

2.2 声明式配置与 use-package 集成

cursory 鼓励并完美支持在 use-package 声明中完成所有配置。这是它与许多需要你在 init.el 里到处调用函数的插件不同的地方。这种方式的优势在于:

  1. 配置集中化 :所有相关设置都在一个 use-package 块内,清晰可管理。
  2. 懒加载与性能 :可以方便地配置 :defer t 等参数,让 cursory 在真正需要时才加载。
  3. 依赖管理 :自动处理与 modus-themes (Prot开发的另一套卓越的主题系列)或其他包的依赖和配置顺序。

一个典型的配置看起来非常简洁和直观:

(use-package cursory
  :ensure t
  :config
  ;; 设置默认预设
  (setq cursory-default-preset 'bar)
  ;; 可选:设置备用预设列表,用于`cursory-cycle-preset`命令轮换
  (setq cursory-presets (list 'bar 'box 'underscore))
  ;; 加载默认预设
  (cursory-set-preset cursory-default-preset))

这短短几行,就完成了包的安装、默认样式设定、以及初始化。这种优雅正是Emacs高阶用户所追求的。

2.3 动态上下文感知

cursory 的另一个强大之处在于其“上下文感知”能力。它可以通过Emacs的钩子(hooks)机制,在特定事件发生时自动切换预设。最常见的应用场景是 随主题切换而改变光标

例如,如果你使用 modus-themes ,你可以这样配置:

(use-package cursory
  :ensure t
  :config
  (setq cursory-default-preset 'bar)
  ;; 当加载任何Modus主题时,自动切换到‘box’预设
  (setq cursory-modus-themes-preset 'box)
  (cursory-set-preset cursory-default-preset))

这样,当你执行 (modus-themes-load-theme 'modus-operandi) (亮色主题)或 modus-vivendi (暗色主题)时, cursory 会自动将光标样式从默认的 bar 切换为 box 预设。这种联动极大地提升了视觉一致性,避免了亮色主题下深色光标不显眼,或暗色主题下亮色光标太刺眼的问题。

3. 安装、配置与深度定制实操

理论说再多,不如动手配一遍。下面我将带你从零开始,配置一个功能完整且个性化的 cursory 环境。

3.1 安装与基础配置

假设你已使用 package.el (如 straight.el , elpaca 或内置的 package )作为包管理器。安装非常简单。

步骤一:通过 use-package straight.el 安装

(use-package cursory
  :ensure t
  :straight (:host github :repo “protesilaos/cursory”)
  :config
  ;; 立即进行基础配置
  )

如果你用的是其他管理器, :ensure t 通常就够了。

步骤二:选择并设置默认预设 首先,查看 cursory 提供了哪些内置预设。最方便的方法是使用 M-x cursory-set-preset 然后按 TAB 键补全,你会看到一个列表。常见的有:

  • box
  • bar
  • underscore
  • beam (类似I-beam文本光标)
  • block (更大的方块)
  • box-no-blink (不闪烁的方块)

在配置中设定默认预设:

(setq cursory-default-preset 'box) ; 我喜欢方块
(cursory-set-preset cursory-default-preset) ; 立即生效

步骤三:配置预设循环列表 为了方便快速切换,可以定义一个预设循环列表。之后通过命令 cursory-cycle-preset (可以绑定到快捷键)在这些预设间轮换。

(setq cursory-presets '(box bar underscore beam))

3.2 创建自定义预设

内置预设虽好,但自定义才是乐趣所在。创建自定义预设有两种主要方式。

方式一:直接覆盖或扩展 cursory-presets 变量

(setq cursory-presets
      (append cursory-presets ; 保留内置预设
              '((my-box
                 :blink-cursor-interval 0.6
                 :blink-cursor-blinks 20
                 :cursor-type (box . 3) ; 3像素宽的实心盒
                 :cursor-in-non-selected-windows (box . 1) ; 非活动窗口用1像素框
                 :cursor-color “#FF6B6B”) ; 自定义光标颜色!
                (my-thin-bar
                 :blink-cursor-mode nil ; 彻底关闭闪烁
                 :cursor-type (bar . 1)
                 :cursor-in-non-selected-windows nil) ; 非活动窗口不显示光标
                )))
(setq cursory-default-preset 'my-box)
(cursory-set-preset cursory-default-preset)

这里的关键是 :cursor-type 参数,它的值是一个 (形状 . 尺寸) 的cons cell。形状可以是 box , hbar , bar , hollow 等。尺寸是数字。 :cursor-color 可以直接覆盖Emacs的 cursor-color 变量,实现彩色光标。

方式二:使用 cursory-define-preset 宏(推荐) 这是更模块化、更清晰的方式,尤其当你有复杂配置时。

(cursory-define-preset my-adaptive-cursor
  “一个根据背景色自动调整颜色的光标预设”
  :blink-cursor-interval 0.75
  :cursor-type (box . 2)
  :cursor-in-non-selected-windows hollow
  :on-set (lambda ()
            (let ((bg (face-background ‘default nil ‘default)))
              ;; 简单逻辑:背景色较暗时,光标用亮色,反之亦然。
              (if (color-dark-p bg)
                  (set-cursor-color “#E0E0E0”)
                (set-cursor-color “#2E3440”)))))

cursory-define-preset 宏会自动将新预设添加到 cursory-presets 列表中。 :on-set 是一个钩子函数,在应用该预设时被执行,这里我们用它实现了光标的自适应颜色。

3.3 高级功能:模式钩子与条件切换

这才是 cursory 发挥威力的地方。我们可以让光标根据编辑模式智能变化。

场景一:在 org-mode prog-mode 中使用不同的光标

;; 定义专门用于编程的光标
(cursory-define-preset prog-cursor
  “编程用的细竖线光标,不闪烁,专注代码”
  :blink-cursor-mode nil
  :cursor-type (bar . 1)
  :cursor-in-non-selected-windows nil)

;; 定义专门用于Org写作的光标
(cursory-define-preset org-cursor
  “Org模式用的下划线光标,区分标题和内容”
  :blink-cursor-interval 0.8
  :cursor-type (hbar . 2))

;; 添加模式钩子
(add-hook ‘prog-mode-hook
          (lambda () (cursory-set-preset ‘prog-cursor)))
(add-hook ‘org-mode-hook
          (lambda () (cursory-set-preset ‘org-cursor)))

;; 注意:需要确保在离开这些模式时能切回默认预设。
;; 一个简单的办法是使用`cursory`的全局默认预设,并为特定模式覆盖它。
;; 或者,为`text-mode-hook`等更通用的钩子设置回默认值。

场景二:根据时间自动切换预设(配合 circadian ts 等包)

(use-package ts) ; 一个时间日期库
(defun my/cursory-preset-by-time ()
  “根据一天中的时间切换光标预设。”
  (let ((hour (ts-hour (ts-now))))
    (cond
     ((and (>= hour 6) (< hour 18)) ; 白天
      (cursory-set-preset ‘bar))
     (t ; 夜晚
      (cursory-set-preset ‘box-no-blink)))))
;; 可以将其添加到`after-init-hook`,或使用定时器每小时运行一次。
(run-at-time “00:00” 3600 ‘my/cursory-preset-by-time) ; 每小時檢查一次

3.4 与主题系统的深度集成

如前所述,与 modus-themes 的集成是开箱即用的。对于其他主题,如 doom-themes , zenburn , solarized 等,你需要手动设置 cursory-themes-custom-alist

(setq cursory-themes-custom-alist
      ‘((doom-one . box) ; 加载`doom-one`主题时用`box`预设
        (doom-dracula . bar)
        (solarized-light . underscore)
        (zenburn . my-box))) ; 也可以使用自定义预设

cursory 会在 enable-theme-functions 这个钩子中检查当前加载的主题是否在这个alist中,如果是,则应用对应的光标预设。这确保了切换主题时视觉风格的统一。

4. 实战排坑与性能调优心得

用了几年 cursory ,踩过一些坑,也总结出一些让体验更丝滑的技巧。

4.1 常见问题与解决方案

问题一:预设切换后,光标样式没有立即生效,或者部分缓冲区生效了,部分没有。

  • 原因 :Emacs的光标变量有些是全局的(如 cursor-type ),有些是缓冲区局部的(buffer-local)。 cursory 在应用预设时,通常只影响当前缓冲区和后续新建的缓冲区。已经存在的、且处于非窗口选中状态的缓冲区可能没有被更新。
  • 解决方案
    1. 确保你在配置中调用了 (cursory-set-preset ‘preset-name) ,而不仅仅是设置了变量。
    2. 如果你希望强制刷新所有已存在缓冲区,可以写一个小函数:
      (defun my/cursory-refresh-all-buffers ()
        “在所有缓冲区中应用当前cursory预设。”
        (interactive)
        (dolist (buf (buffer-list))
          (with-current-buffer buf
            (when (derived-mode-p ‘prog-mode ‘text-mode ‘org-mode) ; 只针对特定模式
              (cursory-set-preset (cursory-get-preset))))))
      
    3. 更根本的,检查是否有其他插件或你的配置在模式钩子中 覆盖 cursor-type 等变量。使用 C-h v cursor-type 查看其文档,确认是否是缓冲区局部变量,以及当前值是什么。

问题二:光标闪烁频率( blink-cursor-interval )设置无效。

  • 原因 blink-cursor-interval 是一个 为单位的浮点数,但Emacs内部计时器可能有最小间隔限制。另外, blink-cursor-mode 必须处于启用状态( (blink-cursor-mode 1) )。
  • 解决方案
    1. 在预设中明确设置 :blink-cursor-mode t
    2. 检查是否有其他配置(如桌面环境设置、终端模拟器设置)覆盖了Emacs的光标闪烁行为。在GUI框架下(如GTK+, NS),Emacs的控制力更强;在终端里,有时终端模拟器的设置优先级更高。
    3. 尝试将间隔设置为一个合理的值,如0.5到1.5之间。太快(如0.1)可能不被支持,太慢则感觉不到闪烁。

问题三:自定义光标颜色在终端中不显示。

  • 原因 :绝大多数终端模拟器不支持改变光标颜色,这是终端的限制,不是Emacs或 cursory 的锅。光标颜色通常由终端主题决定。
  • 解决方案
    1. 接受现实 :在终端中使用Emacs时,忽略 :cursor-color 设置。
    2. 条件判断 :在自定义预设的 :on-set 钩子中,通过 (display-graphic-p) 判断是否在图形界面下,再决定是否设置颜色。
      (cursory-define-preset my-smart-cursor
        :cursor-type (box . 2)
        :on-set (lambda ()
                  (when (display-graphic-p)
                    (set-cursor-color “#FFA726”))))
      

4.2 性能与兼容性考量

cursory 本身非常轻量,它只是对Emacs内置变量的封装和管理。性能开销可以忽略不计。但在复杂配置下,需要注意:

  1. 钩子爆炸 :避免在大量模式钩子中调用 cursory-set-preset ,尤其是那些频繁触发的钩子(如 post-command-hook )。这会导致不必要的重复设置和轻微卡顿。最佳实践是只在主要的模式钩子( prog-mode-hook , text-mode-hook , org-mode-hook )和主题切换钩子中使用。
  2. 配置加载顺序 :如果你的配置中同时有主题包和 cursory ,确保 cursory 在主题包之后加载,或者正确设置了 cursory-themes-custom-alist 。否则可能出现主题加载了,但光标预设没跟上的情况。利用 use-package :after 关键字可以很好地管理依赖。
    (use-package cursory
      :ensure t
      :after modus-themes ; 确保在modus-themes之后加载和配置
      :config ... )
    
  3. evil (Vim模拟)的兼容性 evil 模式会大量操作光标状态(正常模式、插入模式、可视模式)。 cursory 与其配合良好。你甚至可以针对 evil 的不同状态设置不同的光标预设,这需要更精细的钩子,例如 evil-insert-state-entry-hook , evil-normal-state-entry-hook 等。社区有一些相关配置,可以搜索参考。

4.3 我的个人配置片段分享

最后,分享一段我目前正在使用的、相对完整的 cursory 配置。它体现了声明式配置、上下文感知和自定义预设的结合。

(use-package cursory
  :ensure t
  :after (modus-themes doom-themes) ; 在我喜欢的主题之后加载
  :config
  ;; 1. 定义自定义预设
  (cursory-define-preset my/day-bar
    “白天使用的细竖线,不闪烁”
    :blink-cursor-mode nil
    :cursor-type (bar . 1)
    :cursor-in-non-selected-windows (bar . 0.5))

  (cursory-define-preset my/night-box
    “夜晚使用的柔和方块,慢速闪烁”
    :blink-cursor-interval 1.2
    :blink-cursor-blinks 15
    :cursor-type (box . 2)
    :cursor-in-non-selected-windows hollow
    :cursor-color “#88C0D0”) ; Nord主题中的冰蓝色

  (cursory-define-preset my/org-writing
    “Org模式专用,下划线光标”
    :blink-cursor-interval 0.9
    :cursor-type (hbar . 2)
    :cursor-in-non-selected-windows nil)

  ;; 2. 设置主题关联
  (setq cursory-themes-custom-alist
        ‘((modus-operandi . my/day-bar)
          (modus-vivendi . my/night-box)
          (doom-one . my/night-box)
          (doom-dracula . my/night-box)))

  ;; 3. 设置模式钩子
  (add-hook ‘org-mode-hook
            (lambda () (unless (derived-mode-p ‘org-src-mode) ; 排除org代码块
                         (cursory-set-preset ‘my/org-writing))))

  ;; 4. 设置默认预设(作为回退)
  (setq cursory-default-preset ‘my/day-bar)
  (cursory-set-preset cursory-default-preset)

  ;; 5. 快捷键绑定:快速轮换预设
  (global-set-key (kbd “C-c c p”) ‘cursory-cycle-preset)
  (global-set-key (kbd “C-c c d”) (lambda () (interactive) (cursory-set-preset ‘my/day-bar)))
  (global-set-key (kbd “C-c c n”) (lambda () (interactive) (cursory-set-preset ‘my/night-box)))
  )

这个配置实现了:

  • 主题联动 :使用 modus-operandi (亮色)时自动用细竖线,使用暗色主题时自动用柔和的冰蓝色方块。
  • 模式感知 :在 org-mode 中写作时,切换到下划线光标(但进入 org-src 代码块编辑时保持原样)。
  • 快速切换 :绑定了快捷键来手动轮换或直接切换到特定预设。
  • 清晰的预设定义 :每个预设的目的和参数一目了然。

光标,这个编辑器中最微小的元素,在 protesilaos/cursory 的赋能下,从一个静态的配置点,变成了一个可以动态响应环境、模式和个人偏好的交互界面。它可能不会直接提高你的编码效率,但它能显著提升长时间使用Emacs时的视觉舒适度和愉悦感。这种对细节的打磨,正是Emacs文化中“打造属于自己的编辑器”这一精神的完美体现。花一点时间配置它,你会发现那个跟随你指尖跳动的小小光标,从此有了温度和个性。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐