调整缩进与第 2、3、4 …个参数对齐?

一般缩进跟第一个参数对齐(如下,4、6 跟 1 对齐,而不是 2 或 3),这绝大多数是想要的结果

(+ 1 2 3
   4 5
   6)

但是我有时候想对齐比的位置,比如底下的 [...] 跟上面的对齐,而不是第一个参数:

;; 自动缩进得到的:
(new frame% [label "opengl test"]
     [width 400]
     [height 400])

;; 我想要的:
(new frame% [label "opengl test"]
            [width 400]
            [height 400])

另外一个更加常见的例子是 Keyword 参数,Emacs Lisp 也有同样问题:

(open-output-file "output.txt" #:mode 'text
                  #:exists 'truncate)

(open-output-file "output.txt" #:mode 'text
                               #:exists 'truncate)

有没有命令手动的调整缩进位置的?比如假设光标在 d 上,写一个命令依次在下面三种情况中循环。

(foo a b c
     d)

(foo a b c
       d)

(foo a b c
         d)
2 个赞

同求,同求!

估计很多人都有类似需求,但是好像真的没有专门针对这个功能的工具。有个比较类似的:

parinfer 比较有意思,利用缩进去改变数据结构,感觉在 parinfer 的基础上实现这个功能不是很难。

本群的一位同学写了一个类似的包

@cireu 写了一个可定制性很强的包 ( 用sly-el-indent获得更好的Elisp缩进 ), 不知能否支持方括号的缩进。

我也写过一个比较丑的缩进包 ( Emacs Lisp 模式下 If 缩进出问题 ),支持 :& 对齐,也支持简单的定制,但是我刚刚试过了方括号不行。


UPDATE:

我那个包改一个函数就可以支持方括号缩进了,原因是 [ 刚好是个特殊字符,直接拿来拼接正则会出错😅,转义一下就行了:

diff --git a/lisp-keyword-indent.el b/lisp-keyword-indent.el
index af42402..9abfe50 100644
--- a/lisp-keyword-indent.el
+++ b/lisp-keyword-indent.el
@@ -79,7 +79,7 @@ strip text properties from the return value. "
                (string-match-p
                 (concat
                  "\\`\\(?:"
-                 (mapconcat 'identity prefixes "\\|")
+                 (mapconcat 'regexp-quote prefixes "\\|")
                  "\\)")
                 sexp))
       sexp)))

添加自定义规则:

(add-to-list 'lisp-keyword-indent-rules
             '("[" . (:multiple-value nil :value-offset 0)))

测试:

(insert
 (with-temp-buffer
   (insert "\
(new frame% [label \"opengl test\"]
[width 400]
[height 400])")
   (emacs-lisp-mode)
   (goto-char (point-min))
   (dotimes (_ 2)
     (forward-line) (call-interactively 'indent-for-tab-command))
   (concat "\n" (buffer-string))))
;; =>
;; (new frame% [label "opengl test"]
;;             [width 400]
;;             [height 400])

Repo (已更新): https://github.com/twlz0ne/lisp-keyword-indent.el

2 个赞

看上去是在写 Racket(最近也看到你在 racket-mode 和 racket 的 github repo 上活动了😂)

DrRacket 里有设置一个 form 缩进的选项。Racket-mode 也有(虽然我没用过,也是在别人的 emacs 配置里看到的):racket-indent-function


不过感觉这个问题,可以上升到语言层面,怎么在语言层面(定义 form 的时候)可以描述这个 form 在编辑器里的缩进呢?感觉有这样一种机制,会很棒。

Elisp有(declare (indent N)) ,具体用起来可以参考

https://www.gnu.org/software/emacs/manual/html_node/elisp/Indenting-Macros.html#Indenting-Macros

展开含有indent delcaration的宏/函数定义可以得到

(defmacro prog3 (1st 2nd 3rd &rest forms)
  (declare (indent 3))
  `(progn ,1st (prog2 ,2nd ,3rd ,@forms)))

(prog1
    (defalias 'prog3
        (cons 'macro
              (function
               (lambda
                   (1st 2nd 3rd &rest forms)
                `(progn ,1st
                        (prog2 ,2nd ,3rd ,@forms))))))
  (function-put 'prog3 'lisp-indent-function '3))

实际上起作用的是

(function-put 'prog3 'lisp-indent-function '3)

所以“描述缩进”这个行为还要具体的编辑器支持,一门通用用途的语言不太适合含有这种依赖于不同编辑器实现的规范

1 个赞

主页上的演示动画本身也很有意思,是有专门用来制作录屏效果的 js 库吗?

确实有意思,就是 gif 图片吧。不过parinfer 好像已经不维护了。

不是 gif 图片,是用 gears.d3.js 渲染出来的效果,但是回放的“脚本”是 parinfer 作者自己写的:

嗯,对,这个页面确实是的,有意思。

我看的是主页上的 https://camo.githubusercontent.com/142ec87e9c4ab1863c3dc4c26807f6bca4ef3f5b/687474703a2f2f7a697070792e6766796361742e636f6d2f57656972644f6464426c756566696e74756e612e676966。

我自己写了个命令,自动改缩进位置,概念验证:

Feb-19-2020 10-31-32

(defun chunyang-indent-align-cycle ()
  "手动对齐参数位置."
  (interactive)
  (let ((beg (condition-case nil
                 (scan-lists (point) -1 1)
               (scan-error nil))))
    (when beg
      (let (columns cur-col target-col offset)
        (save-excursion
          (forward-line -1)
          (goto-char (line-end-position))
          (let ((lb (line-beginning-position)))
            (while (let ((pt (ignore-errors (scan-sexps (point) -1))))
                     (and pt (> pt lb) (goto-char pt)))
              (push (- (point) lb) columns))))
        (when columns
          (back-to-indentation)
          (setq cur-col (- (point) (line-beginning-position)))
          (or (catch 'done
                (dolist (c columns)
                  (when (> c cur-col)
                    (setq target-col c)
                    (throw 'done t))))
              (setq target-col (car columns)))
          (setq offset (- target-col cur-col))
          (cond
           ((> offset 0) (insert (make-string offset ?\s)))
           ((< offset 0) (delete-char offset))))))))

对齐确实是个恼人的事情。最烦人的是,即使在你环境上对齐很完美了,到其他人的环境下如果没有用相同配置一编辑又乱掉了,每次 merge 都得 revert 一堆。

1 个赞

我觉得还是让编辑器明确知道缩进规则比较好。你用了对齐循环,就得小心 indent-region 了。

可以在 .dir-local.el 设置好缩进规则,甚至强制要求安装额外的 lint / indent 包。但是不像 node 可在项目本地安装包,强制的作法可能引起反感,而且也没实质的约束力。

对啊,如果是内置的就没有这些烦恼了。自己的项目还好办,其他人的项目就无能为力了。

columns中的元素保存点参数信息就好了

比如说这个参数是字符串,这个参数是keyword

这样就可以根据某个偏好设置,让他自动对齐