Browsing all articles tagged with elisp
Feb
12

[elisp]用elisp编译解释当前的buffer

Author laihj    Category 善其事     Tags ,

运行当前的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,把它们对应地存下来。

完整的代码和注释如下:

(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!

Feb
3

[elisp]针对Emacs中文本编辑的编程简介

Author laihj    Category 负暄琐话     Tags ,

emacs

Elisp是Emacs下的Lisp方言,而Emacs是一款编辑器。那么针对于Emacs,Lisp要做的很重要一部分工作当然就是对编辑的自动化支持。比如移动鼠标,输入句子,查找替换,代码高亮等等,简单地说,就是为更好更方便地支持文本编辑提供支持。而把这些函数都重合起来,就起成了Emacs mode。如我们常用的C++ mode,python mode。还有之前我一直在介绍的org-mode等,都是由Elisp拼成的。

学习Elisp,可以满足对Emacs进行定制的需求,你可以把想要的功能写在.el文件里面,让Emacs来调用。一方面可以实现一些简单的轻量级的功能,而不需要为此寻找和安装一个完整的mode。另一方面,可以修改现有的mode,使得更符合自己的习惯。这符合开源的作法,不爽即改。

Elisp函数的简单例子

光标位置

 ;返回当前光标的位置
  (point)
  ;region的头跟尾
  (region-beginning)
  (region-end)
  ;最大光标位置(即文件尾),elisp还提供最小光标位置(point-min),不过我觉得那应该都是1吧。
  (point-max)
  ;返回buffer结尾的绝对位置,无视narrow-to-region
  (buffer-end 1)

移动光标和搜索

;移动光标到392
(goto-char 392)
 
; 向前/向后移动n字符
(forward-char n)
(backward-char n)
 
; 跳过所有\n\t,即把光标移动第一个不是换行或制表符那里
; 返回移动的字符数
(skip-chars-forward "\n\t")
(skip-chars-backward "\n\t")
 
;移动光标到myStr后面,向前和向后
;返回新的光标位置
(search-forward myStr)
(search-backward myStr)
 
; 同上,但是参数是正则表达式,myRegex
; 返回新的光标位置
(re-search-forward myRegex)
(re-search-backward myRegex)

文本编辑

; 删除光标后的九个字符
(delete-char 9)
 
; 删除选中的两点之前的文本
(delete-region mystartpos myendpos)
 
; 在当前光标位置插入一字符串
(insert "Forza Inter")
 
; 从buffer中获得一个字符串并赋给mystr
(setq Mystr (buffer-substring mystartpos myendpos))
 
; 改变字符大小写
;这个例子是指当前光标之前的20个字符
(rapitalize-region (- (point) 20) (point))

字符串

  ; 长度
(length "abc") ; returns 3
 
; 获取一个子串
(substring myStr startIndex endIndex)
 
; 替换,以正则方式
(replace-regexp-in-string myRegex myReplacement myStr)

Buffers

; 当前buffer的名字
(buffer-name)
 
; 文件名(全路径)
(buffer-file-name)
 
; 设定一个buffer名
(set-buffer myBufferName)
 
; 保存
(save-buffer)
 
; 关闭指定的buffer
(kill-buffer myBuffName)
 
; 关闭当前buffer
(kill-this-buffer)
 
; 临时指定一个buffer作为当前buffer
(with-current-buffer myBuffer
  ; do something here ...
)

Files

  ; 打开一个文件
(find-file myPath)
 
; 另存
; 会关闭当前的buffer,打开另存好的文件
(write-file myPath)
 
; 把一个文件内容插入到当前位置
(insert-file-contents myPath)
 
; 将选中的评论文本加到某个文件后面
(append-to-file myStartPos myEndPos myPath)
 
; 重命名
(rename-file fileName newName)
 
; 复制
(copy-file oldName newName)
 
; 删除
(delete-file fileName)
 
; 获取路径
(file-name-directory myFullPath)
 
; 获取文件名(不含路径)
(file-name-nondirectory myFullPath)
 
; 得到文件名后缀
(file-name-extension myFileName)
 
; 获得不含后缀的文件名。
(file-name-sans-extension "abc.htm")

简单的例子

(defun insert-p-tag ()
  "Insert
 
 at cursor point."
  (interactive)
  (insert "
 
")
  (backward-char 4))

在当前光标处插入一个p tag。做法是插放一个串,然后将光标移回4位。

编写一个mode

理论上来讲,知道上面这些东西,你就有能力编写一个mode了。但是,编写mode毕竟是一个复杂的工作,需要编写者对elisp编程具有”hello world”以上很多级的熟练度。

在李杀网,作者有一个系列文章来讨论如何为一个编程语言编写mode

The End. Have fun!

Jan
31

[elisp]EmacsLisp 基础

Author laihj    Category 善其事     Tags ,

来源:李杀网

李杀网有一枚elisp教程我很喜欢,原因是它不解释太多的名词,而从实际动作方面入手,对我的胃口。

以下是相关的笔记。

运行方式:

作为一个实践的手册,第一件事当然是告诉如果运行一行代码,让你在看指南的过程中可以方便地动手尝试。

  • eval-last-sexp
  • 输入后把光标移到表达式后面,如“(+ 3 4)”后面,然后输入”Alt+x eval-last-sexp”或者使用快捷键”C-x C-e”,就可以在mini buffer看到这一句的运行结果”7″。

  • eval-region
  • 解释选中的区域。

  • ielm
  • 打开一个交互式的elips命令行解释器。

寻找帮助:

可以使用”Alt + x describe-function”(快捷键”C-h f”)来查找一个函数的用法。也可以使用”Alt+x elisp-index-search”在手参考手册中查询。

常用函数

打印

  (message "hi")
  (message "her age is:%d" 16) ;%d 数字
  (message "her name is: %s" "Vicky") ;%s 字符串
  (message "her min init is: %c" 86) ;%c 字符

注意:你可以在*message* buffer中看到打印出来的结果。

运算函数

(+ 4 5 1 )  ;=10
(- 9 2)     ;=7
(* 2 3)     ;=6
(* 2 3 4)   ;=24
(/ 7 2)     ;=3  结果的整数部分
(/ 7 2.0)   ;=3.5
(% 7 4)     ;=3 余数

注意,如果你的操作数是小数,必须把后面的0带上。就是说你应该写2.0,而不是2.。

  (integerp 3.)  ;T
  (floatp 3.)    ;nil
  (floatp 3.0)   ;T

以字符p结尾的函数通常意味着它的返回值是True或者False。p意味着”predicate”(判定)。

True和False

在elisp中,标识”nil”代表false,其它的一切都是true,包括0。”nil”是空链表”()”的同义词。所以”()”也是false。

按惯例,标识”t”用来表示true。

  (and t nil) ; nil
  (or t nil)  ; t

在elisp中没有布尔型,只需记住”nil”和”()”是false,其它一切都是true。

比较函数

比较数字

  (< 3 4) ;小于
  (<= 3 4)  ;小于等于
  (> 3 4)  ;大于
  (>= 3 4) ;大于等于
  (= 3 3)  ;等于

比较字符串

  (string= "this" "thiS")
  (string< "a" "b")
  (string< "B" "b")

在字符串比较中大小写是敏感的。比较依据是字典顺序。

要比较两个sysbols是否有相同的数据类型和值,使用"equal"。

(equal "abc" "abc") ;t
(equal 3 3)         ;t
(equal 3 3.0)       ;nil.类型不同
(equal '(3 4 5) '(3 4 5))  ; t
(equal '(3 4 5) '(3 4 "5"))  ;nil

在Elisp中并没有"!="或者“not-equal”。判断不等,可以在对整个等式取非。

  (not (= 3 4))  ;t

全局和局部变量

"setq"用于给变量赋值。格式一般为"setq 变量1 值1 变量2 值2..."

在lisp中,变量不需要声时,并且是全局的。

  (setq a 3 b 2 c 7)  ;三个变是,a=3 b=2 c=7

定义局部变量,使用let。格式为"(let (变量1 变量2) body)"。"body"代表其它的表达式。其中最后一个表达式的取值是整个语句块的返回值。

  (let (a b)
  (setq a 3)
  (setq b 4)
  (+ a b)
  )

a和b都是这个语句块的局部变量,返值是最后一个表达式"(+ a b)"的取值。

另一种格式是"(let ((变量1 值1)(变量2 值2)) body )"。例如:

  (let ((a 3) (b 4))
  (+ a b)
  )

如果你的变量很少,并且值都是已经确定的,可以用这种方法。

表达式块

有时需要把一些表达式括起来。这时可以使用"progn"。

  (progn (message "hi"))

它相当于

  (message "hi")

"progn"类似于C语言中的"{...}"。它使用于某些需要把语句合并起来的场合,其实这跟C语言中也是一样的。比如:"(if something (progn this that))"。这里,如果把progn去掉,变成"(if somethong this that",在lisp中表示如果something,那么this,否则that。在有progn把this和that括真情 为情况下,表示的是如果something,那么执行this和that。

If then else

格式为"(if test then )"。"else"是可选的。例:

  (if (< 3 2) (message "yes")))
  (if (< 3 2) (message "yes") (message "no")))

迭代循环

使用while。

  (setq x 0)
  (while (< x 4)
     (princ (format "yay %d." x))
     (setq x (+ 1 x)))

在elisp中,并没有for语句。

Lists

在lisp中的List是这样的:“'(x y z)”。括号前面那个单引号是很重要的。不需要太在意它的含义,把它当成句法的一部份即可。

  (message "%S" '(a b c))
  (setq mylist '(a b c))  ;定义
  (let ((x 3) (y 4) (z 5))
      (message "%S" (list x y z))
  )

以下是List的一些函数:

Function 目的
(car mylist) 取第一个元素
(nth n mylist) 最第n个元素
(car (last mylist)) 取最后一个元素
(cdr mylist) 从第二个到最后一个
(nthcdr n mylist) 从第n个到最后一个元素
(butlast mylist n) 不包含n到最后一个元素

这里所说的n,都是从0开始的。

下列是一些例子。

  (car (list "a" "b" "c"))
  (nth 2 (list "a" "b" "c"))
  (last (list "a" "b" "c"))
Basic List Functions
Function 目的
(length mylist) List长度
(cons x mylist) 把x加到list前面
(append mylist1 mylist2) 连接两个List

例如:

  (length (list "a" "b" "c"))
Function Purpose
(pop mylist) 删除第一个元素并返回
(nbutlast mylist n) 删除第n个元素,返回删除后的list.
(setcar mylist x) 替换第一个元素,并返回
(setcdr mylist x) 替换除第一个之外的所有元素

遍历运算数组

  (mapcar '1+ '(1 2 3 4))

上例的做所是遍历list中的每一个元素,并对它进行"1+"的操作。

当然,也可以用while循环来完成这件事。

定义函数

基本的函数定义方式是"defun (param) "doc" "。

  (defun myFunction () "testing" (message "Yay!"))

myFunction是函数名,这个函数无参,函数注释"testing",后面是函数体。

可以在doctsing后面加一个"interactive"来使得函数能跟环境进行交互(在emacs中,就呆以可用"Alt + x"来调用)

interactive的一些常用语法:

  • (interactive) 无参
  • (interactive "n") 一个数字参数
  • (interactive "s") 一个字符串参数