emacs中使用python进行功能扩展

Grey

emacs中使用python进行功能扩展

这个标题这么写其实不是很合适,因为这个方法其实不仅仅适用于python,应该说是适用于一切我们系统可以调用的程序。之所以写了python主要还是因为这几年我在工作以及学习中用python的频次高一些。这样,比较容易让emacs跟我以前的学习或者工作进行对接。

使用bash的例子解读

首先看一下从李杀大佬的网站得来的例子:

(defun do-something-region (startPos endPos)
  "Do some text processing on region.
This command calls the external script “wc”."
  (interactive "r")
  (let (cmdStr)
    (setq cmdStr "/usr/bin/wc") ; full path to your script
    (shell-command-on-region startPos endPos cmdStr nil t nil t)))

  • 首先,应该弄明白wc是干啥的。

947_emacs中使用python进行功能扩展_python
简单来说,这个命令可以统计每一个文件的行数、单词数以及字节数目。对于单词的定义,它有自己的分析方式,因此在具体数目上应该只是一个参考。

  • shell=command-on-region,相关的help:

shell-command-on-region is an interactive compiled Lisp function in ‘simple.el’.

It is bound to M-|, <menu-bar> <tools> <shell-on-region>.

(shell-command-on-region START END COMMAND &optional OUTPUT-BUFFER REPLACE ERROR-BUFFER DISPLAY-ERROR-BUFFER REGION-NONCONTIGUOUS-P)

Execute string COMMAND in inferior shell with region as input. Normally display output (if any) in temp buffer ‘*Shell Command Output*’; Prefix arg means replace the region with it. Return the exit code of COMMAND.

To specify a coding system for converting non-ASCII characters in the input and output to the shell command, use C-x RET c before this command. By default, the input (from the current buffer) is encoded using coding-system specified by ‘process-coding-system-alist’, falling back to ‘default-process-coding-system’ if no match for COMMAND is found in ‘process-coding-system-alist’.

Noninteractive callers can specify coding systems by binding ‘coding-system-for-read’ and ‘coding-system-for-write’.

If the command generates output, the output may be displayed in the echo area or in a buffer. If the output is short enough to display in the echo area (determined by the variable ‘max-mini-window-height’ if ‘resize-mini-windows’ is non-nil), it is shown there. Otherwise it is displayed in the buffer ‘*Shell Command Output*’. The output is available in that buffer in both cases.

If there is output and an error, a message about the error appears at the end of the output.

Optional fourth arg OUTPUT-BUFFER specifies where to put the command’s output. If the value is a buffer or buffer name, erase that buffer and insert the output there; a non-nil value of ‘shell-command-dont-erase-buffer’ prevent to erase the buffer. If the value is nil, use the buffer ‘*Shell Command Output*’. Any other non-nil value means to insert the output in the current buffer after START.
从设计看,这个函数的设计输出应该是'Shell Command Output' Optional fifth arg REPLACE, if non-nil, means to insert the output in place of text from START to END, putting point and mark around it.
从设计看,这个工能够可以在这个区域进行插入替换。

Optional sixth arg ERROR-BUFFER, if non-nil, specifies a buffer or buffer name to which to direct the command’s standard error output. If nil, error output is mingled with regular output. When called interactively, ‘shell-command-default-error-buffer’ is used for ERROR-BUFFER.
设计的错误输出是混合模式。

Optional seventh arg DISPLAY-ERROR-BUFFER, if non-nil, means to display the error buffer if there were any errors. When called interactively, this is t.
设计可以显示错误的buffer。

  • 函数设计分析:传入的两个参数其实是选中区域的起始位置,这个是由 interactive 的参数r来决定的。之后,函数调用wc来处理这个区域的信息。命令没有参数,其实是参数来自于STDIO。

测试效果:

  • 测试之前的buffer:

947_emacs中使用python进行功能扩展_emacs_02

  • 命令执行之后的buffer:

947_emacs中使用python进行功能扩展_剪切板_03
这里mini buffer中多了两个数字,是我自己增加的测试点,可以打印region的开始以及结束位置。

调用python的尝试

  • 首先编写一个简单的python脚本,用来处理文本中的多余空白。这个功能其实还是很有用的,我们在做pdf的翻译的时候拷贝出来的文字很多时候有多余的空白。脚本比较简单,具体如下:

#!/usr/bin/python

import sys
import re

lines = []

for l in sys.stdin:
    lines.append(l)
text = ''.join(lines)

text = re.sub('\s+', ' ', text)
print(text)

  • 接下来,对这个函数进行改造:

(defun g-remove-redant-blanks-in-region (startPos endPos)
  "Do some text processing on region.
This command calls the external script “wc”."
  (interactive "r")
  (let (cmdStr)
    (setq cmdStr "python3 /mnt/d/remove_blanks.py") ; full path to your script
    (shell-command-on-region startPos endPos cmdStr nil t nil t)))

  • 测试效果:

947_emacs中使用python进行功能扩展_emacs_04
从结果看,这个设计还是奏效了的。虽然,还有很多局限性,但是已经可以在一定程度上完成一些自动化了。

属于我自己的实用性设计

完成了上面的改造设计,其实已经能够完成一些简单的功能了。为什么我时候还有一些局限性呢?其实,在使用的便捷度上这种功能还是差一些。那么,还需要什么功能加持呢?类似的功能,我觉得应该可以设计成一键操作。比如,根据一个特殊的后缀名激活一个mode,里面支持这个快捷键。可以根据这个后缀的模板完成buffer清空、剪切板插入、空白移除、复制到剪切板一连串的操作。但是,相关的操作接口我暂时还没有掌握,因此这个只能够是放在未来实现了。

小结

这个功能或者说类似的功能我期待了很久了,我一直想知道除了eshell之外还有什么方式可以让我用emacs去调用其他的程序。如果有类似的功能,我的emacs的功能定制或者扩展或许会更容易,毕竟拿来主义就能够解决问题的选择多了。或许,这一次接触的这个功能并不是我之前期待的全部,但是已经是一个很好的开端了。