Emacs-Lisp学习笔记

探索Emacs-Lisp语法的点点滴滴。

Emacs-Lisp学习笔记

基础语法

function definitions

defun template

(defun functoin-name (arguments …) "optional documentation …" (interactive argument-passing info) ; optional body…)

  • example
    (defun multiply-by-seven (number)
      "Multiply by server"
      (* number 7))
    

install a func

evaluate defunc

make a function interactive

(defun multiply-by-seven (number) ;interactive version
  "Multiply by seven"
  (interactive "p")  ;the "p" tells Emacs to pass the prefix argument to the function and use its value for the argument of the function.
  (message "The result is %d" (* 7 number)))

diffrent options for interactive

funcall

调用另外一个函数.

参数

  • prefix-numeric-value

    获取传入进来的参数值

内联函数

定义内联函数使用 defsubst

声明一个函数

(declare-function FN FILE &optional ARGLIST FILEONLY)

声明函数FN定义在文件FILE中。

宏的参数是隐式引用起来的,也就是说,当宏被调用时,它的参数不会被解 析,而是原样传递到宏内部。

例子

(defmacro incr (var)
  "Add one to the named variable"
  '(setq ,var (+ ,var 1)))

其中 macroexpand 用于调试,可以查看一个宏展开后的样子。','表示 backquote, 被backquote的元素在执行宏定义的代码时,不会被解析。

(defmacro limited-save-excursion (&rest subexprs)
  "Like save-excursion, but only restores point."
  (let ((orig-point-symbol (make-symbol "orig-point")))
    '(let ((,orig-point-symbol (point)))
       (unwind-protect
           (progn ,@subexprs)
         (goto-char ,orig-point-symbol)))))

",@"跟","一样,原封不动的保留传入进来的参数,且会自动去掉最外层的 括号。

变量

普通变量

defvar

常量

defconst

可配置变量

defcustom
(defcustom org-timer-display 'mode-line
  "When a timer is running, org-mode can display it in the mode
line and/or frame title.
Allowed values are:

both         displays in both mode line and frame title
mode-line    displays only in mode line (default)
frame-title  displays only in frame title
nil          current timer is not displayed"
  :group 'org-time
  :type '(choice
          (const :tag "Mode line" mode-line)
          (const :tag "Frame title" frame-title)
          (const :tag "Both" both)
          (const :tag "None" nil)))

type的类型有: choice, boolean, alist, plist, "set greedy: t"

变量赋值let

create a name for a local variable let* : set each variable in its varlist in sequence 但通常情况下,应该使用 let ,因为它在大多数情况下,更有效率。

in Emacs Lisp, scoping is dynamic, not lexical

  • sample
    (let ((zera 'strips)
          (tiger 'fierce))
    (message "One kind of animal has %s and another is %s. " 
         zera tiger))
    

条件与控制

if

(if true-or-false-test
    action-to-carryout-if-test-is-true)
  • sample
    (if (> number 5)
        (message "%d is larger than 5" number))
    
    (if true-or-false-test
        action-to-carryout-if-test-is-true
     action-to-carryout-if-test-is-false)
    
  • sample
    (if (> number 5)
        (message "%d is larger than 5" number)
    (message "%d is not larger than 5" number))
    
  • nil

    nil mean an empty list or false

  • save-excursion

    save the current location of point and mark, execute the body of function, and then restores point and mark.

    (defun simple-beginning-of-buffer ()
      "Move point to the beginning of buffer,
    leave mark at previouse position"
      (interactive)
      (push-mark)
      (goto-char (point-min)))
    

unless

catch

(catch TAG BODY…)

Eval BODY allowing nonlocal exits using ‘throw’. TAG is evalled to get the tag to use; it must not be nil.

Then the BODY is executed. Within BODY, a call to ‘throw’ with the same TAG exits BODY and this ‘catch’. If no throw happens, ‘catch’ returns the value of the last BODY form. If a throw happens, it specifies the value to return from ‘catch’.

  • 示例
    (defun test ()
      (catch 'exit
        (throw 'exit "this is the return values")))
    

condition-case

(condition-case nil
        (load (concat (file-name-directory load-file-name)
                      "org-loaddefs.el")
              nil t t t)
      (error
       (message "WARNING: No org-loaddefs.el file could be found from where org.el is loaded.")
       (sit-for 3)
       (message "You need to run \"make\" or \"make autoloads\" from Org lisp directory")
       (sit-for 3)))

条件处理函数,包含一系列的HANDLER处理

cond

(cond
 ((integerp offset) (setq delta offset)) ;; 条件1
 ((stringp offset) (setq delta (org-timer-hms-to-secs offset)));;条件2
 (t  ;;默认条件,任何时候都会执行
  (setq def (if (org-in-regexp org-timer-re)
                (match-string 0)
              "0:00:00")
        s (read-string
           (format "Restart timer with offset [%s]: " def)))
  (unless (string-match "\\S-" s) (setq s def))
  (setq delta (org-timer-hms-to-secs (org-timer-fix-incomplete s)))))

make-symbol

创建一个全新的对象,能确保不会与现存的对象冲突。

错误处理

unwind-protect函数专门用于调用某个程序发生错误时,自动回退到调用前的状态。

Point Marker

处理命令行参数

–script模式下,与处理命令行参数相关的常用变量有如下几个:

  1. command-line-args-left 尚未处理的命令行参数的列表
  2. command-line-args 传递给emacs完整的命令行参数列表,一般用于获取当前执行的脚本名。
  3. command-switch-alist 检查command-line-args-left是否包含以 - 开头的选项,并在变量中查 找并运行对应的handler-function。每处理完一个选项后,就将该参数从 command-line-args-left中删除。该元素为'(option . handler-function)的alist。如果该参数后面还有其他的参数,则在该 函数中可以通过变量'command-line-args-left来获取剩余的命令行参数。
  4. command-line-functions 该变量是一系列函数的列表,这些函数用来处理无法识别的command-line 参数。只要处理的函数不返回非nil的值,则该函数会依次被不同的函数 处理。这些函数被调用时并不传递参数,但这些函数内可以通过变量 argi 获取当前未被处理的命令行参数。

EmacsScript的执行顺序:

  1. Emacs读取并执行EmacsScript中的内容
  2. Emacs遍历command-line-args-left中的参数,对于 command-switch-alist中的参数调用对应的函数,对于不在 command-switch-alist中的参数依次调用command-line-functions中的函 数。
  3. 倘若command-line-functions中没有定义函数或者某参数在依次调用 command-line-functions中的函数后都返回nil的话,那么该参数交由 emacs本身处理。

脚本示例:

#!/bin/sh
":"; exec emacs --script "$0" "$@" #-*- mode: emacs-lisp; lexical-binding: t; -*-
(message "Hello world")

 ;;命令行相关常用变量

 ;; 1. command-line-args-left
 ;; 传递给可执行程序的参数,不包含应用程序本身    
 (print (format "command-line-args-left=%s" command-line-args-left))

 ;;2. command-line-args
   ;;一般用于获取可执行程序本身的名称
   (print (format "command-line-args=%s\n" command-line-args))
   (print (format "$0=%s" (nth 2 command-line-args)))


   ;;3. command-switch-alist
     (defun print-option-value (option)
      (print (format "command-line-args-left=%s\n"  command-line-args-left))
      (print (format "value of %s is %s\n" option (car command-line-args-left)))
      (pop command-line-args-left)
     )

     (add-to-list 'command-switch-alist '("-f" . print-option-value))

     (defun print-option ()
      (print (format "command-line-args-left=%s\n" command-line-args-left))
      (print (format "option is %s\n" argi))
     )

     (add-to-list 'command-line-functions #'print-option)

     (print (shell-command-to-string "ls -l"))

数据结构

向量

  1. 创建一个向量
    ;;创建一个包含rows个元素的向量,初始值为nil
    (make-vector rows nill)
    
  2. 访问向量元素
    (aref VECTOR INDEX)
    
  3. 设置一个向量元素
    (aset VECTOR INDEX NEW-ELEMENT-VALUE)
    

数组

字符串

  • replace-regexp-in-string 替换匹配模式的字符串中字符
  • intern
    require (intern (concat "ob-" lang))
    

    返回规范的符号

列表

普通列表

  • delete-dups 删除重复元素
  • copy-sequence 复制列表

Assocation List

Property List

 (plist-get '(foo 4) 'foo)
     ⇒ 4
(plist-get '(foo 4 bad) 'foo)
     ⇒ 4
(plist-get '(foo 4 bad) 'bad)
     ⇒ nil
(plist-get '(foo 4 bad) 'bar)
     ⇒ nil

主要函数:

  • plist-get plist property
  • plist-put plist property value
  • lax-plist-get plist property 使用equal比较属性
  • lax-plist-put plist property value 使用equal比较属性
  • plist-member plist property

evaluate an expression

C-x C-e

Syntax Table

char-syntax

Buffer

Buffer Names

buffer-name

return file name, make sure this buffer is alive.

buffer-file-name

return full path

Buffers

current-buffer

other-buffer

switch-to-buffer

get-buffer-create

如果存在对应名字的buffer,则返回该buffer,否则,创建一个对应名称的buffer.

Buffer size

buffer-size

point, point-min, point-max

the size of the current buffer

Buffer Operations

with-current-buffer

将buffer临时作为当前buffer执行

with-temp-current

;; use a temp buffer to manipulate string
(with-temp-buffer
  (insert myStr)
  ;; manipulate the string here
  (buffer-string) ; get result
)

File Operations

Open, Append, Write

;; open a file (returns a buffer)
(find-file "/home/jane/test.txt")

;; save current buffer (write to the associated file)
(save-buffer)

;; like “Save As”. Save current buffer, close it, and open the new saved
(write-file "/home/jane/new.txt")

;; append text between positions 100 to 200 to file 
(append-to-file 100 200 "/home/jane/test.txt")

;; close a buffer
(kill-buffer myBuffName)

Rename, Copy, Delete

(rename-file "/home/jane/test1.txt" "/home/jane/test2.txt")

(copy-file "/home/jane/test1.txt" "/home/jane/test2.txt")

(delete-file "/home/jane/test2.txt")

(copy-directory "/home/jane/stuff" "/home/jane/stuff-backup")

;; delete a whole dir. (new in emacs 23)
(delete-directory "/home/jane/stuff" t)

File Name Manipulation

; get the dir path part
(file-name-directory "/home/jane/xyz.txt") ; "/home/jane/"

;; get filename part
(file-name-nondirectory "/home/jane/xyz.txt") ; "xyz.txt"

;; get filename's extension
(file-name-extension "/home/jane/cat.txt.jpg") ; "jpg"

;; get filename without extension
(file-name-sans-extension "/home/jane/cat.txt.jpg") ; "/home/jane/cat.txt"

;; get relative path
(file-relative-name "/home/jane/b/cat.jpg" "/home/jane/") ; "b/cat.jpg"

;; get full path
(expand-file-name "test.el")
;; sample output
;; "/home/jane/misc/emacs/test.el"

Window

window-buffer

返回当前窗口中显示的buffer

Minor Mode

Emacs uses the concept of a mode to encapsulate a set of editing behaviors.

A minor mode, adds to a buffer a package of functionality that doesn't fundamentally change the way editing in the buffer is performed.

实现步骤

  1. 选择一个名称
  2. 定义一个变量: name-mode, 并使它buffer-local,
    (defvar refill-mode nil
      "Mode variable for refill minor mode.")
    (make-variable-buffer-local 'refill-mode)
    
  3. 定义一个叫name-mode的命令,该命令应该接收一个可选的参数。
    (defun refill-mode (&optional arg)
      "Refill minor mode."
      (interactive "P")
      (setq refill-mode
            (if (null arg)
                (not refill-mode)
              (> (prefix-numeric-value arg) 0)))
      (if refill-mode
          code for turning on refill-mode
          code for turning offrefill-mode) )
    
  4. 向minor-mode-alist添加一项
    (if (not (assq 'refill-mode minor-mode-alist))
        (setq minor-mode-alist
              (cons '(refill-mode " Refill")
                    minor-mode-alist)) )
    

示例

(define-minor-mode next-error-follow-minor-mode
  "Minor mode for compilation, occur and diff modes.
With a prefix argument ARG, enable mode if ARG is positive, and
disable it otherwise.  If called from Lisp, enable mode if ARG is
omitted or nil.
When turned on, cursor motion in the compilation, grep, occur or diff
buffer causes automatic display of the corresponding source code location."
  :group 'next-error :init-value nil :lighter " Fol"
  (if (not next-error-follow-minor-mode)
      (remove-hook 'post-command-hook 'next-error-follow-mode-post-command-hook t)
    (add-hook 'post-command-hook 'next-error-follow-mode-post-command-hook nil t)
    (make-local-variable 'next-error-follow-last-line)))

Major Mode

实现步骤

  1. 选择一个名称name
  2. 创建一个文件name.el, 它包含相关代码
  3. 定义一个叫name-mode-hook的变量,包含用户自定义的钩子函数
  4. 如果有需要,可以定义一个模式相关的keymap,名称为name-mode-keymap
    (defvar name-mode-map nil
      "Keymap for name major mode.")
    (if name-mode-map
        nil
      (setq name-mode-map (make-keymap))
      (define-key name-mode-map keysequence command))
    

    如果定义的keybindings不多的话,则可以使用 make-sparse-keymap.

  5. 如果有需要,可以定义一个模式相关的syntax table,名称为 name-mode-syntax-table.
  6. 如果有需要,可以定义一个模式相关的abbrev table,名称为 name-mode-abbrev-table.
  7. 定义一条名为name-mode的命令,它没有参数。 当执行时,需要执行如下 一些步骤:
    1. 必须调用kill-all-local-variables, 清掉所有buffer-local的变量。
      (kill-all-local-variables)
      
    2. 设置变量major-mode的值为name-mode.
      (setq major-mode 'name-mode)
      
    3. 设置变量mode-name描述该模式,用于在mode line中显示
      (setq mode-name "Name Mode")
      
    4. 如果有的话,必须在name-mode-map上调用use-local-map以安装模式 相关的keymap.
    5. 调用用户定义的钩子函数.
      (run-hooks 'name-mode-hook)
      
  8. "provide"该模式
    (provide 'name) ;;allow user to (require 'name)
    

示例

简单示例

(defun fundamental-mode ()
  "Major mode not specialized for anything in particular.
Other major modes are defined by comparison with this one."
  (interactive)
  (kill-all-local-variables)
  (run-mode-hooks))

完整实例

(defvar quip-mode-hook nil
  "*List of functions to call when entering Quip mode.")
(defvar quip-mode-map nil
  "Keymap for quip major mode.")

(defalias 'backward-quip 'backward-page)
(defalias 'forward-quip forward-page)
(defalias 'narrow-to-quip 'narrow-to-page)
(defalias 'what-quip 'what-page)

(if quip-mode-map
    nil
  (setq quip-mode-map (copy-keymap text-mode-map))
  (define-key quip-mode-map "\C-x[" 'backward-quip)
  (define-key quip-mode-map "\C-x]" 'forward-quip)
  (define-key quip-mode-map "\C-xnq" 'narrow-to-quip)
  (define-key quip-mode-map "\C-cw" 'what-quip))

(defun quip-mode ()
  "Major mode for editing Quip files.
Special commands:
\\{quip-mode-map}"
  (interactive)
  (kill-all-local-variables)
  (text-mode) ;first, set things upfor Text mode
  (setq major-mode 'quip-mode) ; now, specializefor Quip mode
  (setq mode-name "Quip")
  (use-local-map quip-mode-map)
  (make-local-variable 'paragraph-separate)
  (make-local-variable 'paragraph-start)
  (make-local-variable 'page-delimiter)
  (setq paragraph-start "%%\\I[ \t\n\^L]")
  (setq paragraph-separate "%%$\\ [ \t\^L]*\$")
  (setq page-delimiter "^%%$")
  (run-hooks quip-mode-hook))
(provide 'quip)

define-derived-mode

从现存的模式中派生出一个新的模式。

(require 'derived)
(define-derived-mode quip-mode text-mode "Quip"
  "Major mode for editing Quip files.
Special commands:
\\ quip-mode-map}"
  (make-local-variable 'paragraph-separate)
  (make-local-variable 'paragraph-start)
  (make-local-variable 'page-delimiter)
  (setq paragraph-start "%%\\[[ \t\n\^L]")
  (setq paragraph-separate "%%$\\ [ \t\^L]*$")
  (setq page-delimiter "^%%$"))
(define-key quip-mode-map "\C-x[" 'backward-quip)
(define-key quip-mode-map "\C-x]" 'forward-quip)
(define-key quip-mode-map "\C-xnq" narrow-to-quip)
(define-key quip-mode-map "\C-cw" 'what-quip)
(provide 'quip)

示例

;;; strace-mode.el --- strace output syntax highlighting
;; COPYRIGHT © 2016, by Preston Moore

;; Author: Preston Moore (prestonkmoore@gmail.com)
;; Version: 0.0.2
;; Keywords: languages

;; This file is not part of GNU Emacs.

;;; License:


;;; Commentary:

;; strace syntax highlighting

;; strace syntax highlighting


;;; Code:

;; create the list for font-lock.
;; each category of keyword is given a particular face
(defvar strace-font-lock-keywords)
(setq strace-font-lock-keywords `(
                                  ("^\\([0-9]+\\) " . (1 font-lock-warning-face))
                                  ("^[0-9]+ \\([a-zA-Z0-9_]*\\)(" . (1 font-lock-constant-face))
                                  (" = \\(0x[[:xdigit:]]+\\).*$" . (1 font-lock-type-face))
                                  (" = \\(-?[[:digit:]?]+\\).*$" . (1 font-lock-type-face))
                                  (" = 0x[[:xdigit:]]+ \\([[:upper:]]+\\).*$" . (1 font-lock-negation-char-face))
                                  (" = -?[[:digit:]?]+ \\([[:upper:]]+\\).*$" . (1 font-lock-negation-char-face))
                                  (" \\((.*)\\)$" . (1 font-lock-comment-face))
                                  ("0x[[:xdigit:]]+" . font-lock-type-face)
                                  ("-?[[:digit:]]+" . font-lock-type-face)
                                  )
)

;;;###autoload
(define-derived-mode strace-mode fundamental-mode
  "strace mode"
  "Major mode for strace output"

  (setq font-lock-defaults '((strace-font-lock-keywords)))
)

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.strace\\'" . strace-mode))

;; add the mode to the `features' list
(provide 'strace-mode)

;; Local Variables:
;; coding: utf-8
;; End:

;;; strace-mode.el ends here

一些功能函数

sit-for

重新显示,并等待几秒钟

(sit-for 3)