emacs上的多行python缩进

kus*_*sut 2 python emacs elisp indentation

我是一个emacs新手,我希望emacs能够像这样缩进我的代码

egg = spam.foooooo('vivivivivivivivivi')\
          .foooooo('emacs', 'emacs', 'emacs', 'emacs')
Run Code Online (Sandbox Code Playgroud)

默认情况下不能自动执行此操作(无需手动插入空格或Cc>),因为emacs始终缩进4个空格(除非我在多行上分割多个参数).

什么是最好的方法来做到这一点?

PS:如果这是一个坏主意(针对PEP 8或其他什么),请告诉我

Gar*_*ees 25

我与阿龙同意你的风格上的选择是可取的,但因为我也同意他的Emacs Lisp是有趣的,我将描述你会如何实现这一点.

Emacs python-mode计算函数中一行的缩进,python-calculate-indentation并且处理延续行的相关部分深埋在函数内部,没有简单的方法来配置它.

所以我们有两个选择:

  1. python-calculate-indentation用我们自己的版本替换整个版本(每当python-mode更改时都会出现维护噩梦); 要么
  2. " 建议 "函数python-calculate-indentation:即将它包装在我们自己的函数中,处理我们感兴趣的案例,并以其他方式遵循原始函数.

在这种情况下,选项(2)似乎是可行的.那就让我们去吧!要做的第一件事是阅读建议手册,建议我们的建议应如下所示:

(defadvice python-calculate-indentation (around continuation-with-dot)
  "Handle continuation lines that start with a dot and try to
line them up with a dot in the line they continue from."
  (unless 
      (this-line-is-a-dotted-continuation-line) ; (TODO)
    ad-do-it))
Run Code Online (Sandbox Code Playgroud)

ad-do-it是一个defadvice用原始函数替换的魔术标记.来自Python背景你可能会问,"为什么不做这个装饰风格?" Emacs建议机制的设计(1)是为了使建议与原始建议保持良好分离; (2)对不需要合作的单一职能提出多项建议; (3)允许您个人控制打开和关闭哪些建议.你当然可以想象用Python编写类似的东西.

以下是如何判断当前行是否为虚线延续行:

(beginning-of-line)
(when (and (python-continuation-line-p)
           (looking-at "\\s-*\\."))
    ;; Yup, it's a dotted continuation line. (TODO)
    ...)
Run Code Online (Sandbox Code Playgroud)

这有一个问题:打电话给 beginning-of-line实际移动的指向行的开头.哎呀.我们不想在仅计算缩进时移动点.因此,我们最好在调用时将save-excursion其包装起来,以确保该点不会徘徊.

我们可以通过向后跳过令牌或带括号的表达式(Lisp称之为"S表达式"或"性别")来找到我们需要排队的点,直到我们找到点,否则我们到达开头声明.在缓冲区的受限制部分进行搜索的一个好的Emacs习语是缩小缓冲区以包含我们想要的部分:

(narrow-to-region (point)
                  (save-excursion
                    (end-of-line -1)
                    (python-beginning-of-statement)
                    (point)))
Run Code Online (Sandbox Code Playgroud)

然后继续向后跳过性别,直到找到点,或直到 backward-sexp停止取得进展:

(let ((p -1))
  (while (/= p (point))
    (setq p (point))
    (when (looking-back "\\.")
      ;; Found the dot to line up with.
      (setq ad-return-value (1- (current-column)))
      ;; Stop searching backward and report success (TODO)
      ...)
    (backward-sexp)))
Run Code Online (Sandbox Code Playgroud)

ad-return-value是一个魔术变量,defadvice用于建议函数的返回值.丑陋但实用.

现在有两个问题.第一个是backward-sexp在某些情况下可以发出错误信号,所以我们最好抓住这个错误:

(ignore-errors (backward-sexp))
Run Code Online (Sandbox Code Playgroud)

另一个问题是突破循环并指示成功.我们可以通过声明命名block然后调用来同时执行这两个操作return-from.阻止和退出是Common Lisp功能,因此我们需要(require 'cl)

让我们把它们放在一起:

(require 'cl)

(defadvice python-calculate-indentation (around continuation-with-dot)
  "Handle continuation lines that start with a dot and try to
line them up with a dot in the line they continue from."
  (unless 
      (block 'found-dot
        (save-excursion
          (beginning-of-line)
          (when (and (python-continuation-line-p)
                     (looking-at "\\s-*\\."))
            (save-restriction
              ;; Handle dotted continuation line.
              (narrow-to-region (point)
                                (save-excursion
                                  (end-of-line -1)
                                  (python-beginning-of-statement)
                                  (point)))
              ;; Move backwards until we find a dot or can't move backwards
              ;; any more (e.g. because we hit a containing bracket)
              (let ((p -1))
                (while (/= p (point))
                  (setq p (point))
                  (when (looking-back "\\.")
                    (setq ad-return-value (1- (current-column)))
                    (return-from 'found-dot t))
                  (ignore-errors (backward-sexp))))))))
    ;; Use original indentation.
    ad-do-it))

(ad-activate 'python-calculate-indentation)
Run Code Online (Sandbox Code Playgroud)

我不会声称这是最好的方法,但它说明了一堆中等棘手的Emacs和Lisp功能:建议,短途旅行,缩小,移动性别,错误处理,阻止和退出.请享用!

  • +1.+10,如果我能,因为这绝对值得一个'好回答'徽章. (3认同)