Emacs Essentials

emacs tech

Table of Contents


It is a steep learning curve to master Emacs lisp, there are mainly two issues in it from my experience

  1. the lisp syntax and functional programming
  2. the fragmented methods and libraries

For the 1st issue, it is easy to master the syntax after writing several programs and getting used to them, but for the 2nd one, one needs to take notes or remember something.

In this blog, I focus on the 2nd point and keep updating the notes of some methods and libraries that I think are essential for writing Emacs lisp packages.

builtin methods

buffer

current-buffer: get the current buffer

(current-buffer)
#<buffer emacs-essentials.org>

get-buffer: get a buffer by name

(get-buffer "*scratch*")
#<buffer *scratch*>

get-buffer-create: create the buffer if not exist

(get-buffer-create "yaya")
#<buffer yaya>

changing the current buffer

(progn
  (set-buffer (get-buffer "*scratch*"))
  (current-buffer))

Goto a buffer

(with-current-buffer "*BUF*"
  ;; do something like progn
  )

Changing the current buffer safely

It will return to the original buffer after the operation finished.

(progn
  (save-current-buffer
    (set-buffer "*scratch*")
    (message "Current buffer: %s" (current-buffer)))
  (current-buffer))
#<buffer 20210801162858-emacs_lisp.org>

Working with file buffers

To get the full file path for the file that the buffer represents

(buffer-file-name)
/Users/yanchunwei/project/myblog2022/content-org/emacs-essentials.org

To find a buffer that represents a particular file

(get-file-buffer "/Users/yanchunwei/project/myblog2022/content-org/emacs-essentials.org")
#<buffer emacs-essentials.org>

Loading a file into a buffer without display it

(find-file-noselect "xx.org")

Get all buffer names

(mapcar #'buffer-name (buffer-list))

save-execution: Operate on other buffers without altering the current context

Buffer is a core data structure in elisp, so it is normial to switch to other buffers, do some operations and return back. save-execution helps to restore the previous context when switching to other buffers.

(save-excursion
  (progn

    ;; do anything on other buffers

    )
  )

;; Return to the previous context: buffer and point

point

The “point” is the location of the cursor in the buffer.

(point)
7508
(point-max)
8010
(point-min)
1

Moving the point

(goto-char 1)
(goto-char (point-max))

;; goto the begining of the buffer
(beginning-of-buffer)

;; goto the end of the buffer
(end-of-buffer)

(forward-char)
(forward-char 5)

(forward-word)
(backward-word)

Preserving the point

(save-excursion
  (goto-char (point-max))
  (point)
  )
8471

Examining buffer text

To look at text in the buffer.

(char-after)
(char-after (point))
(char-after (point-min))
58

The Thing

The thing-at-point function is very useful for grabbing the text at the point if it matches a particular type of “thing”.

(thing-at-point 'word)
(thing-at-point 'sentence)
(thing-at-point 'sentence)
#+END_SRC
(thing-at-point 'sentence t)
(thing-at-point 'sentence t)
#+END_SRC

Serching for text

(search-forward "thing")

Inserting text

(insert "000")
(insert "\n" "This is" ?\s ?\n "Sparta!")

Deleting text

(with-current-buffer ".gitignore"
  (delete-region (point) (point-max)))

Saving a buffer

To save the contents of a buffer back to the file it is associated with

(save-buffer)

org-model programming

Tags in org-mode are as below:

* heading :tag0:tag1:
  • Get tags

    To get tags on the current entry:

    (org-get-tags)
    
  • Set tags

    To set tags on the current entry:

    (org-set-tags '("hello"))  :hello:
    

Properties in org-mode is as follows, where a property called “prop” has a “value”. It is handy to store some meta data using properties.

* headline
:PROPERTIES:
:prop: value
:END:
  • Get properties

    Get properties of the current entry:

    (org-entry-properties)
    
  • Set property

    To set a property on the current entry:

    (org-set-property "name" "tom")
    

file and path

Get the path of the current file

The buffer-file-name is a buffer builtin variable holding the file name of the current buffer.

(file-truename buffer-file-name)
/tmp/emacs-essentials.org

Get path without suffix

(file-name-sans-extension "/tmp/a.org")
/tmp/a

Write to file

Overwrite the content:

(with-temp-file "/tmp/1.org"
  (insert "hello world")
  (message "file content: %s" (buffer-string))
  )
file content: hello world

execute shell command

(shell-command "echo hello")
0

condition-case: try-catch in elisp

Like the try-catch in Python, where a try-catch can launch some unsafe function and catch the error.

(condition-case err
    (progn
      (message "No error"))
  (error "some error")
  )
No error

Modern libraries

ht.el for hashtables

Reference ht.el for more details.

creating a hash table

Create an empty hash table

(let* ((the-dic (ht-create)))
  the-dic
  )
#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ())

Create a hash table with initial records

(let* ((the-dic (ht
                 ("name" "Tom")
                 ("sex" 'male))))
  the-dic
  )
#s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("name" "Tom" "sex" male))

accessing the hash table

(let* ((the-dic (ht ("name" "Tom") ("sex" 'male))))
  ;; get a record
  ;; returns "Tom"
  (ht-get the-dic "name")
  )
Tom

Iterating over the hash table

Readonly mapping:

(let* ((the-dic (ht ("name" "Tom") ("sex" 'male) ("age" 18))))
  (ht-map (lambda (key value) (message "%S: %S" key value)) the-dic)
  )
“age”: 18“sex”: male“name”: “Tom”

Mutable mapping:

(let* ((the-dic (ht ("name" "Tom") ("sex" 'male) ("age" 18))))
  (ht-map (lambda (key value)
            ;; modify the value if is string
            (setf value (if (stringp value)
                            (concat "modified " value)
                          value))) the-dic))
18malemodified Tom

Debug and development in Elisp

One handly tool is toggle-debug-on-error, it will print the error stack.