PRQL 语法高亮:在 GNU Emacs 中配置 prql-mode 的完整指南
2026/9/23 15:26:45 网站建设 项目流程
  • 后端

【免费下载链接】prql

PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement

项目地址:https://gitcode.com/gh_mirrors/pr/prql
点击查看免费下载

PRQL(Pipelined Relational Query Language)是一种面向数据转换的现代语言,致力于以简单、强大的管道式语法替代 SQL。要在 GNU Emacs 中舒适地编写.prql查询文件,官方仓库提供了基于 Font Lock 机制实现的prql-mode主模式。本指南以仓库 grammars/emacs/README.md 与 prql-mode.el 为核心,完整介绍该模式的安装步骤、语法高亮分类规则、数字与字符串词法,以及它与 prqlc 官方词法分析器之间的对应关系,帮助你快速搭建起可用的 PRQL 编辑环境。

一、安装 prql-mode:从复制文件到激活主模式

官方文档给出的安装流程非常简洁,共分两步:先把模式文件放到 Emacs 的 load-path 中,再在init.el里注册文件扩展名与主模式的关联。

1. 放置 prql-mode.el

将仓库中的 grammars/emacs/prql-mode.el 复制到 Emacs 的自定义模式目录:

~/.emacs.d/custom-modes/

如果该目录尚不存在,可先创建。文件复制到位后,Emacs 才能通过require加载它。

2. 配置 init.el 并注册自动模式

编辑~/.emacs.d/init.el,添加以下内容(与官方文档一致):

(add-to-list 'load-path "~/.emacs.d/custom-modes/") (require 'prql-mode) (add-to-list 'auto-mode-alist '("\\.prql\\'" . prql-mode))
  • add-to-list 'load-path将自定义模式目录加入加载路径,使require能检索到prql-mode.el
  • require 'prql-mode加载该文件并执行其中的define-derived-modeprovide声明;
  • add-to-list 'auto-mode-alist.prql扩展名映射到prql-mode主模式。注意这里的正则写法"\\.prql\\'"\\'是 Emacs 正则中的"字符串结尾"锚点,表示只匹配以.prql结尾的文件名。

保存并重启 Emacs(或执行M-x eval-buffer重新加载init.el)后,打开任意.prql文件即可看到语法高亮自动生效;也可在已打开的文件中通过M-x prql-mode手动切换。

二、prql-mode 的实现解剖:一个基于 Font Lock 的主模式

prql-mode.el 只有约 74 行,却完整覆盖了主模式所需的全部要素:语法表、高亮关键词列表、字体锁定(font-lock)规则和派生模式定义。下面按源码顺序逐层拆解。

1. 语法表:定义注释语法

(defvar prql-mode-syntax-table (let ((table (make-syntax-table))) ;; Define comment syntax (modify-syntax-entry ?# "<" table) (modify-syntax-entry ?\n ">" table) table) "Syntax table for `prql-mode'.")

对应 prql-mode.el:#被定义为注释起始符(<),换行符被定义为注释结束符(>),这与 PRQL 的#单行注释语法完全一致——官方词法分析器 lexer/mod.rs 中同样将#识别为注释。因此#之后到行尾的内容会以注释样式显示。

2. 六类高亮关键词列表

prql-mode 通过defconst定义了六组被高亮的词法单元,这是整个模式的"词表核心":

变量内容对应 face源码位置
prql-constantstruefalsethisthatnullfont-lock-constant-faceprql-mode.el#L21-L23
prql-data-typesboolfloatintint8int16int32int64textdatetimetimestampfont-lock-type-faceprql-mode.el#L25-L27
prql-builtin-functionsaggregatederivefilterfromgroupjoinselectsorttakewindowfont-lock-builtin-faceprql-mode.el#L29-L31
prql-operators!=&&\|\|+-*/%<>~=font-lock-builtin-faceprql-mode.el#L33-L37
prql-numbers0o[0-7]+0x[0-9a-fA-F]+0b[01]+[0-9]+font-lock-constant-faceprql-mode.el#L39-L44
prql-other-keywordsletintocaseprqltypemoduleinternalfuncimportenumfont-lock-keyword-faceprql-mode.el#L46-L48

其中几个细节值得注意:

  • this/that被列入常量:这是 PRQL 中引用当前行(this)与目标行(that)的特殊标识符;
  • 内建函数列表恰好对应 PRQL 的十大转换(transform):fromselectfilterderiveaggregatesorttakejoingroupwindow
  • 数字正则覆盖了八进制、十六进制、二进制和普通十进制四类字面量,与词法分析器测试 lexer/test.rs 中验证的0b11110000111100000xff0o777等用例相互印证。

3. 组装 font-lock 关键词规则

(defvar prql-font-lock-keywords `( ,(cons (regexp-opt prql-constants 'words) 'font-lock-constant-face) ,(cons (regexp-opt prql-other-keywords 'words) 'font-lock-keyword-face) ,(cons (regexp-opt prql-builtin-functions 'words) 'font-lock-builtin-face) ,(cons (regexp-opt prql-data-types 'words) 'font-lock-type-face) ,@(mapcar (lambda (kw) (cons kw 'font-lock-builtin-face)) prql-operators) ,@(mapcar (lambda (kw) (cons kw 'font-lock-constant-face)) prql-numbers)))

对应 prql-mode.el#L50-L58。实现要点:

  • 对单词类列表调用regexp-opt自动生成高效的交替正则,并启用'words选项保证按"整词"匹配,避免from误伤from_text之类的子串(Emacs 的regexp-opt默认会按最长词优先排序匹配,配合词边界语义达到精确匹配);
  • 运算符与数字正则直接以字符串形式逐条加入规则列表,不经过regexp-opt
  • 数字正则0o[0-7]+等未加词边界断言,在实际使用中可能出现前缀匹配的边角情况,这是该模式已知的简化处理。

4. 派生主模式与缩进设置

(define-derived-mode prql-mode prog-mode "PRQL" "Major mode for editing PRQL code." (set-syntax-table prql-mode-syntax-table) (setq font-lock-defaults '((prql-font-lock-keywords))) (setq indent-tabs-mode nil) (setq tab-width 2))

对应 prql-mode.el#L60-L68:

  • prog-mode派生,天然继承编程语言的通用行为(如注释高亮基础设施、语法表层次结构);
  • font-lock-defaults指向上面组装好的关键词规则,(setq font-lock-defaults '((prql-font-lock-keywords)))这种"以列表包裹列表"的写法是 Font Lock 的标准传参方式;
  • indent-tabs-mode设为nil表示缩进使用空格而非制表符,tab-width设为 2,即 PRQL 惯用的 2 空格缩进风格(与官方格式化的默认缩进一致);
  • 文件末尾的(provide 'prql-mode)使require能够正确完成加载。

三、与官方词法分析器的对应关系:语法高亮为何与编译器一致

PRQL 仓库刻意保持了各编辑器语法定义与 prqlc 词法分析器的同步。在 prqlc/prqlc-parser/src/lexer/mod.rs 中,词法分析器将以下 10 个词保留为TokenKind::Keyword

pub const KEYWORDS: [&str; 10] = [ "let", "into", "case", "prql", "type", "module", "internal", "func", "import", "enum", ];

并保留字面量:

pub const RESERVED_LITERALS: [&str; 3] = ["true", "false", "null"];

对照可见,prql-mode.el中的prql-other-keywordsKEYWORDS逐项完全一致prql-constants中的true/false/null也与RESERVED_LITERALS吻合(this/that则是高亮模式额外覆盖的语义标识符)。这意味着:凡是关键字,都会同时被编译器保留和编辑器高亮——不会出现"语法高亮为关键词、但编译器当作普通标识符"的误导。源码注释也明确指出,grammars/目录下的各语法文件需要手工跟随这份词表(参见 lexer/mod.rs 的文档注释)。

仓库 grammars/README.md 的索引进一步说明,PRQL 为十余种编辑器/工具维护了语法定义(CotEditor、GtkSourceView、KSyntaxHighlighting、nano、Raku、Lezer 等),其中 emacs 模式由 grammars/emacs/ 目录承载;而 GtkSourceView 的 prql.lang 中同样以"Keep in sync with the lexer'skeywordparser in prqlc/prqlc-parser/src/lexer/mod.rs"的注释标注了这份同步关系,CotEditor 的 PRQL.yaml 也引用了同一处词法分析器。这从侧面印证了 emacs 模式词表设计的可靠性。

四、进阶配置与常见问题

1. 调整缩进与配色

prql-mode默认使用 2 空格缩进、不使用制表符。若你偏好其他风格,可在加载模式后通过setq-local覆盖:

(add-hook 'prql-mode-hook (lambda () (setq-local tab-width 4) (setq-local indent-tabs-mode t)))

注意indent-tabs-modet时缩进将改用制表符,但 PRQL 编译器对空白并不敏感(换行才是语句边界),因此该调整不会影响编译。

2. 缩进能力的边界

prql-modeprog-mode派生但未定义indent-line-function,这意味着目前它只提供语法高亮,不提供智能缩进——按 Tab 仍使用 Emacs 默认行为。如需更完整的编辑体验,可自行通过add-hook接入其他缩进方案,或以 grammars/prql-lezer/ 等更完整的解析器语法作为参考。

3. 关键词与扩展名覆盖范围

  • 模式只对以.prql结尾的文件自动启用;auto-mode-alist也可额外添加,例如同时覆盖大写扩展名或特定目录下的 PRQL 文件;
  • 若打开文件时未出现高亮,请依次检查:prql-mode.el是否在 load-path 目录中、init.elrequire是否位于add-to-list 'auto-mode-alist之前、以及缓冲区是否已切换到该主模式(C-h m可查看当前模式信息)。

五、小结

grammars/emacs/README.md 虽只有一份简洁的安装说明,但配合 prql-mode.el 的完整实现,你可以得到一套开箱即用的 PRQL 高亮方案:#注释、十大数据类型、十大转换函数、运算符、四类数字字面量以及全部保留关键字均按语义分类着色,且词表与 prqlc 词法分析器保持严格同步。对于以 Emacs 为主要编辑环境的 PRQL 用户而言,这份不到百行的模式文件是目前最轻量的官方高亮实现,安装即用,无需额外依赖。

  • 后端

【免费下载链接】prql

PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement

项目地址:https://gitcode.com/gh_mirrors/pr/prql
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询