Skip to content

Latest commit

 

History

History
693 lines (526 loc) · 13.3 KB

emacs-and-emacs-lisp.org

File metadata and controls

693 lines (526 loc) · 13.3 KB

Journal on Emacs and Emacs Lisp

分别用 let/pcase-let/seq-let/-let 实现同样的需求

Entered on [2017-12-24 Sun 14:08]

(let* ((l (benchmark-run (sleep-for .2)))
       (total (nth 0 l))
       (gc (nth 1 l))
       (gc-total (nth 2 l)))
  (list total gc gc-total))
(pcase-let ((`(,total ,gc ,gc-total)
             (benchmark-run (sleep-for .2))))
  (list total gc gc-total))
(seq-let (total gc gc-total) (benchmark-run (sleep-for .2))
  (list total gc gc-total))
(-let (((total gc gc-total)
        (benchmark-run (sleep-for .2))))
  (list total gc gc-total))

一个简单的 Generic Functions 例子

Entered on [2017-12-25 Mon 11:43]

根据参数的类型自动选择不同的方法。

(cl-defgeneric foo-size (x)
  "Return the size of x.")

(cl-defmethod foo-size ((s string))
  "Return the size of the string S."
  (length s))

(cl-defmethod foo-size ((b buffer))
  "Return the size of the buffer B."
  (buffer-size b))
(foo-size "hello")
(with-temp-buffer
  (insert "hello")
  (foo-size (current-buffer)))

Use M-x ediff-regions-wordwise to diff sexps (list)

Entered on [2017-12-27 Wed 14:52]

(bar baz qux quux corge grault garply waldo fred plugh)
(bar baz qux quux corge thud grault garply waldo fred xyzzy plugh)

and ignore white spaces can be userful

(setq ediff-diff-options "-w")

Sexp-diff like mode for elisp source diffing - Emacs Stack Exchange

cl-block and cl-return

Entered on [2017-12-29 Fri 21:41]

cl-block/cl-return is like catch/throw

(catch 'found
  1
  2
  (throw 'found 100)
  3)
(cl-block nil
  1
  2
  (cl-return 100)
  3)

cl-defun/cl-dotimes/etc implies cl-block

(cl-defun foo ()
  1
  2
  (cl-return-from foo 100)
  3)
(foo)
(let (res)
  (cl-dotimes (i 10000)
    (if (< i 4)
        (push i res)
      (cl-return)))
  (nreverse res))

Copy the prompt of the MiniBuffer

Entered on [2017-12-30 Sat 14:13]

Like shell/eshell/comint, find a way to move point (for example, C-b (backward-char)) within the prompt then mark-copy as usual.

echo area - How to copy minibuffer contents? - Emacs Stack Exchange

Hash Table in Emacs Lisp

Entered on [2018-01-04 Thu 00:58]

Create Hash Table

Use make-hash-table

(make-hash-table :test #'equal)

Use the printed representation

#s(hash-table test equal data (:one 1 :two 2))

Access Hash Table

Add and lookup

(let ((hash (make-hash-table :test #'eq)))
  (puthash :one 1 hash)
  (puthash :two 2 hash)
  (list :one (gethash :one hash)
        :two (gethash :two hash)))

Remove

(let ((hash #s(hash-table test eq data (:one 1 :two 2))))
  (remhash :one hash)
  hash)

Clear (remove all)

(let ((hash #s(hash-table test eq data (:one 1 :two 2))))
  (clrhash hash)
  hash)

Lookup all

(let (alist)
  (maphash
   (lambda (key value)
     (push (cons key value) alist))
   #s(hash-table data (:one 1 :two 2)))
  alist)

Count

(hash-table-count #s(hash-table data (a 1 b 2)))

Keys & Values (hash-table-keys and hash-table-values is defined by subr-x.el)

(hash-table-keys #s(hash-table data (one 1 two 2 three 3)))
(hash-table-values #s(hash-table data (one 1 two 2 three 3)))

Performance (vs Alist)

(let ((alist nil)
      (hash (make-hash-table :test #'eq)))
  (mapatoms
   (lambda (symbol)
     (let ((key symbol)
           (val (length (symbol-name symbol))))
       (push (cons key val) alist)
       (puthash key val hash))))
  (let ((t1 (car (benchmark-run 1000 (assq 'length alist))))
        (t2 (car (benchmark-run 1000 (gethash 'length hash)))))
    (message "Alist takes %.5fs, Hash Table takes %.5fs" t1 t2)))

setf

Entered on [2018-01-07 Sun 01:54]

Modify element of list

(let ((l '(0 1 2 3 4 5 6)))
  (setf (nth 3 l) 100)
  l)
(let ((l '(0 1 2 3 4 5 6)))
  (setcar (nthcdr 3 l) 100)
  l)

Move point

(macroexpand '(setf (point) (point-min)))

cl-callf

(let ((l '(0 1 2 3 4 5 6)))
  (cl-callf + (nth 3 l) 100)
  l)
(let ((alist '((one . 1)
               (two . 2)
               (thr . 3)
               (fou . 4))))
  (setf (alist-get 'one alist) 100)
  (cl-callf + (alist-get 'two alist) -2)
  alist)

Define new setf form

(gv-define-setter length (val x) `(substring ,x 0 ,val))
(setf (length "hello") 2)

Recursive Editing

Entered on [2018-01-07 Sun 15:55]

info:elisp#Recursive Editing

在 Emacs command loop 中运行另外一个 command loop,比如 ediff-regions-wordwise 中间需要让用户选中两个区域,就可以用 recursive-edit ,Debug 和 Isearch 等地方也有用到。

举个例子,我需要让用户从 Dired 中选中一个文件来处理:

(defun foo ()
  (interactive)
  (dired ".")
  (message
   (substitute-command-keys
    "When done, type \\[exit-recursive-edit]. Use \\[abort-recursive-edit] to quit"))
  (recursive-edit)
  (message "You have picked '%s'" (dired-get-filename)))

Protect form from (recursive-edit)

Entered on [2018-01-09 Tue 16:56]

(progn
  (unwind-protect
      (recursive-edit)
    (message-box "Always run"))
  (message-box "Only run with 'C-M-c' ('exit-recursive-edit')")
  nil)
(progn
  (condition-case nil
      (recursive-edit)
    (quit (message-box "Only run with 'C-]' ('abort-recursive-edit')")))
  (message-box "Always run")
  nil)
(progn
  (condition-case err
      (recursive-edit)
    (quit
     (message-box "Only run with 'C-]' ('abort-recursive-edit')")
     (signal (car err) (cdr err))))
  (message-box "Only run with 'C-M-c' ('exit-recursive-edit')")
  nil)

一次性去掉所有的 Advice

Entered on [2018-01-09 Tue 21:29]

添加:

(advice-add #'emacs-version :filter-return #'upcase)
(advice-add #'emacs-version :filter-return #'nreverse)
(advice-add #'emacs-version :filter-return (lambda (x) (concat "=> " x)))

检查:

(defun chunyang-function-advices (symbol)
  (let (advices)
    (advice-mapc
     (lambda (f _)
       (push f advices))
     symbol)
    (nreverse advices)))

(chunyang-function-advices 'emacs-version)

删除:

(advice-mapc
 (lambda (f _)
   (advice-remove 'emacs-version f))
 'emacs-version)

(chunyang-function-advices 'emacs-version)

Structure

Entered on [2018-01-11 Thu 17:42]

;; * Define structure
(cl-defstruct person
  name
  age
  sex)
     => person

;; * Make object of structure
(setq P (make-person :name (rot13-string user-full-name)
                     :age 99
                     :sex "TWFsZQ=="))
     => #s(person "Kh Puhalnat" 99 "TWFsZQ==")

;; * Access slots

(person-name P)
     => "Kh Puhalnat"

(person-age P)
     => 99

(person-sex P)
     => "TWFsZQ=="

;; * Change slots

(cl-callf + (person-age P) 100)
     => 142

(person-age P)
     => 142

(setf (person-name P) (md5 user-full-name))
     => "670a835cea7294c07ccb015c6ec651dd"

(person-name P)
     => "670a835cea7294c07ccb015c6ec651dd"

;; * Type Check

(person-p P)
     => t

;; * Create object using printd representation
(let ((not-me #s(person "某某" 22 "Unknown")))
  (person-p not-me))
     => t

Swap multiple variables at a time

Entered on [2018-01-11 Thu 18:47]

(let ((x1 +1) (x2 +2) (x3 +3)
      (y1 -1) (y2 -2) (y3 -3))
  (cl-psetq x1 y1
            y1 x1
            x2 y2
            y2 x2
            x3 y3
            y3 x3)
  (list (list x1 x2 x3) (list y1 y2 y3)))
(let ((a 1) (b 2) (c 3)
      (x -1) (y -2) (z -3))
  (cl-rotatef a x)
  (cl-rotatef b y)
  (cl-rotatef c z)
  (list (list a b c) (list x y z)))
(defmacro chunyang-swap-varsf (vars-1 vars-2)
  (cons 'cl-psetf
        (cl-loop for x in vars-1
                 for y in vars-2
                 append (list x y y x))))

(let ((a 1) (b 2) (c 3)
      (x -1) (y -2) (z -3))
  (chunyang-swap-varsf (a b c) (x y z))
  (list (list a b c) (list x y z)))

thread-first and thread-last :25.1:

Entered on [2018-02-18 Sun 15:44]

For example, get parent directory

(file-name-nondirectory
 (directory-file-name
  (file-name-directory
   "~/.emacs.d/init.el")))
(thread-first "~/.emacs.d/init.el"
  file-name-directory
  directory-file-name
  file-name-nondirectory)
(thread-last "~/.emacs.d/init.el"
  file-name-directory
  directory-file-name
  file-name-nondirectory)

thunk-let and thunk-let :27.1:

Entered on [2018-02-21 Wed 02:20]

(with-temp-buffer
  (thunk-let ((a (insert "1"))
              (b (insert "2")))
    (insert "3")
    b
    a)
  (buffer-string))

read-answer :27.1:

Entered on [2018-02-23 Fri 18:27]

(read-answer
 "Are you sure? "
 '(("yes"  ?y "perform the action")
   ("no"   ?n "skip to the next")
   ("all"  ?! "accept all remaining without more questions")
   ("help" ?h "show help")
   ("quit" ?q "exit")))

make-symbol vs intern

Entered on [2018-03-21 Wed 15:41]

(assq (make-symbol "one") '((one . 1)))
     => nil

(assq (intern "one") '((one . 1)))
     => (one . 1)

(let ((alist (list (cons (make-symbol "one") 1))))
  (list (assq 'one alist)
        (assq (make-symbol "one") alist)
        (assq (intern "one") alist)))
     => (nil nil nil)

(eq (intern "foo") (intern "foo"))
     => t

(eq (make-symbol "foo") (make-symbol "foo"))
     => nil

获得一个 Emacs Package 的主页

Entered on [2018-03-22 Thu 15:00]

(oref (epkg "magit") homepage)

info:epkg#Using Epkg Objects

Reuse interactive form

Entered on [2018-03-23 Fri 13:04]

获得

(interactive-form 'find-file)

重用

(eval (cadr (interactive-form 'find-file)))

在某些特殊情况下,指的是没经过 byte-compile 和用 list 构造,甚至能选择重用哪一部分

(defun foo (string number)
  (interactive (list (read-string "String: ")
                     (read-number "Number: "))))
(interactive-form 'foo)
(pcase-let ((`(interactive (list ,string ,number))
             (interactive-form 'foo)))
  (list string number))