Vompeccc案例研究:Emacs中的Spotify纯ICR应用
A Vompeccc Case Study: Spotify as Pure ICR in Emacs

原始链接: https://www.chiply.dev/post-vompeccc-spot

`spot` 包利用 `Marginalia` 库在 Spotify 搜索结果旁边显示丰富的元数据,而无需编写复杂的格式化代码。每个搜索结果(曲目、专辑等)都附带艺术家、时长和发行日期等信息,这些信息通过 `Marginalia` 以视觉对齐的列呈现。 核心函数 `spot--annotate-track` 只是*声明* Spotify API 中的哪些数据字段应该出现在每一列,并分配特定的样式。`Marginalia` 通过其 `marginalia--fields` 宏处理所有实际的格式化——对齐、截断和着色。 `spot` 定义了七个这样的注释器,每个注释器都针对不同的 Spotify 数据类型进行了定制。它们通过一个简单的列表注册到 `Marginalia`,将数据类别(如“专辑”或“曲目”)链接到各自的注释器函数。 关键在于,这种分离意味着 `spot` 不需要管理显示逻辑,并且可以“免费”受益于 `Marginalia` 的功能,例如由 `Orderless` 库启用的可搜索注释。这展示了一种强大的“底层借用”——利用现有库以最少的自定义代码实现复杂的功能。

这篇 Hacker News 帖子重点介绍了一个由 chiply 提供的案例研究,详细介绍了使用名为 VOMPECCC 的系统在 Emacs 中*完全*创建 Spotify 客户端 ("spot") 的过程。VOMPECCC 是围绕“增量补全读取”(ICR) 概念构建的八个 Emacs 包的集合。 Chiply 认为 ICR 不仅仅是一个用户界面特性,而是强大界面的一种基本结构元素,而 Emacs 独特地允许可编程补全。 这篇文章是该系列的一部分,探讨了这个想法,之前的文章已经建立了理论基础并概述了 VOMPECCC 包。 最新一期展示了 VOMPECCC 的实际应用,通过展示 Spotify 客户端背后的代码,证明了利用 Emacs 的补全功能作为核心基础来构建复杂应用程序是可行的。 一条用户评论建议为脚注添加“返回”按钮可以改善体验。
相关文章

原文

If you watch the video carefully, each track in the candidate list is followed by a horizontally aligned column of fields: #<track-number>, artist, a M:SS duration, album name, album type, release date. Each field is rendered to a fixed width in its own face, so numbers and dates and names land as visually distinct columns rather than getting mashed together with a delimiter. Small glyph prefixes (# for counts, for popularity, for followers) disambiguate otherwise bare numbers. That column is provided by Marginalia, and it comes from one function:

(defun spot--annotate-track (cand)
  "Annotate track CAND with number, artist, duration, album, type, and date.
The track number is prefixed with `#' and duration rendered as M:SS."
  (let ((data (get-text-property 0 'multi-data cand)))
    (marginalia--fields
     ((spot--format-count (ht-get data 'track_number))
      :format "#%s" :truncate 5 :face 'spot-marginalia-number)
     ((spot--annotation-field (spot--first-name (ht-get data 'artists)))
      :truncate 25 :face 'spot-marginalia-artist)
     ((spot--format-duration (ht-get data 'duration_ms))
      :truncate 7 :face 'spot-marginalia-number)
     ((spot--annotation-field (ht-get* data 'album 'name))
      :truncate 30 :face 'spot-marginalia-album)
     ((spot--annotation-field (ht-get* data 'album 'album_type))
      :truncate 8 :face 'spot-marginalia-type)
     ((spot--annotation-field (ht-get* data 'album 'release_date))
      :truncate 10 :face 'spot-marginalia-date))))

The first line is the only plumbing: (get-text-property 0 'multi-data cand) pulls the full Spotify API response off the candidate (exactly the hash table spot--propertize-items stashed earlier), and everything after it is Marginalia's own marginalia--fields macro doing the formatting. marginalia--fields handles the alignment, the per-field truncation, and the face application. The only thing my code does is declare which fields of the Spotify payload go in which columns with which faces. This is another substrate borrow hiding in plain sight: Marginalia registers the annotator and formats its output. I never wrote a single character of alignment, padding, or colourisation logic. The annotator reached into multi-data for its fields, Marginalia's macro did the cosmetic work, and Marginalia never had to know about Spotify's data model.

spot ships seven annotators. Each one is a domain-specific projection of a single Spotify response type onto a display string. Albums surface artist, release date, and track count, artists surface popularity and follower count, shows surface publisher, media type, and episode count; and all this context is really important, especially if you are 'browsing'. The annotators are independent of the search code, independent of the actions code, and independent of each other.

Registering them with Marginalia is three lines of bookkeeping:

(defvar spot--marginalia-annotator-entries
  '((album spot--annotate-album none)
    (artist spot--annotate-artist none)
    (playlist spot--annotate-playlist none)
    (track spot--annotate-track none)
    (show spot--annotate-show none)
    (episode spot--annotate-episode none)
    (audiobook spot--annotate-audiobook none))
  "List of marginalia annotator entries registered by spot.")

(defun spot--setup-marginalia ()
  "Register spot annotators with marginalia."
  (dolist (entry spot--marginalia-annotator-entries)
    (add-to-list 'marginalia-annotators entry)))

The spot--marginalia-annotator-entries list keys on the category symbol (album, artist, and so on), the very same symbols the Consult sources stamp onto their candidates. Marginalia looks up the category of the current candidate in marginalia-annotators, finds the entry, and runs the annotator. No spot code is in that path. I only had to declare the mapping.

This is where one of the most interesting benefits of the second post shows up concretely. That post mentioned that because Marginalia annotations are themselves searchable, Orderless's @ dispatcher lets you match against annotation text. spot did not ship this feature. Orderless and Marginalia did, for free, because I stamped the annotation onto the candidate in the right way.

联系我们 contact @ memedata.com