Emacs笔记之修复yaml-mode的格式化问题


更新时间 备注
2023-02-26 增加更多判断逻辑, 比如上一行是列表,当前行也是列表,则按正常缩进计算

一直以来 yaml-mode 都有一个问题,在使用 (indent-region (point-min) (point-max)) 时已经格式好的内容总是会得到错误的缩进,比如一个简单的配置

test1: "val"
test2:
  - "val1"
  - "val2"
test3:
  test4: "val4"

在调用 indent-regin 后就会变成

test1: "val"
test2:
  - "val1"
  - "val2"
  test3:
    test4: "val4"

而我又总是手痒,写好的配置文件总是会忍不住 indent-region 一番,结果本来正确的格式立马变得各种乱七八糟,当初为了解决这个问题,还专门为 yaml-mode 激活了 lsp-mode, 并且使用 lsp-format-buffer 来进行处理。

本来一切都还正常,但最近发现了另外一个问题,如果在 org-mode 中插入 yaml 的代码片段,调用 indent-region 还是还有之前的问题, org-mode 中又无法直接调用 lsp-format-buffer, 所以专门研究了一下 yaml-mode

发现 yaml-mode 只是定义了 indent-line-function 变量,没有定义 indent-region-function, 导致调用 indent-region 时其实是逐行调用 yaml-indent-line, 这里面有一个计算当前缩进的 yaml-compute-indentation 函数,计算方式有些简单粗暴,如果上一行是一个 hash key, 当前行的缩进就是上一行的缩进加 yaml-indent-offset 的值, 这里并没有判断当前行是新建行还是已经有相关内容,如果是新建行那么缩进就是对的,如果是已有的内容就是错的,所以我修改了这一部分逻辑,如果是 已有内容并且当前行的缩进小于上一行 则不进行处理, 比如:

  - "val2"
test3:

虽然增加的判断逻辑很简单,但至少在使用 indent-region 时已经格式好的内容就不会出现混乱的情况了, 也不用专门为 yaml 文件激活一个 lsp 进程。

目前处理的方式还是采用 advice + letf 形式,否则需要修改很多内容,有兴趣的同道可以参考一下

(defun maple/yaml-compute-indentation()
  (let ((ci (current-indentation))
        (cm (looking-at yaml-hash-key-re))
        (cl (looking-at "^\s*-\s+.*$")))
    (save-excursion
      (beginning-of-line)
      (if (looking-at yaml-document-delimiter-re) 0
        (forward-line -1)
        (while (and (looking-at yaml-blank-line-re)
                    (> (point) (point-min)))
          (forward-line -1))
        (let ((li (current-indentation))
              (ll (looking-at "^\s*-\s+.*$")))
          ;; 如果上一行的缩进大于当前行, 上一行是列表,当前行也是是列表 - 正常缩进
          ;; 如果上一行的缩进大于当前行, 上一行和当前行有一个不是列表 - 原有缩进
          ;; 如果上一行的缩进等于当前行, 上一行是字典,当前行是列表 - 正常缩进
          ;; 如果上一行的缩进等于当前行, 上一行是字典,当前行是不是列表 - 原有缩进
          ;; 如果上一行的缩进小于当前行, 上一行是字典 - 正常缩进
          ;; 如果上一行的缩进小于当前行, 上一行是列表,当前行也是列表 - 正常缩进
          ;; 如果上一行的缩进小于当前行, 上一行是列表,当前行是字典 - 错误/正常缩进
          (if (or (and (> li ci) (or (not cl) (not ll))) (and (= li ci) cm (not ll))) ci
            (+ li
               (if (looking-at yaml-nested-map-re) yaml-indent-offset 0)
               (if (looking-at yaml-nested-sequence-re) yaml-indent-offset 0)
               (if (looking-at yaml-block-literal-re) yaml-indent-offset 0))))))))

(defun maple/yaml-indent-region(func &rest args)
  (if (derived-mode-p 'yaml-mode)
      (letf (((symbol-function 'yaml-compute-indentation) 'maple/yaml-compute-indentation))
        (apply func args))
    (apply func args)))

(advice-add 'indent-region :around 'maple/yaml-indent-region)
作者: honmaple
链接: https://honmaple.me/articles/2023/02/Emacs笔记之修复yaml-mode的格式化问题.html
版权: CC BY-NC-SA 4.0 知识共享署名-非商业性使用-相同方式共享4.0国际许可协议
wechat
alipay

加载评论