用elisp编译解释当前的buffer
作者: laihj
运行当前的buffer,就是说编译或解释当前的buferr。比如在编辑的是python文件”hello.py”,那么运行它的命令就是”python hello.py”,一般的python-mode用”C-c C-c”来处理这个问题,在编辑过程中可以调用这个快捷键来运行。当然你也可以使用”M-!”来打开shell command的mini buffer来输入命令。
诚然,很多语言的mode里面实际上都内置了这一功能,但我觉得,一方面不同的mode可能定义不同快捷键,造成了额外的记忆负担,另一方面并不能要求所有的mode都提供这一功能,在mode不完整的情况下你大概也需要自己定义的能力。
要做这事儿,当然是自定义一个函数。那么最直观的想法,就是在elisp里面写一行调用shell来执行命令的代码。是的,有这种东西,示例如下:
(shell-command "python hello.py") |
这一行代码的相当于在终端中执行”python hello.py”。
我们的目的是执行当前buffer,所以下一步要做的是得到当前buffer所对应的文件名。在上一篇blog中介绍过相关方法,就是”buffer-file-name”。
于是代码就变成了这样。
(shell-command (concat "python " (buffer-file-name))) |
用concat把”python “和当前buffer的文件名连起来作为要执行的命令。
至此,就完成了.py文件的执行函数,那推广之,需要对不同的文件进行编译/解释时,就需要扩展这个命令。
我们知道对.sh文件要用bash命令执行,对.py要用python,对.htm要调用firefox。现在要做的就是让机器知道这件事。很明显的,我们需要一个map,把它们对应地存下来。
完整的代码和注释如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | (defun run-current-file () (interactive) (let (ext-map file-name file-ext prog-name cmd-str (setq ext-map '( ("py" . "python") ("sh" . "bash") ("htm" . "firefox") ) );定义命令-文件类型映身表. (setq file-name (buffer-file-name));得到当前的buffer名 (setq file-ext (file-name-extension file-name));得到后缀 (setq prog-name (cdr (assoc file-ext ext-map)));根据后缀得到执行的命令,通过对ext-map的查找 (setq cmd-str (concat prog-name " " file-name));拼出一个命令 (shell-command cmd-str)));执行 |
完毕,为这个函数绑定一个键,在编辑相应文件的时候调用,就可以对其进行需要的操作了,不仅限于编译和解释,实际上很容易看出来,只要shell能支持的,你可以玩。当然,要有权限。
Have Fun!

loading...
以前和 ahei 讨论过高阶函数的东西。我重写你的例子如下
(defvar run-current-file-alist
‘((“py” . “python”)
(“sh” . “bash”)
(“htm” . “firefox”)))
(defun run-current-file ()
(interactive)
(funcall
(\. #’shell-command
($ (wlflip ($ mapconcat #’identity)) ” “)
($ (wlflip list) buffer-file-name)
cdr
($ (wlflip assoc) run-current-file-alist)
file-name-extension
($ buffer-file-name)
)))
评论不能格式化代码,copy paste 之后 C-M-\ 吧
关于 wlcompose.el 参考 http://www.emacswiki.org/emacs/Wang_ChunYe
[回复]
ahei 回复:
三月 5th, 2010 at 9:06 上午
@ann77, 好复杂阿,看来我要学习的东西还有很多阿
[回复]
十分handy的功能,学习学习
做了几个小的改进:
- 当对于不预知的文件类型不做处理
- 文件名的全路径中有可能有空格,使用”将文件名括起来
- 通过cond做switch选择来简化代码
;; ————————– separator ————————–
(defun run-current-file ()
(interactive)
(setq file_name (buffer-file-name))
(setq file_ext (file-name-extension file_name))
(setq prog_name
(cond ((string= file_ext “py”) “python”)
((string= file_ext “sh”) “bash”)
(t “”)))
(unless (string= prog_name “”)
(progn
(setq cmd_str (format “%s ‘%s’” prog_name file_name))
(message cmd_str)
;;(shell-command cmd_str)
))
)
[回复]
ahei 回复:
四月 3rd, 2010 at 3:26 下午
@filebat, good
[回复]
filebat 回复:
四月 3rd, 2010 at 3:29 下午
@ahei, 你博文对emacs的介绍通俗易懂, 值得我们好好学习。
[回复]
ahei 回复:
四月 3rd, 2010 at 11:43 下午
谢谢, 都是靠众作者的一起努力, 还要靠大家的支持.
[回复]
filebat 回复:
四月 3rd, 2010 at 3:39 下午
@filebat,
上面的代码貌似还是有些小问题
如果打开的文件是tramp打开的,不知道全路径名是不是会带上”/root@10.32.189.13:/tmp/1.py”之类的。
这样的话,貌似只能取短文件名才行。
[回复]
ahei 回复:
四月 4th, 2010 at 12:39 上午
@filebat, 感觉tramp操作远程文件还是有点卡的, 不如直接ssh上去用emacs, 而且即使不能ssh上去, 还不如直接down下来修改, 呵呵, 我一般都不用tramp
[回复]
filebat 回复:
四月 4th, 2010 at 7:47 上午
@ahei,
远程ssh上的电脑一般没有我们配置好的emacs, 所以不太方便。 tramp貌似第一次比较卡,之后都还好。
[回复]
ahei 回复:
四月 4th, 2010 at 8:07 上午
@filebat, 呵呵, 我一般都把配置文件放到svn上, 直接在远程svn up就可以了.
[回复]
filebat 回复:
四月 4th, 2010 at 8:10 上午
@ahei,
可能是我太吹毛求疵了, 关键是要登录的是客户或QA的机器。
[回复]
ahei 回复:
四月 4th, 2010 at 8:11 上午
@filebat, 哦, 那样是麻烦了点.
[回复]
确实是个很实用的设置,根据此修改了一下,将shell-command 修改为compile,这样当编译出问题时,就可以点击错误,跳转到发生错误的行。我将编译绑定到F5,只有按F5,就能编译c 、c++、python、perl、emacs-lisp等。
;;系统安装了cygwin,gcc 不能使用windows路径,要先装化为cygwin型路径。
(defun change-to-cygwin-style-path (full-path)
(let ((file-full-path nil) (disk nil) (left nil) (cygwin-style-file-path nil))
(setq file-full-path full-path)
(setq disk (substring file-full-path 0 1))
(setq left (substring file-full-path 2 (length file-full-path)))
(setq cygwin-style-file-path (concat “/cygdrive/” disk left))))
(defun xilbert-compile ()
(interactive)
(let ((mode major-mode)
(compstr nil))
(cond ((eq mode ‘c-mode)
(setq compstr (concat “gcc -std=\”c99\” ” (change-to-cygwin-style-path (buffer-file-name)))))
((eq mode ‘c++-mode)
(setq compstr (concat “c++ ” (change-to-cygwin-style-path (buffer-file-name)))))
((eq mode ‘emacs-lisp-mode)
(emacs-lisp-byte-compile))
((eq mode ‘python-mode)
(setq compstr (concat “python ” (buffer-file-name)))
)
((eq mode ‘cperl-mode)
(setq compstr (concat “D:/Perl/bin/perl.exe ” (buffer-file-name))))
)
(save-buffer)
(if compstr
(compile compstr))))
(global-set-key [(f5)] ‘xilbert-compile)
初学emacs-lisp,写得不好,只是能用而已,希望高手指点!
[回复]
ahei 回复:
五月 30th, 2010 at 11:24 上午
@xilbert, 不错不错
[回复]
xilbert 回复:
五月 30th, 2010 at 1:35 下午
@ahei,
站长好,看了你们这些作者这么用心地在写文章,我也有点想写点文章,虽然水平有限,但想到
能让更多地人了解这么好的软件就管不了那么多了,而且通过写文章如果能让你们这些高手指点一二
进步也会更快。不知在emacser上怎么发文章?
[回复]
ahei 回复:
五月 30th, 2010 at 3:12 下午
非常欢迎.你加我的msn/gtalk就是了,或者直接把文章发到我邮箱,我的联系方式在这里
[回复]
xilbert 回复:
五月 30th, 2010 at 3:19 下午
@ahei,
哈哈,谢谢啦!
[回复]
是不是还应该分析一下文件开头的#!。
也许有些没有扩展名的文件也是可执行的脚本(例如configure)
[回复]