目前(文化)对Python缩进的标准是4个空格的缩进级别,并且只用空格缩进,绝不用制表符;这是GNU Emacs的python模式的默认值,也是YAPF和其它代码格式化器所使用的。我们新的和更新的Python 3代码是用这个官方标准编写的,一些相对较新的Python 2代码也是如此。然而,我花了很长时间使用8个空格的缩进级别和基于Tab的缩进来编写Python代码,这意味着我有大量的现有Python代码是这种风格,包括我们工作中几乎所有的Python 2代码和所有的DWiki。由于各种原因,我不想重新格式化或重新缩进所有这些代码,所以我想在现有的代码上使用其当前的风格,不管那是什么。 因为Python 3 不喜欢你混合使用空格和制表符,这应该包括缩进中的制表符的使用。
在不同的系统中,我现有的.emacs 设置基本上是不一致的混乱。在我的桌面上,我通过各种python模式设置反射性地坚持我的旧缩进风格;在我们的Ubuntu登录服务器上,由于转向标准风格,我不再覆盖python模式的默认值,但这给我留下了制表符的问题。今天,作为处理我的.emacs 的一部分,我决定我希望在任何地方都有相同的.emacs ,这促使我积极地制定一个解决方案。
首先,我意识到,如果我愿意真正致力于将我的缩进方式转变为标准的缩进方式,那么我唯一真正的问题就是标签。GNU Emacs的python模式会自动检测现有Python代码的当前缩进程度,对于新文件,无论项目中的其他文件存在什么情况,我都会使用4个空格的缩进方式。对于制表符,我想继续使用制表符,如果且仅当文件已经是我的基于8空格的旧缩进风格,所以唯一的问题是检测这一点。
就我所知,没有现成的GNU Emacs功能或函数可以做到这一点,所以我写了一些ELisp,作为python-mode钩子运行(这意味着它发生在每个文件的基础上)。我不会说这是非常好的ELisp,但它是这样的:
(defun cks/leading-tabs-p ()
"Detect if the current buffer has a line with leading tab(s)."
(save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(if (re-search-forward "^\t+" nil t)
t
nil))))
(add-hook 'python-mode-hook
(lambda ()
(if (and (= python-indent-offset 8) (cks/leading-tabs-p))
(setq indent-tabs-mode t))))
这里对有制表符的行的检测是非常不完善的,可能会被各种东西所欺骗,但对于我的目的来说,它已经足够好了;在实践中不太可能出现误报。我不确定我是否有任何Python代码使用了8个空格的缩进,但没有制表符。
(cks/leading-tabs-p的开头是直接从python-mode函数中复制的,该函数扫描缓冲区以确定它当前使用的缩进程度。这个函数的命名是基于我在互联网上看到的迷信内容)。
我还决定写一些ELisp函数,在现代风格和我的旧风格之间来回切换,并报告一个缓冲区的缩进状态:
(defun cks/python-toggle ()
"Toggle between old-style Python 2 and modern Python 3 settings."
(interactive)
(if (= python-indent-offset 8)
(progn (setq indent-tabs-mode nil) (setq python-indent-offset 4)
(message "Set to modern Python 3 (4-level spaces)"))
(progn (setq indent-tabs-mode t) (setq python-indent-offset 8)
(message "Set to ancient Python 2 with tabs"))))
(defun cks/rep-python ()
"Report the Python indentation status of the current buffer."
(interactive)
(message "Python indentation is %d-space indents with %s %s" python-indent-offset
(if (eq indent-tabs-mode t) "tabs" "spaces only")
(cond ((and (= python-indent-offset 4) (eq indent-tabs-mode nil))
"(Python 3 standard)")
((and (= python-indent-offset 8) (eq indent-tabs-mode t))
"(my Python 2 style)")
(t "(something weird)"))))
这是故意的,在cks/python-toggle 之后,我处于我的标准缩进风格中的一种或另一种,即使缓冲区开始时是某种奇怪的风格。
PS: python-indent-offset和indent-tabs-mode在我拿到它们的时候都是缓冲区的本地变量,所以我可以直接使用setq ,等等。现在可能有更好的方法,但我的ELisp知识已经很老很生疏了。