简体   繁体   English

如何在 emacs lua-mode 中配置缩进?

[英]How to configure indentation in emacs lua-mode?

Complete emacs newbie here.在这里完成 emacs 新手。

I'm using emacs 23.1.1 on Ubuntu with emacs starter kit .我在 Ubuntu 上使用 emacs 23.1.1 和emacs starter kit I primarily work in the lua-mode (installed with package-install lua-mode ).我主要在 lua 模式下工作(使用package-install lua-mode )。

I need to tune how indentation works, so it would match my coding guidelines.我需要调整缩进的工作方式,使其符合我的编码指南。

The guidelines are:指导方针是:

  • tabs-to-spaces;制表符到空格;
  • two spaces per indent;每个缩进两个空格;
  • 80 chars per line maximum, without trailing spaces.每行最多 80 个字符,没有尾随空格。

Example:示例:

local foo = function()
  print("Hello, world!")
end

What I get with emacs if I don't try to fight with its auto-indent:如果我不尝试与它的自动缩进作斗争,我会用 emacs 得到什么:

local foo = function()
               print("Hello, world")
end

Update:更新:

(This belongs to a comment, but since it needs extra formatting, I have to place it here.) (这属于评论,但因为需要额外的格式,所以我必须把它放在这里。)

If I try solution by Thomas, I get this:如果我尝试 Thomas 的解决方案,我会得到:

local foo = function()
               print("Hello, world")
        end

Note that end is indented with a tab and four spaces.请注意, end缩进一个制表符和四个空格。 Does not quite work...不太行...

Update 2:更新 2:

This thing is also gets indented in the wrong way:这件事也以错误的方式缩进:

local bar = foo(
    "one",
    "two",
   baz(), -- Note three spaces
   "quo"
)

It should be:应该是:

local bar = foo(
    "one",
    "two",
    baz(),
    "quo"
  )

Update 3:更新 3:

Third case of the wrong indentation:错误缩进的第三种情况:

local bar = foo(
    "one",
    "two"
  )

  local t = 5 -- This line should not be indented, 
              -- also note tab between local and t.

Update 4:更新 4:

Here is what I get with the current version from Thomas:以下是我从 Thomas 那里得到的当前版本:

local foo = function()
               print("Hello, world")
        end

            local bar = 5 -- Emacs put \t before 5

            local zzz = foo( -- Emacs put \t before foo
                "one", -- Pressed TAB here twice
                "two",
               three(),
               "four"
            )

Except where explicitly noted, I did not do anything for indentation, only typed in the code and pressed RETURN at the end of each line.除了明确指出的地方,我没有做任何缩进,只是输入代码并在每行末尾按回车键。 I did not actually type any comments.我实际上没有输入任何评论。

It should look as follows:它应该如下所示:

local foo = function()
  print("Hello, world")
end

local bar = 5

local zzz = foo(
    "one",
    "two",
    three(),
    "four"
  )

Update 5:更新 5:

One more wrong indentation case:另一个错误的缩进情况:

local foo =
{
bar(); -- Did press a TAB here, but closing brace killed it
baz;
}

Should be:应该是:

local foo =
{
  bar();
  baz;
}

Update 6:更新 6:

For the sake of completeness, here is what I get with the current Git HEAD of lua-mode , without Thomas's configuration tuning:为了完整起见,这里是我使用当前 lua-mode 的 Git HEAD 得到的,没有 Thomas 的配置调整:

local foo = function()
               print("Hello, world!")
            end

local bar = 5

local foo = bar(
bar,
   baz(),
   quo(),
aaa
)

local t =
{
"one",
two(),
}

With tuning:带调谐:

local foo = function()
           print("Hello, world!")
            end

            local bar = 5

            local foo = bar(
            bar,
               baz(),
               quo(),
               aaa
            )

            local t =
            {
            "one",
            two(),
         }

To match my coding guidelines, it should look as follows:为了符合我的编码指南,它应该如下所示:

local foo = function()
  print("Hello, world!")
end

local bar = 5

local foo = bar(
    bar,
    baz(),
    quo(),
    aaa
  )

local t =
{
  "one",
  two(),
}

Okay, let's give this another try... After browsing through the source code of lua-mode, I've come up with the following approach.好吧,让我们再试一次…… 浏览了 lua-mode 的源代码后,我想出了以下方法。

The reason for the admittedly strange default indentation is a function called "lua-calculate-indentation" which computes the column to which to indent the current line.公认奇怪的默认缩进的原因是一个名为“lua-calculate-indentation”的函数,它计算当前行缩进的列。 Unfortunately, the values returned by it do not match your desired specification.不幸的是,它返回的值与您想要的规格不匹配。

For instance, if you enter a single line into a fresh .lua file like this one:例如,如果您在新的 .lua 文件中输入一行,如下所示:

local foo = function()

and hit enter to move the point to the second line, you can invoke the above function by typing M-: (lua-calculate-indentation) .并按回车键将点移动到第二行,您可以通过键入M-: (lua-calculate-indentation)来调用上述函数。 The result is 15, which means that lua-mode will indent the second to column 15. This is the reason for the unorthodox indentation you've described and exemplified in your original question.结果是 15,这意味着 lua-mode 会将第二个缩进到第 15 列。这就是您在原始问题中描述和举例说明的非正统缩进的原因。

Now, to fix this I suggest re-defining the function "lua-calculate-indentation" so that it returns the indentation you want.现在,为了解决这个问题,我建议重新定义函数“lua-calculate-indentation”,以便它返回您想要的缩进。 For this, put the following code into an otherwise empty file, and save it under the name "my-lua.el" in the same directory where "lua-mode.el" lives.为此,将以下代码放入一个空文件中,并将其保存在“lua-mode.el”所在目录中的“my-lua.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))

This code sets the indentation level to two spaces (instead of 3), modifies a regular expression that detects if a statement stretches over multiple lines, and finally redefines the indentation function using an auxiliary.这段代码将缩进级别设置为两个空格(而不是 3 个),修改一个正则表达式来检测语句是否延伸到多行,最后使用辅助函数重新定义缩进函数。

All that's left to do is make sure this code is actually loaded.剩下要做的就是确保实际加载此代码。 That must happen after the original lua-mode is loaded, or else that code would re-install the original indentation function.这必须加载原始 lua-mode之后发生,否则该代码将重新安装原始缩进功能。

The way we do that here is a little hacky: we install a call-back function that is invoked each time a buffer changes its major-mode to lua-mode.我们这样做的方式有点棘手:我们安装了一个回调函数,每次缓冲区将其主要模式更改为 lua 模式时都会调用该函数。 It then checks if the auxiliary function mentioned before is defined - if not, it loads "my-lua.el".然后它检查是否定义了前面提到的辅助函数——如果没有,它加载“my-lua.el”。 That is a little fragile, but as long as you don't play around with the lua source code, you should be fine.这有点脆弱,但只要你不玩 lua 源代码,你应该没问题。

Add the following lines to your ~/emacs.d/agladysh.el file (assuming that "agladysh" is your username):将以下行添加到您的 ~/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)))))

I assume that lua-mode is on your load-path which it should be if you followed lua-mode's installation instructions.我假设 lua-mode 在你的加载路径上,如果你遵循 lua-mode 的安装说明,它应该是。

I hope that it works for you this time, if not, let me know.我希望这次它对你有用,如果没有,请告诉我。

I know it's been a while since this was asked, but I just wanted to point out that this is still an issue, with lua-mode installed via the Emacs package system.我知道自从问这个问题已经有一段时间了,但我只想指出这仍然是一个问题,lua-mode 是通过 Emacs 包系统安装的。

However, the latest version on GitHub works very well, didn't notice any indentation weirdness.但是, GitHub 上的最新版本运行良好,没有发现任何缩进异常。 All you have to do to conform with the Lua style guide is to set indent-tabs-mode to nil and lua-indent-level to 2 .为了符合Lua 风格指南,你所要做的就是将indent-tabs-modenil并将lua-indent-level2

If you enter the following code into .emacs file in your home directory, it will make lua-mode (and only lua-mode) behave the following way:如果您在主目录中的.emacs文件中输入以下代码,它将使 lua-mode(并且仅 lua-mode)表现如下:

  • If you press ENTER, a newline will be inserted and by default the next line will be indented like the previous line.如果按 ENTER,将插入一个换行符,默认情况下下一行将像上一行一样缩进。
  • Whenever you press TAB to indent the line, point either jumps to the first non-whitespace character of the line or, if the line is empty of point is already at that character, two spaces are inserted.每当您按 TAB 缩进该行时,point 要么跳转到该行的第一个非空白字符,要么,如果该行为空,point 已在该字符处,则插入两个空格。

Especially the latter may not be what you want, but perhaps its a first approximation.尤其是后者可能不是您想要的,但可能是第一个近似值。

(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)

Restart Emacs for these changes to take effect.重新启动 Emacs 以使这些更改生效。

A cleaner way to do this was added in 2019 , in the form of two lua-indent- variables. 2019添加了一种更lua-indent- ,以两个lua-indent- variables 的形式。 This gets us almost there, but it still double-indents nested blocks for some reason.这让我们几乎到了那里,但由于某种原因它仍然对嵌套块进行双缩进。 Adding a little advice hack finishes the job.添加一点建议 hack 即可完成工作。

(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)

I can't help much right now - I have a deadline in two days 8-( - but here is what I use in my .emacs to make lua-mode usable for me...我现在帮不上什么忙 - 我有两天的最后期限 8-( - 但这是我在我的 .emacs 中使用的使 l​​ua 模式对我可用的东西......

(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?

I indent my code in an unusual way - see the example below - and so I've disciplined myself to only press TAB when lua-mode can infer the right indentation correctly from the lines above...我以一种不寻常的方式缩进了我的代码 - 请参阅下面的示例 - 所以我训练自己只有在 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

I'm the maintainer (but not author) of lua-mode.el.我是 lua-mode.el 的维护者(但不是作者)。 As I'm a lot less fluent in Emacs Lisp than other contributors to this thread, I welcome patches.由于与此线程的其他贡献者相比,我对 Emacs Lisp 的熟练程度要低得多,因此我欢迎补丁。 I'd just like to point out that there's nothing weird or incorrect about the default rules: the idea, as far as I can see, is simply that when you're in an anonymous function, the indentation should take the function keyword as its left margin.我只想指出,默认规则没有任何奇怪或不正确的地方:据我所知,这个想法很简单,当你在匿名函数中时,缩进应该将 function 关键字作为它的左边距。 This makes sense when you consider using function expressions in other places, eg as function parameters.当您考虑在其他地方使用函数表达式时,这很有意义,例如作为函数参数。

So, one simple workaround is not to write因此,一种简单的解决方法是不要编写

local f = function...局部 f = 函数...

but但是

local function f...局部函数 f...

Unless perhaps you're working with a version of Lua that predates the "local function" syntax.除非您使用的 Lua 版本早于“本地函数”语法。

Having said that, I can see why you might want to indent differently.话虽如此,我可以理解为什么您可能想要以不同的方式缩进。 In this case it seems to me reasonable to have a configuration variable lua-indent-function-from-function-keyword (better name, anyone?), and I'd be happy to accept a patch that implemented it.在这种情况下,我认为有一个配置变量 lua-indent-function-from-function-keyword (更好的名字,有人吗?),我很乐意接受一个实现它的补丁。

I think a lot of what you're looking for can be found in the emacs manual on custom C indentation definitions which falls under general indentation engine descriptions.我认为您正在寻找的很多内容都可以在关于自定义 C 缩进定义emacs 手册中找到,这些定义属于一般缩进引擎描述。

You can make it do anything you can imagine, which would be strongly preferable to just doing anything you imagine.你可以让它做任何你能想象的事情,这比做任何你想象的事情都要好得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM