介绍一些Emacs功能强大的函数

作者: xilbert

1 字符串类

string-match 函数: 用指定的正则表达式去匹配字符串,将匹配的结果放到 match-data中。(match-data 用于存储emacs中字符串模式匹配中的匹配结果) 让我们看一个例子吧!

例 1:

? View Code LISP
1
2
3
(setq mystr "The quick fox jumped quickly.")
(string-match "\\(qu\\)\\(ick\\)"
                   mystr)   =>副作用:将匹配结果存储到match-data中

这条语句的意思是:用\(qu\)\(ick\) 模式对mystr进行搜索,将匹配的结果存储,并且也存储分组1\(qu\) 和分组2 \(ick\)的匹配结果。

那么怎么取出匹配的结果呢? 答案是使用 match-string

? View Code LISP
1
2
3
(match-string 0 mystr)      =>结果为quick,也就是模式匹配的结果
(match-string 1 mystr)      =>结果为qu   ,也就是第一个分组的匹配结果
(match-string 2 mystr)      =>结果为ick  ,也就是第二个分组的匹配结果

string-match 是用正则表达式去搜索字符串的,如果你想替换字符串,那怎么办呢? 那就要用到replace-regexp-in-string。

replace-regexp-in-string 函数:

用指定的正则表示式对字符串去搜索,将搜索到的模式用指定的字符串对原字符串进行替换。这个函数是非破坏性的,不会将你的原字符串破坏,函数返回的结果就是替换结果。
例1 :
假如你想将”d:/gnu/” 替换为”d:\\gnu\\” ,你可以这样写:

? View Code LISP
(replace-regexp-in-string "/" "\\\\" "d:/gnu/")

那么为什么将替代字符串写成\\\\,而不是\\ ? 实际上在emacs的正则表示式中,\\\\才代表一个\,而emacs的普通字符串中\\代表\ 。

问题1:emacs 的正则表示达式中\太多了,能不能用个函数将\(hello\) 转化为\\(hello\\) ?
答案 :我也想知道!在emacs 中,我还没找到哪个函数能将“\(hello\)”转化为”\\(hello\\)”,这是字符串转义的缘故。我只能用一个比较拙劣的方法来帮助我写emacs style regexp ——用一段perl代码:

? View Code PERL
1
2
3
4
5
#!/usr/bin/perl
print "please input a raw regexp ,i will generic a emacs style regexp\n";
$str =<STDIN>;
$str =~ s/\\/\\\\/g;
print $str;

问题2:如果你想将^ . * $ 等特殊字符作为普通字符,你不想手动写成^ ,\\. 等形式,有什么办法帮助你吗?
答 :用 regexp-quote 函数。 例:(regexp-quote “^”) 自动生成”^”

问题3:如果你想用正则表达式表达搜索”hello” 或”world” ,你不想手动写成”hello\\|world”,有无简化的办法?
答: regexp-opt 可以办到。 例:(regexp-opt ‘(“hello” “world”)) 输出了”\\(?:hello\\|world\\)”

让我们再次回到replace-regexp-in-string函数来,它的功能真的很强大,替代字符串中还可以引用分组
例2 : (replace-regexp-in-string “\\(hello\\)\\(world\\)” “\\2\\1″ “helloworld”)
这个表达式的意思是:用\(hello\)\(world\)模式对“helloworld”进行搜索,将搜索到的结果用“\\2\\1”进行替换。函数返回了”worldhello”

2 hook类函数

emacs 中存在大量的hook ,有emacs-lisp-mode-hook ,c-mode-hook 等,基本上一个mode就提供了一个hook 。那么hook的本质是什么,我们能不能设计一个自己的hook ? 实际上,hook 是一个elisp 表,这个表中存放着要被调用的函数的符号(或代码)。
让我们用一个例子说明一下吧!

? View Code LISP
1
2
3
4
5
6
7
8
(defvar myhook nil) ;;定义myhook为空表
(add-hook 'myhook '(lambda ()
          (insert "fun1 was called ")))  ;;将这个匿名函数1加入到myhook表中去
 
(add-hook 'myhook '(lambda ()
          (insert "fun2 was called "))) ;;将匿名函数2加入myhook表中去
 
(run-hooks 'myhook) ;; =>执行这条语句会在光标后插入fun2 was called fun1 was called

那么为什么会先执行匿名函数2呢,而不是匿名函数1呢?这是因为add-hook 会将函数插入到myhook列表的头部,并且run-hooks 是从myhook头执行到尾的,因此匿名函数2会先执行。
那么这样设计有什么好处呢?好处是比较明显的,比如用户执行了(add-hook ‘c-mode-hook ‘myfun),其中myfun是要修改了c-mode中的某个变量值的,但不巧的是,系统在此之前就执行了(add-hook ‘c-mode-hook ‘firstfun),并且firstfun也要设置这个变量的值。那么通过这样设计,你的修改将不会成功(因为你的修改会被系统的系统覆盖),这样就保证了用户不能随意地改变mode的状态,保证了系统的稳定性。

既然知道了hook就是一个表,那么我们也就可以用操作列表的方式来操作它了。

? View Code LISP
1
2
3
(setq myhook (cdr myhook))      ;; =>丢弃myhook中的第一个函数
(setq myhook (reverse myhook))  ;; =>将myhook反转,改变函数执行的顺序
(setq myhook nil)               ;; =>将myhook清空

3 用来高亮的函数

1、hi-lock-set-pattern 函数
它的作用为在当前buffer中对匹配的模式应用用户指定的face
我之前在text-mode 中打开outline-minor-mode,结果标题没有被高亮,经千辛万苦终于将问题解决,这得益于hi-lock-set-pattern函数。我是这样设置的:
先定义一些face ,如下:

? View Code LISP
1
2
3
4
5
6
7
8
9
10
11
(defface cyan-face
  '((t :foreground "cyan"))
  "我自定义的cyan face")
 
(defface yellow-face
  '((t :foreground "yellow"))
  "我自定义的yellow face")
 
(defface magenta-face
  '((((class color)) :foreground "magenta"))
  "我自定义的magenta face")

这些face 都是从ahei-face.el中的,那里面还有许多其他的face。

然后然加入下面的代码,让outline-minor-mode启动时执行hi-lock-set-pattern,将标题高亮。

? View Code LISP
1
2
3
4
5
(add-hook 'outline-minor-mode-hook
          '(lambda ()
             (hi-lock-set-pattern "^\\*[^*]*?$" 'cyan-face)
             (hi-lock-set-pattern "^\\*\\*[^*]*?$" 'yellow-face)
             (hi-lock-set-pattern "^\\*\\*\\*[^*]*?$" 'magenta-face)))

2、hi-lock-unface-buffer
既然有高亮的函数,就应该有取消高亮的函数,它就是hi-lock-unface-buffer了

4 编译类函数

complie 函数非常强大,它的参数是个编译命令字符串,它会执行这个编译命令,执行完后会打开一个buffer,其主模式为compilation-mode。如果编译出错,会在它打开的buffer中输出出现错误的行数,这时就可以通过点击鼠标跳转到发生错误的代码处。如果编译成功,则会输出编译结果。
我们可以这样设置编译命令:

? View Code LISP
1
2
3
4
(compile "gcc hello.c")
(compile "c++ hello.cpp")
(compile "perl hello.pl")
(compile "d:/python/python25.exe hello.py")

5 将这些函数串起来

下面我会用一个例子将上面所讲的大部分函数串起来。
需求 :我想实现按f5就能编译当前buffer,并且要有错误定位功能,支持的语言可以添加(不要修改已经写好的代码)
实际环境:window xp ,cygwin
由于使用的是cygwin,调用gcc 时,gcc 需要cygwin类型的路径
这是emacs 给出的路径类型: d:/gnu/
在cygwin下是 :/cygdirve/d/gnu

实现:

? View Code LISP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
;;这个函数就是用来转换路径的
(defun change-to-cygwin-style-path (emacs-style-path-on-w32)
  (let ((full-path emacs-style-path-on-w32))
    (replace-regexp-in-string "\\(\\w+?\\):\\(\\w*\\)" "/cygdrive/\\1\\2" full-path)))
(change-to-cygwin-style-path (buffer-file-name))
 
(defvar my-compile-hook nil)
 
;;这是主函数,按f5执行此函数
(defun my-compile-main-fun ()
  (interactive)
  (run-hooks  'my-compile-hook))
 
;;这个函数用来编译现在用得到的语言写的代码,以后如果要用到其他语言,可以写个函数,然后加入到my-compile-hook中去。
(defun orginal-compile-fun ()
   (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)))
      )
      ((or (eq mode 'cperl-mode) (eq mode 'perl-mode))
       (setq compstr (concat "D:/Perl/bin/perl.exe " (buffer-file-name))))
      )
    (save-buffer)
    (if compstr 
    (compile compstr))))
;;将orginal-compile-fun加入my-compile-hook中
(add-hook 'my-compile-hook 'orginal-compile-fun)
 
;;将my-compile-main-fun 绑定到f5上
(global-set-key [(f5)] 'my-compile-main-fun)

如果有一天,你要用octave 语言,你可以这样扩展:

? View Code LISP
1
2
3
4
5
(add-hook 'my-compile-hook '(lambda ()
                              (if (eq major-mode 'octave-mode)
                                  (progn
                                    (save-buffer)
                                    (compile (concat "octave --silent " (buffer-file-name)))))))

使用hook机制是不是很方便呀,这样就不用去修改orginal-compile-fun的代码了。

ps:不知不觉罗嗦了这么多,其实我写这篇文章的目的有两个:1、是给初学emacs lisp的人介绍一下emacs字符串的操作和hook的机理以及强大的compile函数,希望有点用处。2、希望高手们也分享一下自己觉得很强大的emacs lisp 函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值