【问题标题】:How to configure indentation in emacs lua-mode?如何在 emacs lua-mode 中配置缩进?
【发布时间】:2011-06-06 07:19:18
【问题描述】:

在这里完成 emacs 新手。

我在 Ubuntu 上使用 emacs 23.1.1 和 emacs starter kit。我主要在 lua 模式下工作(使用package-install lua-mode 安装)。

我需要调整缩进的工作方式,使其符合我的编码指南。

指导方针是:

  • 制表符到空格;
  • 每个缩进两个空格;
  • 每行最多 80 个字符,没有尾随空格。

例子:

本地 foo = 函数() print("你好,世界!") 结尾

如果我不尝试与它的自动缩进作斗争,我会用 emacs 得到什么:

本地 foo = 函数() 打印(“你好,世界”) 结尾

更新:

(这属于注释,但由于需要额外的格式,所以我必须放在这里。)

如果我尝试 Thomas 的解决方案,我会得到:

本地 foo = 函数() 打印(“你好,世界”) 结尾

请注意,end制表符 和四个空格缩进。 不太好用……

更新 2:

这件事也以错误的方式缩进:

本地酒吧 = foo( “一”, “二”, baz(), -- 注意三个空格 “现状” )

应该是:

本地酒吧 = foo( “一”, “二”, 巴兹(), “现状” )

更新 3:

错误缩进的第三种情况:

本地酒吧 = foo( “一”, “二” ) local t = 5 -- 此行不应缩进, -- 还要注意 local 和 t 之间的选项卡。

更新 4:

这是我从 Thomas 那里得到的当前版本:

本地 foo = 函数() 打印(“你好,世界”) 结尾 local bar = 5 -- Emacs 把 \t 放在 5 之前 local zzz = foo( -- Emacs 把 \t 放在 foo 之前 "one", -- 在此处按 TAB 两次 “二”, 三(), “四” )

除了明确指出,我没有做任何缩进,只是输入代码并在每一行的末尾按 RETURN。我实际上并没有输入任何 cmets。

它应该如下所示:

本地 foo = 函数() 打印(“你好,世界”) 结尾 当地酒吧 = 5 本地 zzz = foo( “一”, “二”, 三(), “四” )

更新 5:

还有一个错误的缩进情况:

本地富= { 酒吧(); -- 在这里按了一个 TAB,但右括号杀死了它 巴兹; }

应该是:

本地富= { 酒吧(); 巴兹; }

更新 6:

为了完整起见,这是我使用 current Git HEAD of lua-mode 得到的,没有 Thomas 的配置调整:

本地 foo = 函数() print("你好,世界!") 结尾 当地酒吧 = 5 本地 foo = bar( 酒吧, 巴兹(), 现状(), 啊啊啊 ) 本地 t = { “一”, 二(), }

调整:

本地 foo = 函数() print("你好,世界!") 结尾 当地酒吧 = 5 本地 foo = bar( 酒吧, 巴兹(), 现状(), 啊啊啊 ) 本地 t = { “一”, 二(), }

为了符合我的编码指南,它应该如下所示:

本地 foo = 函数() print("你好,世界!") 结尾 当地酒吧 = 5 本地 foo = bar( 酒吧, 巴兹(), 现状(), 啊啊啊 ) 本地 t = { “一”, 二(), }

【问题讨论】:

  • @Alexander Gladysh:当您编辑 lua 文件时,您是否处于 lua 模式?模式行(缓冲区底部的灰线)是否显示(Lua ...)
  • @Alexander Gladysh:请注意,您的更新 2(“应该是”)不符合您自己的上述规范/指南,其中您声明缩进应该是 2 个空格:在更新 2 , foo 的参数缩进为 4 个空格。
  • @Thomas: -UU-:**--F1 test.lua Top L41 (Lua Abbrev)
  • @Thomas:不,那是函数参数的两个缩进(一个右括号)。
  • @Alexander Gladysh:好的,那很好。 indent-line-function 的值是多少?您可以通过输入C-h v 来查找,然后输入“indent-line-function”回车。

标签: emacs lua lua-mode


【解决方案1】:

我认为您可以在emacs manual on custom C indentation definitions 中找到很多您正在寻找的内容,它属于一般indentation engine 描述。

你可以让它做任何你能想象的事情,这比只做你想象的任何事情要好。

【讨论】:

  • 这个答案不正确,自定义C缩进不会影响lua-mode
【解决方案2】:

如果您将以下代码输入到您的主目录中的.emacs 文件中,它将使 lua-mode(并且只有 lua-mode)的行为方式如下:

  • 如果按 ENTER,将插入一个换行符,默认情况下下一行将像上一行一样缩进。
  • 每当您按 TAB 缩进该行时,point 或者跳转到该行的第一个非空白字符,或者,如果该行是空的,point 已经在该字符处,则插入两个空格。

尤其是后者可能不是您想要的,但可能是第一个近似值。

(defvar my-lua-indent 2
  "The number of spaces to insert for indentation")

(defun my-lua-enter ()
  "Inserts a newline and indents the line like the previous
non-empty line."
  (interactive)
  (newline)
  (indent-relative-maybe))

(defun my-lua-indent ()
  "Moves point to the first non-whitespace character of the
line if it is left of it. If point is already at that
position, or if it is at the beginning of an empty line,
inserts two spaces at point."
  (interactive)
  (when (looking-back "^\\s *")
    (if (looking-at "[\t ]")
        (progn (back-to-indentation)
               (when (looking-at "$")
                 (kill-line 0)
                 (indent-relative-maybe)
                 (insert (make-string my-lua-indent ? ))))
      (insert (make-string my-lua-indent ? )))))

(defun my-lua-setup ()
  "Binds ENTER to my-lua-enter and configures indentation the way
I want it. Makes sure spaces are used for indentation, not tabs."
  (setq indent-tabs-mode nil)
  (local-set-key "\r" 'my-lua-enter)
  (setq indent-line-function 'my-lua-indent))

;; add `my-lua-setup' as a call-back that is invoked whenever lua-mode
;; is activated.
(add-hook 'lua-mode-hook 'my-lua-setup)

重新启动 Emacs 以使这些更改生效。

【讨论】:

  • 因为我用的是emacs starter kit,所以这个合适的地方是~/.emacs.d/<my-username>.el
  • @AG:我更新了缩进,对我来说效果很好。您能否编辑您的原始问题以解释什么不适合您?
【解决方案3】:

我现在帮不上什么忙 - 我有两天后的最后期限 8-( - 但是 这是我在 .emacs 中用来使 lua-mode 对我可用的东西...

(setq lua-indent-level 2)
(setq lua-electric-flag nil)
(defun lua-abbrev-mode-off () (abbrev-mode 0))
(add-hook 'lua-mode-hook 'lua-abbrev-mode-off)
(setq save-abbrevs nil)   ;; is this still needed?

我以一种不寻常的方式缩进我的代码 - 请参阅下面的示例 - 所以 我已经训练自己只在 lua-mode 可以推断出时按 TAB 从上面的行正确缩进...

map = function (f, A, n)
    local B = {}                 -- TAB here doesn't work
    for i=1,(n or #A) do         -- TAB here works
      table.insert(B, f(A[i]))   -- TAB here works
    end                          -- TAB here works
    return B                     -- TAB here works
  end                            -- TAB here works

【讨论】:

  • 谢谢!我想得到一个完整的修复。如果一定要和编辑打架,还是用我习惯打架的吧。
【解决方案4】:

好的,我们再试一次……浏览了lua-mode的源码后,我想出了以下方法。

公认奇怪的默认缩进的原因是一个名为“lua-calculate-indentation”的函数,它计算当前行缩进到的列。不幸的是,它返回的值与您所需的规格不匹配。

例如,如果您在新的 .lua 文件中输入一行,如下所示:

local foo = function()

按回车键将点移动到第二行,您可以通过键入M-: (lua-calculate-indentation) 调用上述函数。结果是 15,这意味着 lua-mode 会将第二个缩进到第 15 列。这就是您在原始问题中描述和举例说明的非正统缩进的原因。

现在,为了解决这个问题,我建议重新定义函数“lua-calculate-indentation”,以便它返回您想要的缩进。为此,请将以下代码放入一个空文件中,并将其以名称“my-lua.el”保存在“lua-mode.el”所在的同一目录中。

;; use an indentation width of two spaces
(setq lua-indent-level 2)

;; Add dangling '(', remove '='
(setq lua-cont-eol-regexp
      (eval-when-compile
        (concat
         "\\((\\|\\_<"
         (regexp-opt '("and" "or" "not" "in" "for" "while"
                       "local" "function") t)
         "\\_>\\|"
         "\\(^\\|[^" lua-operator-class "]\\)"
         (regexp-opt '("+" "-" "*" "/" "^" ".." "==" "<" ">" "<=" ">=" "~=") t)
         "\\)"
         "\\s *\\=")))

(defun lua-calculate-indentation (&optional parse-start)
  "Overwrites the default lua-mode function that calculates the
column to which the current line should be indented to."
  (save-excursion
    (when parse-start
      (goto-char parse-start))

    ;; We calculate the indentation column depending on the previous
    ;; non-blank, non-comment code line. Also, when the current line
    ;; is a continuation of that previous line, we add one additional
    ;; unit of indentation.
    (+ (if (lua-is-continuing-statement-p) lua-indent-level 0)
       (if (lua-goto-nonblank-previous-line)
           (+ (current-indentation) (lua-calculate-indentation-right-shift-next))
         0))))

(defun lua-calculate-indentation-right-shift-next (&optional parse-start)
  "Assuming that the next code line is not a block ending line,
this function returns the column offset that line should be
indented to with respect to the current line."
  (let ((eol)
        (token)
        (token-info)
        (shift 0))
    (save-excursion
      (when parse-start
        (goto-char parse-start))

      ; count the balance of block-opening and block-closing tokens
      ; from the beginning to the end of this line.
      (setq eol (line-end-position))
      (beginning-of-line)
      (while (and (lua-find-regexp 'forward lua-indentation-modifier-regexp)
                  (<= (point) eol)
                  (setq token (match-string 0))
                  (setq token-info (assoc token lua-block-token-alist)))
        ; we found a token. Now, is it an opening or closing token?
        (if (eq (nth 2 token-info) 'open)
            (setq shift (+ shift lua-indent-level))
          (when (or (> shift 0)
                    (string= token ")"))
            (setq shift (- shift lua-indent-level))))))
    shift))

此代码将缩进级别设置为两个空格(而不是 3),修改了一个正则表达式以检测语句是否跨越多行,最后使用辅助重新定义缩进函数。

剩下要做的就是确保实际加载了此代码。这必须发生在 加载原始 lua 模式之后,否则该代码将重新安装原始缩进功能。

我们这样做的方式有点笨拙:我们安装了一个回调函数,每次缓冲区将其主模式更改为 lua 模式时都会调用该函数。然后它检查是否定义了前面提到的辅助函数——如果没有,它加载“my-lua.el”。这有点脆弱,但只要你不玩lua源代码,你应该没问题。

将以下行添加到您的 ~/emacs.d/agladysh.el 文件中(假设“agladysh”是您的用户名):

(add-hook 'lua-mode-hook 
          (lambda () (unless (fboundp 'lua-calculate-indentation-right-shift-next)
                       (load-file (locate-file "my-lua.el" load-path)))))

我假设 lua-mode 在您的加载路径上,如果您按照 lua-mode 的安装说明进行操作,它应该是。

希望这次对你有用,如果没有,请告诉我。

【讨论】:

  • 谢谢!我应该将它与您第一个答案中的代码一起使用,还是应该删除它?
  • 现在效果更好(在没有您第一个答案的代码的情况下尝试了它)。仍然不完美,因为它不会自动缩进表构造函数中的函数参数和代码,但是,至少,当我手动缩进它们时它不会与我作对。再次感谢您!
  • 这段代码独立于我之前的答案。如果通过“自动缩进函数参数”,您的意思是在您按 ENTER 后该点将跳转到正确的列,那么您可以将它与我的其他答案中的代码结合起来。我应该编辑这个答案来合并它吗?
  • 这通常运作良好 - 谢谢。需要进行哪些更改才能使其适用于表格? IE。用{ 打开一个表,但下一行缩进到与{ 相同的列,而不是缩进2 个空格。
  • @meowsqueak 最新版本的 lua-mode (github.com/immerrr/lua-mode) 似乎合理地缩进了 IMO 表格,即表格的内容是左对齐的。这不是你想要的吗?
【解决方案5】:

我是 lua-mode.el 的维护者(但不是作者)。由于我在 Emacs Lisp 方面的流利程度远低于该线程的其他贡献者,因此我欢迎使用补丁。我只想指出,默认规则并没有什么奇怪或不正确的地方:据我所知,这个想法只是当你在一个匿名函数中时,缩进应该将 function 关键字作为它的左边距。当您考虑在其他地方使用函数表达式时,这是有道理的,例如作为函数参数。

所以,一个简单的解决方法是不写

局部 f = 函数...

但是

局部函数f...

除非您正在使用早于“本地函数”语法的 Lua 版本。

话虽如此,我明白为什么你可能想要不同的缩进。在这种情况下,我觉得有一个配置变量 lua-indent-function-from-function-keyword 是合理的(更好的名字,有人吗?),我很乐意接受实现它的补丁。

【讨论】:

  • @RT:嗨,鲁本,感谢光临。我并不是说缩进很“奇怪”,但上面 AG 提供的示例确实以某种非正统的方式缩进。 (请注意,AG 的问题包括除了具有 `function' 的情况之外的其他情况。)但这就是编辑器的好处,它是如此可配置,每个人都可以让它按照自己的喜好行事......
  • @RT:确实感谢您的光临。我碰巧遵循的编码指南对local function f() 语法皱眉。
  • @Thomas:我还没有遇到过广泛颁布的 Lua 格式化标准,所以讨论什么是“正统”可能是徒劳的。我再说一遍:我欢迎补丁。我允许我自己的编码标准以 lua-mode 的功能为指导;我有幸不必遵循外部强加的标准。
  • @RT:关于“(非)正统”,你是对的。我应该更仔细地选择我的措辞。
  • @RT:我不知道 lua,所以我会犹豫提交真正的补丁。更具体地说,我的回答是一个针对特定编码准则的小技巧。不知道这对其他人有多大用处。
【解决方案6】:

我知道这个问题已经有一段时间了,但我只是想指出这仍然是一个问题,通过 Emacs 包系统安装了 lua-mode。

但是,GitHub 上的最新版本运行良好,没有发现任何缩进怪异。要符合Lua style guide,您只需将indent-tabs-mode 设置为nil 并将lua-indent-level 设置为2

【讨论】:

    【解决方案7】:

    一个更简洁的方法是added in 2019,以两个lua-indent- 变量的形式。这让我们几乎到了那里,但由于某种原因它仍然使嵌套块双缩进。添加一点建议技巧即可完成工作。

    (setq lua-indent-nested-block-content-align nil)
    (setq lua-indent-close-paren-align nil)
    
    (defun lua-at-most-one-indent (old-function &rest arguments)
      (let ((old-res (apply old-function arguments)))
        (if (> old-res lua-indent-level) lua-indent-level old-res)))
    
    (advice-add #'lua-calculate-indentation-block-modifier
                :around #'lua-at-most-one-indent)
    

    【讨论】:

      最近更新 更多