多线python缩进emacs [英] multi lines python indentation on emacs
问题描述
egg = spam.foooooo(我想要一个emacs新手,我想要emacs能够缩进我的代码, 'viviviviviviviviv')\
.foooooo('emacs','emacs','emacs','emacs')
默认情况下,不需要自动执行 (不需要手动插入空格或Cc>),因为emacs总是缩进4个空格(除非我将多个参数分割成多行)
最好的做法是什么?
PS:如果是一个坏主意(对于PEP 8或某事)请告诉我
我同意Aaron关于你的风格选择,但由于我也同意Emacs Lisp的乐趣,我将介绍如何实现这一点。
Emacs python -mode
计算函数 python-calculate-indentation
,处理连续行的相关部分埋在函数内部,没有简单的方法来配置。
所以我们有两个选择:
- 替换整个
python-calculate-缩进
与我们自己的版本(每当python-mode
更改时的维护噩梦);或 - 建议函数
python-calculate-indentation
:就是将它包装在我们自己的函数中,处理我们感兴趣的情况,否则会延迟原来的。
选项(2)在这种情况下似乎是可行的。所以让我们去吧!首先要做的是阅读建议手册这表明我们的建议应该如下所示:
(defadvice python-calculate-indentation(around continue-with-点)
处理从点开始的连续行,尝试
将它们与他们继续的行中的一个点对齐。
(除非
(此行) -i-a-dot-continuation-line);(TODO)
ad-do-it))
这里 ad-do-it
是一个魔术令牌, defadvice
替换原来的功能。来自Python背景,你可能会问:为什么不做这个装饰风格?设计Emacs建议机制(1)保持建议与原来的分离;和(2)为不需要合作的单个功能提供多个意见; (3)允许您个人控制哪些建议被打开和关闭。您可以想象在Python中编写类似的东西。
以下是如何判断当前行是否为虚线的连续行:
$ b $
(当(和(python-continuation-line-p))
(look-at\\ (TODO)
...)
这里有一个问题:调用开始行
实际上移动到线。哎呀。我们不想在仅仅计算缩影时移动点。所以我们最好在打电话给 save-excursion
,以确保该点不会徘徊。
我们可以找到我们需要通过向后跳过令牌或括号表达式(Lisp称为S表达式或sexps),直到找到点,否则我们到达语句的开头。在缓冲区的限制部分进行搜索的好的Emacs成语是 narrow 缓冲区只包含我们想要的部分:
(narrow-to-region(point)
(save-excursion
(行结束-1)
(python-begin-of-statement)
(point)))
然后继续跳过sexps直到找到点,或直到 backward-sexp
停止进行:
(let((p -1))
(while(/ = p点))
(setq p(point))
(when(look-back\\。)
;;找到要匹配的点
(setq ad-return-value(1-(current-column)))
;;停止向后搜索并报告成功(TODO)
...)
(backward-sexp)) )
这里 ad-return-value
是一个魔术变量, defadvice
用于从建议的函数返回值。丑陋但实用。
现在有两个问题。第一个是 backward-sexp
可以在某些情况下发出错误信号,所以我们最好抓住这个错误:
(ignore-errors(backward-sexp))
其他问题是打破循环,也表明成功。我们可以通过声明一个命名的块
然后调用返回
来同时执行。 阻止和退出是Common Lisp功能所以我们需要(require'cl)
让我们把它们放在一起:
(require'cl)
(defadvice python-calculate-indentation(around continue-with-dot)
处理从点开始的连续行,尝试
将它们与他们继续的行中的点对齐。
(除非
(块'found-dot
(save-excursion
(行首)
(当(和(python-continuation-line-p))
(look-at\\s- * \\\。))
(保存限制
;;处理点连续行
(窄到区域(点)
(save-excursion
(行尾-1)
(python-begin-of-statement)
(point)))
;;向后移动,直到找到一个点或不能向后移动
;;
(let((p -1))
(while(/ = p(point))
(setq p(point))$
(返回\\。)
(setq ad-return-value(1-(current-column)))
t))
(ignore-errors(backward-sexp)))))))))
;;使用原始缩进。
ad-do-it))
(ad-activate'python-calculate-indentation)
我不会声称这是最好的方法,但它说明了一堆适度棘手的Emacs和Lisp功能:建议,短途旅行,缩小,移动超过性别,错误处理 ,阻止和退出。享受!
Im an emacs newbie, I want emacs to be able to indent my code like this
egg = spam.foooooo('vivivivivivivivivi')\
.foooooo('emacs', 'emacs', 'emacs', 'emacs')
It's not possible to do this automatically by default (without manually inserting spaces or C-c >), since emacs always indents 4 spaces (unless Im splitting multiple arguments over multiple lines).
Whats the best approach to do this?
PS: If this is a bad idea (against PEP 8 or something) please do tell me
I agree with Aaron about the desirability of your stylistic choice, but since I also agree with him that Emacs Lisp is fun, I'll describe how you might go about implementing this.
Emacs python-mode
computes the indentation of a line in the function python-calculate-indentation
and the relevant section for handling continuation lines is buried deep inside the function, with no easy way to configure it.
So we have two options:
- Replace the whole of
python-calculate-indentation
with our own version (a maintenance nightmare wheneverpython-mode
changes); or - "Advise" the function
python-calculate-indentation
: that is, wrap it in our own function that handles the case we're interested in, and otherwise defers to the original.
Option (2) seems just about doable in this case. So let's go for it! The first thing to do is to read the manual on advice which suggests that our advice should look like this:
(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))
Here ad-do-it
is a magic token that defadvice
substitutes with the original function. Coming from a Python background you might well ask, "why not do this decorator-style?" The Emacs advice mechanism is designed (1) to keep advice well separated from the original; and (2) to have multiple pieces of advice for a single function that don't need to co-operate; (3) to allow you individual control over which pieces of advice are turned on and off. You could certainly imagine writing something similar in Python.
Here's how to tell if the current line is a dotted continuation line:
(beginning-of-line)
(when (and (python-continuation-line-p)
(looking-at "\\s-*\\."))
;; Yup, it's a dotted continuation line. (TODO)
...)
There's one problem with this: that call to beginning-of-line
actually moves point to the beginning of the line. Oops. We don't want to move point around when merely calculating indention. So we better wrap this up in a call to save-excursion
to make sure that point doesn't go a-wandering.
We can find the dot that we need to line up with by skipping backwards over tokens or parenthesized expressions (what Lisp calls "S-expressions" or "sexps") until either we find the dot, or else we get to the start of the statement. A good Emacs idiom for doing a search in a restricted part of the buffer is to narrow the buffer to contain just the part we want:
(narrow-to-region (point)
(save-excursion
(end-of-line -1)
(python-beginning-of-statement)
(point)))
and then keep skipping sexps backwards until we find the dot, or until backward-sexp
stops making progress:
(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)))
Here ad-return-value
is a magic variable that defadvice
uses for the return value from the advised function. Ugly but practical.
Now there are two problems with this. The first is that backward-sexp
can signal an error in certain circumstances, so we better catch that error:
(ignore-errors (backward-sexp))
The other problem is that of breaking out of the loop and also indicating success. We can do both at once by declaring a named block
and then calling return-from
. Blocks and exits are Common Lisp features so we'll need to (require 'cl)
Let's put it all together:
(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)
I won't claim that this is the best way to do this, but it illustrates a bunch of moderately tricky Emacs and Lisp features: advice, excursions, narrowing, moving over sexps, error handling, blocks and exits. Enjoy!
这篇关于多线python缩进emacs的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!