Elisp Text Processing vs Structured Syntactic Manipulation (2021)

By Xah Lee. Date: .

Major rewrite of a command, using completely different approaches.

Left: text processing. Right: turn the text into nested list. In this case, the nested list is shorter, more readible, and actually more features.

elisp change algorithm 2021-09-04
elisp change algorithm 2021-09-04

Here's the comparison code. i think the text processing version is faster and uses less memory. but the split string version is clearer, shorter.

;; -*- coding: utf-8; lexical-binding: t; -*-

(defun my-lines-to-list1 ()
  "Make the current lines or text blocks into a HTML list.
If there is no selection, make each line into a list item.
If there is selection, each text block becomes a list item. (text block is separated by blank lines.)
If `universal-argument' is called first, use ordered list ol instead of ul.
2021-09-04 2025-03-26"
  (interactive)
  (let* ((xbounds
          (if (region-active-p)
              (cons (region-beginning) (region-end))
            (cons
             (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point)))
             (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point))))))
         (xp1 (car xbounds))
         (xp2 (cdr xbounds)))
    (save-restriction
      (narrow-to-region xp1 xp2)
      (progn
        (goto-char (point-min))
        (insert "<li>")
        (if mark-active
            (while (re-search-forward " *\n\n+ *" nil 1)
              (replace-match "</li>\n\n<li>" t t))
          (while (re-search-forward " *\n *" nil 1)
            (replace-match "</li>\n<li>" t t)))
        (insert "</li>\n"))
      (if current-prefix-arg
          (progn
            (goto-char (point-min)) (insert "<ol>\n")
            (goto-char (point-max)) (insert "</ol>"))
        (progn
          (goto-char (point-min)) (insert "<ul>\n")
          (goto-char (point-max)) (insert "</ul>")))
      (goto-char (point-min))
      (while (search-forward "<li></li>" nil 1)
        (replace-match "" t t))
      (goto-char (point-min))
      (while (search-forward "<li>\n" nil 1)
        (replace-match "<li>" t t))
      (goto-char (point-min))
      (while (re-search-forward "\n\n+" nil 1)
        (replace-match "\n\n" t t))
      (insert "\n\n"))))
;; -*- coding: utf-8; lexical-binding: t; -*-

(defun my-lines-to-list2 ()
  "Make the current block or selection into a HTML list.
If there is no selection, make each line into a list item.
If there is selection, each text block becomes a list item. (text block is separated by blank lines.)
If `universal-argument' is called first, use ordered list ol instead of ul.
2021-09-04 2025-03-26"
  (interactive)
  (let* ((xsep (if mark-active "\n\n+" "\n"))
         (xbounds
          (if (region-active-p)
              (cons (region-beginning) (region-end))
            (cons
             (save-excursion (if (re-search-backward "\n[ \t]*\n" nil 1) (match-end 0) (point)))
             (save-excursion (if (re-search-forward "\n[ \t]*\n" nil 1) (match-beginning 0) (point))))))
         (xp1 (car xbounds))
         (xp2 (cdr xbounds))
         (xinput (buffer-substring-no-properties xp1 xp2))
         (xitems (split-string xinput xsep t " +"))
         (xsList (mapcar (lambda (x) (format "<li>%s</li>" x)) xitems))
         (xlistStr (mapconcat 'identity xsList "\n")))
    (save-restriction
      (narrow-to-region xp1 xp2)
      (delete-region (point-min) (point-max))
      (insert (if current-prefix-arg
                  (concat "<ol>\n" xlistStr "\n</ol>")
                (concat "<ul>\n" xlistStr "\n</ul>"))))))
elisp xah-html-lines-to-def-list 2021-09-04
elisp xah-html-lines-to-def-list 2021-09-04