Skip to content

开箱即用 与 自由定制

约 2658 字大约 9 分钟

emacs

2025-10-03

From 知名开发者 Matt Gemmell 谈 Emacs

Now I am exaggerating a little bit, because of course a default (or at least a well-chosen one) actually is an opinion. An important one. The distinction I’m making is just that emacs takes nothing for granted in terms of its appearance or behaviour; the user is given infinite rope with which to either elevate or entangle themselves. Everything is customisable. Everything.

现在我有点夸张了,因为当然,默认(或者至少是精心挑选的)实际上是一种观点。一个重要的观点。我所说的区别只是 emacs 在它的外观或行为方面从不理所当然;用户被给予无限的绳索,可以用来提升自己或陷入困境。一切都是可定制的。一切。

我觉得作者这一段的观察相当的准确且重要。

”开箱即用” VS “自由定制”

“一切都是可定制的” 带来 “提升自己” 的同时也会让使用者 “陷入困境“,这便是自由的代价。如果插件的开发者没有提供合适的默认值,使用者就需要许多的学习成本来首先了解各个配置项的含义,然后才能很好的使用插件。这种极度自由的配置与其他软件的 “开箱即用” 对比,需要为 “emacs学习曲线陡峭“ 的观点负大部分责任。

但 “开箱即用” 的局限性是明显的:它让用户个性化的偏好变得不可能。用户只能按照开发者的预设的方式来使用功能,或者它可以提供一些简单的配置选项,但无法做到 emacs 那样深度的定制。所以,”开箱即用“ 是否好用非常考验开发者的水平:即该功能如何设计是符合大多数用户的需求和使用习惯的,在减少用户学习成本和心智负担的同时,又很好的满足了功能需求。

”开箱即用” 是可以漂亮的完成特定功能的工具,上手容易,但你几乎很难深入其内部结构,并且工具坏了还得找专门的人去维修。而 emacs 的 “自由定制“ 更像是暴露在外的各种机械结构:链条,齿轮...,你得先知道如何组合和调整它们才能使用,而一旦你学会了这些知识,就可以用它们来打造不局限于特定功能的工具。

如果可以话,作为一个普通用户,两者我都不想要;或者反过来说,两者我都想要。当我还是一个初学者时,我希望工具提供了针对不同场景的开箱即用的功能,它可以满足我绝大部分的需求;随着我经验渐长,有了些更个性的想法,我希望工具也能够提供足够自由的定制供我玩耍。

开箱即用的编辑器几乎做不到足够自由的定制,因为底层模型设计的不够通用(或者商业考虑等原因),它不可能将所有的底层API都暴露给用户进行定制。但 emacs 却完全有可能做到足够省心的开箱即用,但现状却不如人意。

何谓良好的"开箱即用"

我觉得 emacs 插件开箱即用不尽如人意的主要原因是:长期使用 emacs 的用户很多时候也是插件的开发者,即使没有写过插件,至少也写过一些 elisp 来定制功能。这样写出来的插件很多时候是带了“开发者”的视角的,缺少了一些“使用者”的视角。这导致的问题是:有些我们觉得理所当然的设计和交互方式可能对新手并不直观。

良好的"开箱即用"应当可以让用户以尽可能小的代价来达到目的。对于开发者来说就是要将复杂性封装在软件内部,并提供给用户简洁好用的抽象。这种抽象指的是满足不同场景功能的命令、一个直观的操作界面 或 良好的错误提示和建议,甚至是自动化的错误处理等等。

还有一个很重要的因素:默认值。正如作者提到的,默认值需要精心挑选!“开箱即用”意味着用户在没有进行配置或只进行了极少的必要配置时,就可以获得良好的使用体验,这对底层模型及其默认值的挑选有极高的要求。

默认值和复合选项

设置一个默认值限定了软件的一种行为,而不同选项可能有不同的默认值,这样排列组合下来,就会表现为丰富的行为,如何组合对用户来说是头疼的问题。除了给单个选项设置良好的默认值,我觉得给多个选项的不同默认值进行组合是不错的idea。这种良好的组合就形成了开箱即用的不同的场景。

许多插件是只提供了选项含义,让用户来自己定制,用户需要花不少时间来了解不同选项的含义和不同选项组合的行为,这一点都不开箱即用。插件的开发者是对所有选项最了解的人,如果能够预设一些良好的选项的组合,并封装为一个新的复合选项并辅以合适的命名,那么对于使用者就大大的减少了需要了解的选项的个数。如果这些预设的组合设置的足够好,可以覆盖大多数的场景,用户便不需要将其拆开进行更细致的配置就可以获得极好的使用体验。这便是默认值和复合选项的魅力!

一个具体的例子

本着遵循前文核心思想的原则,可以把“抽象的表述”理解为一个"选项",那么“用户对抽象文本的具体理解”就是一个"默认值",那么我当然应该提供一个具体的例子作为开箱即用的“默认值”来降低阅读者的理解负担。

为了开发ETML,最近在阅读CSS的文档,我发现默认值和复合选项的设置在CSS中是广泛应用的。比如单独设置一个 border 属性,其实是设置了 border-width, border-style, border-color 这些子属性,而这些子属性又可以拆分为四个不同方向的子属性 border-left-width, border-left-style, border-left-color... 这样的复合选项的层层封装,让用户在非必要的情况下不必每次都去设置最底层的"具体的特定方向的边框的特定属性的值"。

还有一个例子,我在开发 etml-scroll-bar.el 的时候,为滚动条定义了如下的模型:

etml-scroll-bar 模型
(defclass etml-scroll-bar ()
  ((track-height
    :initarg :thumb-height :initform 1
    :documentation "滚动条轨道高度,根据 content-linum,content-height 计算得到")
   (track-color
    :initarg :track-color :initform nil
    :documentation "滚动条轨道的颜色,仅作用于 padding 部分")
   (track-left-padding
    :initarg: :track-left-padding :initform 0
    :documentation "滚动条轨道的左 padding 像素")
   (track-right-padding
    :initarg: :track-right-padding :initform 0
    :documentation "滚动条轨道的右 padding 像素")
   (track-left-margin
    :initarg :track-left-margin :initform 0
    :documentation "滚动条轨道的左 margin 像素")
   (track-right-margin
    :initarg :track-right-margin :initform 0
    :documentation "滚动条轨道的右 margin 像素")
   (track-left-border-pixel
    :initarg :track-left-border-pixel :initform 0
    :documentation "滚动条轨道左边的边框像素宽度")
   (track-right-border-pixel
    :initarg :track-right-border-pixel :initform 0
    :documentation "滚动条轨道右边的边框像素宽度")
   (track-left-border-color
    :initarg :track-left-border-color :initform nil
    :documentation "滚动条轨道左边的边框颜色")
   (track-right-border-color
    :initarg :track-right-border-color :initform nil
    :documentation "滚动条轨道右边的边框颜色")
   (thumb-offset
    :initarg :thumb-offset :initform 0 :documentation "滚动条初始偏移量")
   (thumb-height
    :initarg :thumb-height :initform 1
    :documentation "滚动条高度,根据 content-linum,content-height 计算得到")
   (thumb-pixel :initarg :thumb-pixel :initform 2
                :documentation "滚动条滑块像素宽度")
   (thumb-border :initarg :thumb-border :initform nil
                 :documentation "滚动条滑块的边框,nil 或颜色")
   (thumb-color :initarg :thumb-color :initform nil
                :documentation "滚动条滑块颜色"))
  "滚动条模型")

如果你对浏览器滚动条的实现不是很了解,自己去实例化这个模型来定义的滚动条可能会比较困难,因为需要先了解(滚动条、轨道等)每个字段的含义和组合之后的效果。但是我会预设一些默认值的组合来命令为不同的风格的滚动条。用户只需要选择更加上层模型中定义不同风格滚动条字段的值就可以了,可能这些风格的滚动条也会暴露几个字段允许用户做一些简单的定制,但是比起设置上面这十几个字段可简单太多了。

总结

总结起来,我觉好的设计应该是对于不同层次的使用者都能有好的体验。简单有简单的用法,复杂有复杂的用法。emacs 已经具备了复杂的能力,如何让它变得"简单"就显得很重要。毕竟作为开发者,不可能只使用自己写的插件,在使用别人插件的时候,也希望上手能够容易一些;而对于刚刚接触 emacs 的新手用户,前期的简单就决定了是否会继续使用 emacs。

相较于纯文本的环境,现代用户更习惯于有一个良好布局和交互的界面来使用各种功能,因为它简单、直观。如果 emacs 中的插件都是以类似桌面或手机app、网页的方式呈现出来的,我相信未来 emacs 的学习曲线会平缓很多。这就是我开发 ETAF 的原因。