<p>我同意Aaron关于您的风格选择的可取性,但是由于我也同意他关于EmacsLisp很有趣的观点,我将描述您如何实现这一点。在</p>
<p>Emacs<code>python-mode</code>计算函数<a href="http://codesearch.google.com/codesearch/p?hl=en#a5T9K7pO0yw/lisp/progmodes/python.el&q=python-calculate-indentation&sa=N&cd=1&ct=rc" rel="noreferrer">^{<cd2>}</a>中一行的缩进,用于处理连续行的相关部分深埋在函数内部,无法轻松配置它。在</p>
<p>所以我们有两个选择:</p>
<ol>
<li>将整个<code>python-calculate-indentation</code>替换为我们自己的版本(每当{<cd1>}发生变化时,<code>python-mode</code>就会成为维护的噩梦);或者</li>
<li>“<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Advising-Functions.html" rel="noreferrer">Advise</a>”函数<code>python-calculate-indentation</code>:也就是说,将它包装在我们自己的函数中,该函数处理我们感兴趣的情况,否则将遵从原始函数。在</li>
</ol>
<p>选项(2)在这种情况下似乎是可行的。所以让我们开始吧!首先要阅读<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Advising-Functions.html" rel="noreferrer">manual on advice</a>,它建议我们的建议应该如下所示:</p>
<pre><code>(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))
</code></pre>
<p>这里<code>ad-do-it</code>是一个神奇的令牌,<code>defadvice</code>用原始函数代替它。从Python背景出发,您可能会问:“为什么不使用这种装饰风格?”Emacs建议机制设计为(1)将建议与原始建议很好地分开;以及(2)为一个不需要协作的函数提供多个建议;(3)允许您单独控制哪些建议被打开和关闭。当然可以想象用Python编写类似的东西。在</p>
<p>以下是如何判断当前行是否是虚线延续线:</p>
^{pr2}$
<p>这有一个问题:对<code>beginning-of-line</code>的调用实际上将点移动到行的开头。哎呀。我们不想在仅仅计算缩进时移动点。所以我们最好用一个调用<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Excursions.html" rel="noreferrer">^{<cd9>}</a>来结束这一点,以确保该点不会走投无路。在</p>
<p>我们可以通过向后跳过标记或带圆括号的表达式(Lisp称之为“S-expressions”或“sexps”)来找到需要对齐的点,直到找到点,否则就可以找到语句的开头。在缓冲区的受限部分进行搜索的一个好的Emacs习惯用法是<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Narrowing.html" rel="noreferrer">narrow</a>缓冲区只包含我们想要的部分:</p>
<pre><code>(narrow-to-region (point)
(save-excursion
(end-of-line -1)
(python-beginning-of-statement)
(point)))
</code></pre>
<p>然后继续向后跳过sexps,直到找到点,或者直到<code>backward-sexp</code>停止前进:</p>
<pre><code>(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)))
</code></pre>
<p>这里<code>ad-return-value</code>是一个神奇的变量,<code>defadvice</code>使用它来从建议的函数返回值。丑陋但实用。在</p>
<p>现在有两个问题。首先,<code>backward-sexp</code>可以在某些情况下发出错误信号,因此我们最好捕获该错误:</p>
<pre><code>(ignore-errors (backward-sexp))
</code></pre>
<p>另一个问题是跳出循环,同时也表示成功。我们可以通过声明一个命名的<code>block</code>,然后调用<code>return-from</code>,同时完成这两个操作。<a href="http://www.gnu.org/software/emacs/manual/html_node/cl/Blocks-and-Exits.html" rel="noreferrer">Blocks and exits</a>是常见的Lisp特性,因此我们需要<code>(require 'cl)</code></p>
<p>让我们把它们放在一起:</p>
<pre><code>(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)
</code></pre>
<p>我不认为这是最好的方法,但它说明了一系列比较棘手的emac和Lisp特性:<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Advising-Functions.html" rel="noreferrer">advice</a>,<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Excursions.html" rel="noreferrer">excursions</a>,<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Narrowing.html" rel="noreferrer">narrowing</a>,<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/List-Motion.html" rel="noreferrer">moving over sexps</a>,<a href="http://www.gnu.org/s/emacs/manual/html_node/elisp/Handling-Errors.html" rel="noreferrer">error handling</a>,<a href="http://www.gnu.org/software/emacs/manual/html_node/cl/Blocks-and-Exits.html" rel="noreferrer">blocks and exits</a>。享受吧!在</p>