创建一个帖子,记录自己学习 Elisp 的经过

sub 不是小写的意思,是取一部分。(equal (buffer-substring ...) 结果等于 (substring (buffer-string) ...)

1 个赞

终于重启学习 Elisp 了。主要是受今天 Emacs Talk 采访懒猫的影响。这一次,从李杀的 Elisp 教程开始,学习到 Elisp Basic 这一章,用懒猫推荐的 ielm 来测试每一个命令,得到了很有趣的体验,我将我的笔记暂时先发布在这里:

Elisp Basic

Elisp 基础数据类型以及数据的表达方式

Printing

  1. message

    (message) 在 Emacs 的 minibuffer 里发送某些数据,并在 minibuffer 中显示。 (message FORMAT-STRING &rest ARGS)

    1. 与不同变量值结合的方式

      1. %d 表示 decimal number(不包小数点后面的整数)

        (message "age is %d " 16) 测试:不能表示 1/3 这类表达的分数

      2. %s 表示 string(字符串)

        (message “name is %s” “Joe”) 测试:中英文皆可

      3. %c is char by unicode codepoint in decimal

        (message "Mid init is %c“ 65) 返回结果:“Mid init is A” 测试:一开始不太理解为啥输入 65 的值表达是 A。检索 unicode codepoint,找到一些编码进行测试,返回错误信息【值错了】。后来,M-x 输入 unicode 指令,找到一个命令 counsel-unicode-char,可以检索 Emacs 内置的编码信息,于是输入不同的数值,可得到用这一数字表达的【字符】。理解为,只能表达 Emacs 内置的 unicode 编码。

  2. insert

    (insert) 在当前 buffer 中,当前光标的位置中输入【字符串】。 (insert &rest ARGS)

  3. print

    (print) Print lisp object. Output can be read back by function read. Optional arg for a target buffer, or other functions. (print OBJECT &optional PRINTCHARFUN)

    测试:关于 print 指令,李杀的解释是,它可以 print 任何数据格式,The “OBJECT” is any elisp object you want to print. It can be any lisp datatype, such as string, number, list, buffer, frame, …, etc.

    print 和 insert 的区别是,insert 操作当前的 buffer,print 则是操作新 buffer。

  4. prin1

    print 的变式,性质作用一样,不同的是【不会在末尾另起一行】。 (prin1 OBJECT &optional PRINTCHARFUN)

  5. princ

    (princ OBJECT &optional PRINTCHARFUN) Print without newline nor delimiters. For human reading. 测试:输出的结果更符合人类阅读习惯,通常可以理解为把【数值旁边的双引号给去掉】。

Arithmetic 数学运算

Convert Float/Integer 浮点数/整数的转换

  1. (float 3) 用于将整数转换成浮点数

    3.0

  2. (truncate 3.3) 用于将浮点数转换成整数

    3

  3. (floor 3.3) 用于将浮点数转换成整数,但取小值

    3p

  4. (ceiling 3.3) 用于将浮点数转换成整数,但取大值

    4

  5. (round 3.4) 根据四舍五入的法则,来取大小值

    (round 3.4) → 3 (round 3.5) → 3 (round 3.6) → 4

  6. 字符串/数值之间的转换

    1. (string-to-number)

    2. (number-to-string)

5 个赞

今天还是在 Elisp Basic 章节,来到条件判断这一章,用 ielm 来验证各种函数非常有趣。

Ture, False 判断值的真/错

【理解】判断 Ture, False 比较重要,是用于判断某些条件——也就是说,有些程序本身是应当在某些条件下才执行,或者判断某个状态为出错,这些都是编写程序时需要注意的。

在 Elisp 里通常表达 true、false 的方法

在 Elisp 世界里,只有 nil 表达 false,只要不用 nil 表达,则都是 true。 nil 等于 () 所以 () 也表达 false。 字符 t 通常表达 true。

几种常见的表达 false 的方式

  1. (if nil “yes” “no”)

    测试:nil 似乎通常表示括号里所有的值,除了最后一个值是 true 外,其余为 false,若执行(if)语句则反馈表达 true 的 string

    ELISP> (if nil "yes" "no")
    "no"
    ELISP> (if nil "yes" "yes")
    "yes"
    ELISP> (if nil "no" "no")
    "no"
    ELISP> (if nil "1" "0")
    "0"
    ELISP> (if nil "nil" "ture")
    "ture"
    ELISP> (if nil "no" "yes")
    "yes"
    ELISP> (if nil "对" "错")
    "错"
    ELISP> (if nil "*" "-")
    "-"
    ELISP> (if nil "-" "*" "1")
    "1"
    ELISP> (if nil "1" "2" "3")
    "3"
    
  2. (if () “yes” “no”)

    测试:() 也可以表达 nil,用法和 (if nil “yes” “no”) 是一致的。

    ELISP> (if () "yes" "no")
    "no"
    ELISP> (if () "1" "0")
    "0"
    ELISP> (if () "0" "1")
    "1"
    ELISP> (if () "_" "*" "1")
    "1"
    ELISP> (if () "1" "2" "3")
    "3"
    
  3. (if '() “yes” “no”)

    :question:测试:’() 也可以表达 nil,用法和 (if nil “yes” “no”) 是一致的。暂时不知道符号 ' 的作用,但在翻看各种 Package 和 Emacs 的配置时,这个' 经常出现, 猜测 是用于标记 Symbol,或者别的数据类型。

    ELISP> (if '() "_" "*" "1")
    "1"
    ELISP> (if '() "对" "错")
    "错"
    ELISP> (if '() "1" "2" "3")n
    "3"
    

几种表达 true 的方式

虽然说 Elisp 里只要是不表达 nil 的地方,就默认是 true。不过,为了方便区分表达,Elisp 通常会用字符 t 来来表达 true。

  1. (if t “yes” “no”)

    测试:表达第一个值为 ture,之后的为 false,返回第一个值

    ELISP> (if t "yes" "no")
    "yes"
    ELISP> (if t "对" "错")
    "对"
    ELISP> (if t "_" "*" "1")
    "_"
    ELISP> (if t "1" "2" "3")
    "1"
    
  2. (if 0 “yes” “no”)

    测试:0 也能表达 true

    ELISP> (if 0 "yes" "no")
    "yes"
    
  3. (if “” “yes” “no”)

    测试: "" 也表达 true

    ELISP> (if "" "yes" "no")
    "yes"
    
  4. (if [] “yes” “no”)

    测试:根据李杀的解释, [] 表达 0

    ELISP> (if [] "yes" "no")
    "yes"
    

Boolean Functions 布尔函数

  1. (and t nil)

    测试:在 and 函数下,只有后面跟着的数值,只要有一项是 nil,返回的结果就是 nil

    ELISP> (and nil 1 2 3 4 5 s * 好好好)
    nil
    
  2. (or t nil)

    测试:如果 or 命令后跟着 tnil ,它会返回 t 。至于 or 命令本身,它好像主要是返回非空的第一个值

    ELISP> (or t nil)
    t
    ELISP> (or t 1 2 3 4 5 s * 好好好)
    t
    ELISP> (or nil 1 2 3 4 5 s * 好好好)
    1 (#o1, #x1, ?\C-a)
    

Comparing Numbers 数字大小的对比

顾名思义。仅记录命令例子,只能针对数字类型的数值。

  1. (< 3 4) 小于

  2. (> 3 4) 大于

  3. (<= 3 4) 小于等于

  4. (>= 3 4) 大于等于

  5. (= 3 3) 等于

    整数和浮点数也可以对比

    (= 3 3.0000000000)
    
  6. (/= 3 4) 不等于

Equality Test 相差测试

不知道 Eqality Test 怎么翻译,就按照李杀教程里的例子理解来叫了。 我的理解为,它是测试两个或多个值之间的数据类型是否相符,两个或多个值要完全一样否则返回结果 nil,或者直接就是报错。 用途:可能是用于检查某些值是否可以对应,可以作为某种条件来判断。

  1. (string-equal S1 S2)

    一般是比较两个 string 是否相等—— (string-equal S1 S2) 测试:但李杀带来了另外一个比较,就是它还可以比较 symbol 和 string,以及比较 symbol 和 symbol 之间是否相等。 但 string-equal 不能比较两个 list 之间的差异,而 equal 可以;另外和 equal 一样 string-equal 也不能比较整数和浮点数,返回结果为 nil。

    ELISP> (string-equal "abc" "abc")
    t
    ELISP> (string-equal "abc" "Abc")
    nil
    ELISP> (string-equal "abc" 'abc)  ;比较 string 和 symbol
    t
    ELISP> (string-equal 'abc 'abc)   ;比较 symbol 和 symbol
    t
    ELISP> (string-equal "3.0" "3") ;比较 integer number 和 floating number,因为数据类型的不同,nil
    nil
    ELISP> (string-equal "3" "3")
    t
    ELISP> (string-equal "3.0" "3.0")
    t
    ELISP> (string-equal '(3 4 5) '(3 4 5))
     Eval error ***  Wrong type argument: stringp, (3 4 5)
    ELISP> (equal '(3 4 5) '(3 4 5)) ;equal 可以比较两条 list
    t
    
  2. (equal S1 S2)

    参考 (string-equal) 一节。李杀还给出 equal 另外一个用法,就是在 equal 命令前加 not 命令,代表数值不相等这一结果为 true。 测试:

    ELISP> (not (equal 3 4))
    t
    ELISP> (not (equal "3" "3.0"))
    t
    
  3. (eq SYMOL1 SYMOL2)

    比较两个 symbol 的对象是否相等, 测试:

    ELISP> (eq 'x 'x)
    t
    ELISP> (eq obj1 obj2) ;字符返回错误
     Eval error ***  Symbol’s value as variable is void: obj1
    ELISP> (eq "e" "e") ;string 返回nil
    nil
    ELISP> (eq 2.1 2.1) ;浮点数返回nil
    nil
    
  4. (eql 0.1 0.1)

    eql 是用来比较两个完全一致的浮点数,是否一致 测试:

    ELISP> (eql 2.1 2.1)
    t
    ELISP> (eql "abc" "abc")
    nil
    ELISP> (eql 2.1 -2.1)
    nil
    
  5. :question:even, odd 偶数,奇数

    检查数字是否可以被 2 整除,写法如下,但不太理解后面跟着的数字 0 和 1 有什么区别。李杀没有给出具体的解释。

    ELISP> (= (% 6 2) 0)
    t
    ELISP> (= (% 6 2) 1)
    nil
    ELISP> (= (% 3 2) 1)
    t
    ELISP> (= (% 3 2) 0)
    nil
2 个赞

模余 2 的结果与 0 或者 1 比较,与 0 相等就是偶数,另一种就是奇数。

1 个赞

[] 是空的 vector, vector 在 Elisp 里面表示连续但不可改变大小的一片空间(随机访问时间复杂度 O(1))

(vectorp [])
;; => t

(numberp [])
;; => nil

估计你看错了或者李杀写错了

(require 'cl-lib)

(cl-evenp 2)
;; => t

(cl-oddp 2)
;; => nil
1 个赞

C-h f quote RET

另外 nil t () :keyword 还有数字字面量, [] 这些加不加 ' 都是一样的, 这种性质叫 self-quoting

李杀没写错,我没有理解 vector 的意思,他说是 vector 里的 elements 为 0

哦,还没学到 quote 的概念,我现在还停留在 Elisp Basic 一章,估计往下学就会看到,感谢你的解释。

非常郁闷,Emacs 中途崩溃了一次,导致今天有很多笔记丢失,好一段时间用来恢复笔记了。贴上今天的笔记,今天学习到【变量】部分。

Variables 变量

  1. Global Variables 全局变量 steq

    1. (setq [SYM VAL] …)

      测试:SYM 和 VAL 必须两个一组。

      ELISP> (setq a 3 b 2 c 7)
      7 (#o7, #x7, ?\C-g)
      ELISP> (setq a 3 4 5 6 7)
      Eval error ***  Wrong type argument: symbolp, 4 ;提示 symbol 错误 
      
  2. Local Variables 局部变量 let

    (let (var1 var2 …) body) 返回最后一则表达式的结果。 测试:

    ELISP> (let (a b) (setq a 3) (setq b 4) (+ a b))
    7 (#o7, #x7, ?\C-g)
    
  3. if Then Else

    1. (if COND THEN ELSE…)

      If COND yields non-nil, do THEN, else do ELSE… Returns the value of THEN or the value of the last of the ELSE’s. THEN must be one expression, but ELSE… can be zero or more expressions. If COND yields nil, and there are no ELSE’s, the value is nil.

      若 COND 返回的值是 non-nil,执行 THEN 部分,返回 THEN 部分的结果
      若 COND 返回的值不是 non-nil,执行 ELSE 部分,返回 ELSE 部分的结果
      测试:

      ELISP> (if (< 3 2) 7 8)
      8 (#o10, #x8, ?\C-h)
      ELISP> (if (> 3 2) 7 8)
      7 (#o7, #x7, ?\C-g)
      

      若 COND 返回的值是 nil 则直接返回 nil;若为 non-nil,则返回 ELSE 部分的结果 测试:

      ELISP> (if (< 3 2) (message "yes"))
      nil
      ELISP> (if (> 3 2) (message "yes"))
      "yes"
      
  4. Block of Expression 一块表达式? progn

    主要讲 progn 的用法,多个表达式组合在一起,以一个表达式写出来。返回最后一则表达式的结果。 测试:

    ELISP> (progn (message "a") (message "b"))
    "b"
    
1 个赞

今天工作十分繁重,晚上在论坛里水了几条帖子之后,还是滚回去学习了。今天终于把 Elisp Basic 一章过完了。非常有意思。

今天学习了 Loop、Define a Function。笔记如下。

  1. Loop 循环

    1. while

      elisp 里 Loop 最常用的命令是 while 。 语法: (while TEST BODY…)

      while 语句由 TEST 和 BODY 两部分组成。按照文档里的解释,TEST 部分返回的值是 non-nil,那么 BODY 部分会被按顺序执行,执行顺序是 TEST, BODY。一直到 TEST 部分返回的值为 nil。

      If TEST yields non-nil, eval BODY… and repeat. The order of execution is thus TEST, BODY, TEST, BODY and so on until TEST returns nil.

      :question:The value of a ‘while’ form is always nil.

      测试: :question:这段代码设 x = 0,在循环里, x 循环小于 4 次,循环时执行 print 表达式里的指令,然后就不知道了

      (setq x 0)
      (while (< x 4)
        (print (format "number is %d" x))
        (setq x (1+ x)))
      得到:
      "number is 0"
      
      "number is 1"
      
      "number is 2"
      
      "number is 3"
      

      第二段代码试出来了,我理解为 x = 32,循环次数 x 小于 127 次,输入 insert-char x 次,同时又设定 x = x + 1,所以最后循环输入了 insert-char 32 次到 128 次得到一个 char 列表。

      ELISP> (let ((x 32))  (while (< x 127)    (insert-char x)    (setq x (+ x 1))))
      nil
      ELISP>  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
      

      李杀提到,两种更加好用的命令是 dolistdotimes

    2. dolist

      语法: dolist (var list [result]) body 测试:

      (defun reverse (list)
        (let (value)
      	(dolist (elt list value)
      	  (setq value (cons elt value)))))
      
    3. dotimes

      语法: dotimes (var count [result]) body 测试:

      ELISP> (dotimes (i 100)
        (insert "I will not obey absurd orders\n"))
      nil
      ELISP> I will not obey absurd orders
      I will not obey absurd orders
      I will not obey absurd orders
      I will not obey absurd orders
      I will not obey absurd orders
      ……
      
  2. Break/Exit a Loop 中断/推出某个 Loop

    属于李杀 Elisp 教程里的高级部分,之后再来学习。 http://xahlee.info/emacs/emacs/elisp_break_loop.html

  3. Sequence , List, Vector

    同上。 http://xahlee.info/emacs/emacs/elisp_list_vs_vector.html http://xahlee.info/emacs/emacs/elisp_vector.html http://xahlee.info/emacs/emacs/elisp_list.html

  4. Define a Function 定义一项功能

    1. defun

      语法: (defun functionname (param1 param2 …) docstring body) 测试: :question:这里定义了一个功能名叫 myFunction,若定义成功,则输入 myFunction 命令会在 minibuffer 显示 Yay!

      (defun myFunction ()
        "testing"
        (message "Yay!"))
      
1 个赞

决定今天是复习日,反刍一下之前学习的基本概念,发现有的已经开始淡忘,比如比较难的 Loop 的概念和写法,已经有点模糊……还好的就是 True, False 还有印象。学习就是这样吧。

这几天出了很多事情,一是外公过世了,妈妈这边十分需要陪伴,二是工作方面的需要紧急处理的事情变多,今天终于忙得差不多。可以继续学习接下来的内容,值得高兴的是,我开始接触到更多的指令,目前进度在和指针有关的指令上。

Overview of Text-Processing in Emacs Lisp

通览 Emacs 的文本处理指令。

Example of Simple Elisp Functions

  1. Cursor Postion 指针位置

    1. (point) 返回当前指针位置

      测试:若指针在第一个字符的左边,返回 1。

      ELPL> (point)
      1
      
    2. 返回一个区域 reigion 开始/结束的位置

      1. (region-beginning)

      2. (regionend)

    3. 返回一当前行的开始/结束的位置

      1. (line-beginning-position)

      2. (line-end-position)

    4. 返回 buffer 的开始/结束的位置

      :question:李杀在这里多写了一句,「taking account of narrow-to-region」。不知道是什么意思。

Basic Function

Cursor Position Functions 指针

Cursor 指针

  1. Get Cursor Position 取得指针当前位置

    部分内容同上,省略。

    1. region-beginning/region-end

      测试:

      (region-beginning) ;返回选中文字开始的位置
      (region-end) ;返回选中文字结束的位置
      
    2. point-min/point-max

      关于 visible buffer 的开始/结束位置。 visible buffer 是指当前在显示的 buffer。 测试:

      (point-min) ;返回 visible buffer 开始的位置
      
      (point-max) ;返回 visible buffer 结束的位置
      
  2. Move Cursor 移动指针

    1. goto-char

      (goto-char POSITION) 将指针移动到一个确定的位置 测试:

      (goto-char 9399) 按下 C-x C-e 后,指针直接跳转到对应的位置
      
    2. forward-char

      (forward-char &optional N) 将指针往前移动 n 个字符,

      (forward-char 9)
      
    3. barckward-char

      (backward-char &optional N) 将指针往后移动 n 个字符

      (backward-char 9)
      
    4. beginning-of-line

      (beginning-of-line &optional N) 将指针移动到行首 测试:

      (beginning-of-line)
      
      (beginning-of-line 3) ;可以指定指针移动到下 N-1=2 行的行首
      
    5. end-of-line

      (end-of-line &optional N) 将指针移动到行末 测试

      (end-of-line)
      
      (end-of-line 2) ;可以将指针移动到下 N-1=1 行的行末
      
  3. Search Text and Move Cursor 搜索文本同时移动指针

    以下几个命令需要组合别的 string 来使用,目前超出我的……能力,尚未尝试,留待日后。

    1. search-forward

    2. re-search-forward

    3. search-backward

    4. re-search-backward

    5. skip-chars-forward

  4. Save Cursor Position 保存指针的位置

    当指针在移动,通常希望它能回到原来的位置上,以避免指针飞去一个奇怪的位置

    1. save-excuresion

      (save-excuresion BODY)

      (save-excursion 130) ; C-x C-e 执行之后,minibuffer 返回 130 的值
2 个赞

今天把李杀 Elips 里的 Basic Functions 章节学完。

  1. Text Editing Functions 文本编辑

    1. Insert Text 键入文本

      在当前指针位置键入 string 里包含的内容

      (insert "sun and moon")
      sun and moon ;指针移动到下方,按下 C-x C-e 执行,指针位置键入了 sun and moon
      
    2. Delet Text 删除文本

      1. delete-char

        从指针位置开始,向右删除 n 个字符

        (delete-char 9) ;按下 C-x C-e 执行后,光标后面 9 个字符都删除
        
      2. delete-region

        删除 2 个确定位置之间的文本

        (delete-region 151 152)
        (delete-region 152 151) ;delete-region 命令不要求数值是顺序的,只需要用数值表示确定位置即可
        
      3. erase-buffer

        将整个 buffer 里的文本全部清除 测试:成功,将 scratch buffer 里的内容全部清除

        (erase-buffer)
        
      4. delete-and-extract-region

        删除两个位置之间的文本,同时返回被删除的文本

        (delete-and-extract-region 3 20) ;返回的文本显示在 message
        
  2. String Functions 关于 string 的功能

    1. Basic String Functions 基本 string 功能

      1. Length 长度

        string 的长度,计算的是字符长度

        (length "abcd")
        >4
        
      2. Substring

        返回 substring 里从位置 FROM 到位置 TO 的的内容,起始位置以 0 为代表 写法: (substring STRING &optional FROM TO)

        (substring "abc123" 0 3)
        >"abc"
        
      3. Join Strings

        将两条 string 合并为一条

        (concat "some" "thing")
        >"somthing"
        
      4. Split String

        将一条 string 里都某一元素【拆除】,返回另外一条列表

        (split-string "xy_007_cat" "_")
        >("xy" "007" "cat)
        
      5. String To/From Number

        (string-to-number "3") ;将 string 转变为数据格式 number
        >3
        
        (number-to-string 3) ;将数据格式 number 转变为 string
        >"3"
        
        (format "%d" 3) ;效果和 number-to-string 相同,but also do format
        
    2. Buffer Text to String Buffer 上的文本转换成 string

      感觉这是一个非常重要的功能,比如说,Emacs 很多插件是其实就是将一个 buffer 里的内容,显示在另外一个地方。 这里李杀单独另外开辟了一章进行命令的讲解。我不一一细看了。先把命令记录下来。

      1. Get String from Region 从特定的区域里的文本转换成 string

        (buffer-substring-no-properties 3 99)
        >"XahLee Elisp
        :BACKLINKS:
        [2022-03-28 Mon 00:30] <- [[file:~/我的坚果云/Diary/2022.org::*想继续学习 Elisp -"
        
      2. Get Text Selection

        (buffer-substring-no-properties (region-beginning) (region-end))
        >****** Get Text Selection
        #+begin_src
        (buffer-substring-no-properties (region-beginning) (region-end))"
        
      3. Get Current Char

        (char-before) ;❓不明白为何返回的值是 41
        >41
        
        (char-after) ;❓不明白为何返回的值是10
        >10
        
      4. Get Current Word

        (current-word)
        >"current-word"
        

        后面的比较复杂,我略过了。

    3. String to Buffer 把 string 里的内容转换到 buffer 上

      (with-temp-buffer
         (insert bigString)
         (goto-char (point-min))
         (buffer-string))
      

      报错,错误信息:

      Debugger entered--Lisp error: (void-variable bigString)
        (insert bigString)
        (progn (insert bigString) (goto-char (point-min)) (buffer-string))
        (unwind-protect (progn (insert bigString) (goto-char (point-min)) (buffer-string)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))
        (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert bigString) (goto-char (point-min)) (buffer-string)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer))))
        (let ((temp-buffer (generate-new-buffer " *temp*" t))) (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert bigString) (goto-char (point-min)) (buffer-string)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
        eval((let ((temp-buffer (generate-new-buffer " *temp*" t))) (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert bigString) (goto-char (point-min)) (buffer-string)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer))))) nil)
        elisp--eval-last-sexp(nil)
        eval-last-sexp(nil)
        funcall-interactively(eval-last-sexp nil)
        call-interactively(eval-last-sexp nil nil)
        command-execute(eval-last-sexp)
      
    4. Match String by Regex 以正则表达式来匹配 string

      (string-match "b+" "abbc")
      >1
      
    5. Get Substring by Regex 以正则表达式取出 string 中的一部分

      (setq myStr "swimming in pool")
      (string-match "\\([a-z]+?ing\\)" myStr)
      (match-string 1 myStr)
      
    6. Regex Replace in String 以正则表达式替换 string 里的某些内容

      (replace-regexp-in-string REGEXP REP STRING &optional FIXEDCASE LITERAL SUBEXP START)

      (replace-regexp-in-string "</*div>" "<p>" "<div>somthing</div>")
      >"<p>somthing<p>"
      
1 个赞

Writing Command 写命令

终于进入到实质性写命令的章节了,真是让人兴奋。

Writing Command Basics 写命令的基础

  1. Command Template 命令模板

    李杀说,命令字符宽度最好不要超过 70 个字符,以及不要把段落全部压缩到一行

    (defun my-comand ()
      (interactive)
      (let (var1 var2 ...)
    	(setq var1 ...)
    	(setq var2 ...)
    	))
    
  2. Elisp: Region, Active Region

    如何用 Elisp 操作 region,激活 region

    1. What is Mark, Region?

      mark 就是「标记」的意思,它有两个意思,标记一段选中的字符,或者标记一个即将跳转的位置。 在 Emacs 中,可以用 M-x set-mark-command 来开始对字符进行标记。 在 elisp,它相关的命令有两个 push-markset-mark

      region 是指从最新的标记位置,到当前指针所在的位置,这一连续标记选中的字符,叫 region。 每当我们设置了一个标记,就激活了 region。

      1. region-beginning

        返回 region 最开始的位置。

        (region-beginning)
        
      2. region-end

        返回 region 最末位置。

        (region-end)
        
      3. push-mark

        :question:Set mark at LOCATION (cursor position by default) and push old mark on mark ring. 没看明白。 (push-mark &optional LOCATION NOMASG ACTIVATE)

    2. What is Active Region

      因为当人进行了标记,那么指针会跟着一段选中的文本会一直高亮,这个问题烦人。因此有了一个新的概念叫:Active Region。

      1. Check Active Region Status or Set it 检查 Active Region 的状态,或者设置它的状态

        mark-active 当该命令返回的值为 t,则意味着一段 region 已经激活。可以把它的值设为 t/nil 来让该段 region 处于激活/非激活状态。

      2. Highlighting of Region: transient-mark-mode

        Emacs 有一个 minor mode 叫 transient-mark-mode 。当启动这一 mode,会将 Active Region 高亮显示出来。

      3. use-region-p

        use-region-p 会检查 3 样东西: 1、 transient-mark-mode 已经启动 2、 mark-active 是 ture 3、 通过 use=empty-active-region 检查 region 非空

        (defun my-is-region-active ()
         "print whether region is active"
         (interactive)
         (if (use-region-p)
        	 (message "region active")
        	 (message "region not active")))
        

        测试: 检测出当前 region not active。

      4. 设置一个 Active Region

        例子

        (defun my-select-line ()
          "Selet current line."
          (interactive)
          (let (p1 p2)
        	(setq p1 (line-beginning-position))
        	(setq p2 (line-end-position))
        	(goto-char p1)
        	(push-mark p2)
        	(setq mark-active t)))
        

        测试: 选中当前光标所在行。

      5. Get Active Region or Current {Word, Line, Text Block, File Path, Buffer, etc}

        (defun downcase-word-or-region ()
           "Downcase current word or region"
         (interactive)
         (let (pos1 pos2 bds)
           (if (use-region-p)
        	   (setq pos1 (region-beginning) pos2 (region-end))
        	 (progn
        	   (setq bds (bounds-of-thing-at-point 'symbol))
        	   (setq pos1 (car bds) pos2 (cdr bds))))
           (downcase-region pos1 pos2)
           ))
        

        测试: 所选中文本的大写字母成功变成小写。

1 个赞

Xahlee 写的非常好, 一直都在学, 这段时间, 现在也可以 顺利 浏览了, 前阵子 都还要 「酸酸乳」

2 个赞

确实是这样的,http://www.xahlee.info 现在可以直接访问了

  1. Elisp: Get Buffer String

    在前面章节已经进行了学习。略。

  2. Elips: Functions on Line elisp 下关于 Line 的操作

    1. Get Position of Beginning/End of Line 获取 Line 开头/末尾的位置

      测试: minibuffer 里返回了一个位置数值

      (line-beginning-position)
      ;;
      
      (line-end-position)
      
    2. Move Cursor to Beginning/End of Line 将光标移动到 Line 的开头/末尾的位置

      测试: 光标移动到了行首

      (beginning-of-line)
      ;李杀说该命令优于 (goto-char (line-beginning-position))
      

      测试: 返回 nil,因为光标当时处于行末,光标没有发生移动

      (end-of-line)
      ;;李杀说该命令优于 (goto-char (line-end-position))
      

      李杀提到,这两个命令是基于 C 来写的,所以运行效率比其它基于 elisp 来写的要快。

      They work by ewline char. That is, not soft-wrapped line. 这句话还需要理解,因为我不太知道什么叫 newline char

    3. Move Cursor to Previous/Next Line 将光标移动到上一行/下一行

      (forward-line -1) ;光标移动到上一行,光标出现在行首
      
      (forward-line 1) ;光标移动到下一行,光标出现在行首
      
    4. Get Current Line as String 提出当前行的所有内容到 String

      测试: 李杀直接编写了一个名为 myLine 的命令

      (setq myLine
      	(buffer-substring-no-properties
      	 (line-beginning-position)
      	 (line-end-position)
      	 ))
      
    5. Get All Lines in a Flie into a List

      李杀提供了 2 个命令模板

      (defun get-string-from-file (filePath)
       "Return file content as string"
        (with-temp-buffer
      	(insert-file-contents filePath)
      	(buffer-string)))
      
      (defun read-lines (filePath)
        "Return a list of lines of a file at filePath"
        (with-temp-buffer
      	 (instert-file-contents filePath)
      	 (split-string (buffer-string) "\n" t)))
1 个赞

不错,已经收录。继续

mark一下

Elisp:Cut Copy Paste to/from kill-ring kill-ring 与剪贴、复制、粘贴的操作

Copy Region to Kill Ring 复制 Region 到 Kill Ring

关于 Region 的内容,可以看之前 XahLee Elisp/Writing Command 写命令/Elisp: Region, Active Region 测试:返回 nil,因为我在当前 buffer,没有 mark

(copy-region-as-kill 3 22) ;将两个标记的位置里的内容复制到 kill ring

Kill Region to Kill Ring 将 region 里的内容 kill 之后,放进 kill ring

测试:返回 nil,原因还是因为我在当前 buffer,没有 mark

(kill-region 247 528)

String to Kill Ring 把 String 里的内容放进 kill ring

如果你已经有一段 string,可以用命令 kill-new 。 测试: kill-new 只能跟着命令的第一段 string 放进 kill ring

(kill-new "cute cat")
;; =C-y= 结果只出现了 cute

Append String to Kill Ring

Append 在英文是「附加」的意思,不太理解这个 Append String 的概念。 测试: C-y 得到结果 cute catcute cat。 也就是说,Append String 意思是复制多一个内容到 Kill Ring,但形式就好像两条内容「黏在一起」一样,不留空格

(kill-append "cute cat" nil)

Paste from Kill Ring 从 Kill Ring 里粘贴

(yank)

Mark a Region 标记一块 Region

第一步:标记处一块 Region,用 (push-mark POSITION) ,这样从光标的位置,到 POSTION 的位置都会标记为新的 Region 第二步:令 Region 处于 Active 状态,用 (setq mark-active t)

在此处键入或粘贴代码
1 个赞

一个建议:可以尝试通过写一些实用、有趣的小函数把这些知识点串联起来,这样学起来更生动一点。当然,这需要投入更多的时间和精力。

顶。

1 个赞