emacs上的多行python缩进

时间:2010-10-30 09:06:22

标签: python emacs elisp indentation

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

egg = spam.foooooo('vivivivivivivivivi')\
          .foooooo('emacs', 'emacs', 'emacs', 'emacs')

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

这样做的最佳方法是什么?

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

2 个答案:

答案 0 :(得分:25)

我同意Aaron关于你的风格选择的可取性,但是因为我也同意Emacs Lisp很有趣,我会描述你如何实现这个。

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

所以我们有两个选择:

  1. 将整个python-calculate-indentation替换为我们自己的版本(每当python-mode更改时维护噩梦);或
  2. Advise”函数python-calculate-indentation:也就是说,将它包装在我们自己的函数中,处理我们感兴趣的案例,并以其他方式遵循原文。
  3. 在这种情况下,选项(2)似乎是可行的。那就让我们去吧!首先要做的是阅读manual on advice,这表明我们的建议应该是这样的:

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

    此处ad-do-itdefadvice替换原始函数的魔术标记。来自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)
        ...)
    

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

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

    (narrow-to-region (point)
                      (save-excursion
                        (end-of-line -1)
                        (python-beginning-of-statement)
                        (point)))
    

    然后继续向前跳过性别,直到我们找到点,或直到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)))
    

    这里ad-return-value是一个神奇变量,defadvice用于建议函数的返回值。丑陋但实用。

    现在有两个问题。第一个是backward-sexp在某些情况下可以发出错误信号,因此我们最好能够发现错误:

    (ignore-errors (backward-sexp))
    

    另一个问题是突破循环并指示成功。我们可以通过声明一个名为block然后调用return-from来同时执行这两项操作。 Blocks and exits是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)
    

    我不会声称这是最好的方法,但它说明了一堆中等棘手的Emacs和Lisp功能:adviceexcursionsnarrowing,{ {3}},moving over sexpserror handling。享受!

答案 1 :(得分:3)

这很丑陋,需要你写一些emacs lisp。我需要学习emacs lisp,所以如果它不是那么难看,我可能会这样做。但事实并非如此。看起来你学习emacs lisp :)(如果你真的想这样做)。我有点嫉妒。无论如何,你说通知你这是一个坏主意是一个可以接受的答案,所以这里是:

这是一种可怕的风格选择。不是

egg = spam.foo('viviviv')
egg = egg.foo('emacs', 'emacs', 'emacs')

更容易阅读?

虽然没有具体针对PEP 8,但提到线路延续字符的使用应保持在最低限度。此外,这最明确和客观地违背了PEP 8的精神。我只是不确定如何;)