Emacs as python IDE

最近 python 写的比较多,比较了几个编辑器,最后还是留下了 emacs。

主要比较了 emacs 和 pycharm。pycharm 绝对是一个很强的 IDE,几乎可以补全任何东西,写代码各种提示。比如 Django 里面定义一个 model User 之后,就可以 User. 之后提示 objects ,这个是依据 metaclass 来补全的。另外还有比如写 User.objects.get(|) 的时候,光标在竖线那个位置,会提示 User 的字段,这个相当好用。这两个只是皮毛,实在是太好用了。

但是为什么还要用 emacs 呢?emacs 的编辑器功能太好用了。比如 < 到页首, > 到页尾, C-x b 切换 buffer,还有切换 frame,等等快捷键非常舒服,完全不用鼠标。不过也可能是我习惯了 emacs 的快捷键了。在 pycharm 里面时不时就不行,比如选择一段文字,纯键盘需要按 -> 配合才可以,那还不如用鼠标算了。

其实如果一上手就用 pycharm,那绝对会觉得很爽。

emacs 写 python 在原生的 python-mode 基础上有两个好用的选择,一个是 anaconda-mode,一个是 elpyanaconda-mode 相对来说比较简陋一点,但是补全什么的没问题,缺少重构功能。两个的工作模式都是会启动一个补全用的进程,然后通过 lisp 和这个进程交互获取补全信息。

anaconda-mode 遇到的问题和解决

anaconda-mode 我遇到一个问题,为了下载 emacs 的 package 方便,我设置了代理,这个代理导致 anaconda-mode 和补全进程交互的时候,连接不能断开,就会不停的新建连接,一会就打开文件数满了,可以参观这个 issue。主要是设置了 no_proxy 解决。

 (setq url-proxy-services
       '(("no_proxy" . "^\\(127.0.0.1\\|localhost\\|10.*\\)")
         ("http" . "127.0.0.1:6152")
         ("https" . "127.0.0.1:6152")))

auto-virtualenv

我的 python 项目使用了 virtualenv ,会在项目目录下面建一个 .venv 的目录,把虚拟环境放进去。 anaconda-mode 提供了 pythonic-activate 命令, elpy 提供了 pyvenv-activate 来切换环境。但是每次打开项目都需要搞一下就挺恶心了。

然后找到了 auto-virtualenv 这个工具。安装之后,他会自动查找你的项目里面的可能的虚拟环境。项目根目录识别是通过 .git.hg 等一些逻辑来判定的,具体可以看代码。然后虚拟环境识别是通过根目录下面的 .python-version .venv 等来识别的。

我的项目是建了一个 .venv 目录,所以每次打开一个 python 文件,会自动配置好 virtualenv 的环境,这样 elpy 在 django 自带的 model 上面也可以查找 defination。

(use-package auto-virtualenv
  :ensure t
  :config
  (add-hook 'python-mode-hook 'auto-virtualenv-set-virtualenv)
)

elpy

elpy 其实没有什么好配置的,主要注意的是,因为我们用了 virtualenv 环境,所以需要他依赖的包都装在 .venv 环境或者装在 python 自己的目录下面应该都可以。启动 emacs 之后可以使用 M-x elpy-config 看看还有什么没配置好。

(use-package elpy
  :ensure t
  :init
  (setq elpy-rpc-backend "jedi")
  (elpy-enable)
  :config
  (add-hook 'python-mode-hook 'elpy-mode)
  (with-eval-after-load 'elpy
  (add-hook 'elpy-mode-hook 'elpy-use-ipython))
  :bind (("M-*" . pop-tag-mark))
  )

elpy-rpc-backend 有两个选择,一个 jedi ,一个 rope ,我试了感觉区别不大,另外 rope 感觉要死了。所以我用了 jedi

flycheck

flycheck 可以配合 flake8 实时显示出来你的代码有不符合 flake8 要求的地方,很方便。这个工具我也遇到一个坑 issue,有兴趣可以看看。主要原因是 flycheck 是使用 flake8 < xxx.py 这种方式检查的,而这种方式下 flake8 不会考虑文件头部的 coding 设置,来识别文件编码,而是根据 LC_CTYPE 环境变量来的,所以只要正确设置这个变量就可以了。 issue 里面提到的设置 emacs 的编码屁用么有的。

flycheck 可以配合 flake8 和 pylint 来做 python 代码的检查,如果装了前者,就不会考虑后者了。我试过 pylint,这货默认要求有点高,比如 class 和 method 没有 doc string 也会提示,代码一堆问题,我就赶紧换掉了。。。

python outline

好处是打开 python 文件的时候,会把代码都折叠起来,按照需要你自己打开就好。elpa 里面没有,网上搜到的。

(use-package python-magic
  :ensure outline-magic
  :config
  (add-hook 'python-mode-hook 'my-python-outline-hook)
  (add-hook 'python-mode-hook
            (lambda ()
              (setq outline-regexp "def\\|class ")))

  )

indent-tools

这个工具是用来锁进 python 代码和浏览代码用的。搜一下有动图,看看就知道了。

(use-package indent-tools
  :ensure t
  :init
  (add-hook 'python-mode-hook
            (lambda () (define-key python-mode-map (kbd "C-c i") 'indent-tools-hydra/body))
            )
  )

yasnippet 和 company

elpy 是使用这两个补全的。有几个有用的配置, C-s 那个,可以在补全候选菜单出来的时候,用关键词过滤结果。

我没搞定在 company 里面直接显示出来 yasnippet 可用的 snippet,只好设置了一个快捷键 C-c y 来提示。可以提示出来一大堆。比如我经常写错的 -*- coding:utf8 -*- 有一个 snippet 叫做 utf8 ,直接输入之后 tab 就可以了。

(use-package company
  :ensure t
  :init
  (setq company-minimum-prefix-length 2)
  (setq company-dabbrev-ignore-case t)
  :config
  (add-hook 'after-init-hook 'global-company-mode)
  (define-key company-active-map (kbd "C-n") #'company-select-next)
  (define-key company-active-map (kbd "C-p") #'company-select-previous)
  (define-key company-active-map (kbd "C-s") #'company-filter-candidates)

  (global-set-key (kbd "C-c y") 'company-yasnippet)
  )