branch: elpa/typst-ts-mode
commit aad1cde1538f9996f77826fa98827ba0013ca388
Author: Huan Nguyen <nguyenthieuh...@gmail.com>
Commit: Huan Nguyen <nguyenthieuh...@gmail.com>

    refactor: Use outline library for headings.
---
 typst-ts-mode.el | 212 +++++++++++++++----------------------------------------
 1 file changed, 58 insertions(+), 154 deletions(-)

diff --git a/typst-ts-mode.el b/typst-ts-mode.el
index fd8fe133fc..f8d3afa04a 100644
--- a/typst-ts-mode.el
+++ b/typst-ts-mode.el
@@ -31,7 +31,7 @@
 
 (require 'treesit)
 (require 'compile)
-(require 'subr-x)
+(require 'outline)
 
 (defgroup typst-ts nil
   "Tree Sitter enabled Typst Writing."
@@ -568,6 +568,26 @@ buffer before compilation."
       (remove-hook 'compilation-finish-functions
                    (typst-ts-mode-compile--compilation-finish-function 
cur-buffer)))))
 
+;; outline-minor-mode 
================================================================================
+
+(defconst typst-ts-mode-outline-regexp "=+ "
+  "Regexp identifying Typst header.")
+
+(defun typst-ts-mode-outline-level ()
+  "Return the level of the heading at point."
+  (save-excursion
+    (end-of-line)
+    (if (re-search-backward "^=+ " nil t)
+       (1- (- (match-end 0) (match-beginning 0)))
+      0)))
+
+(defconst typst-ts-mode-outline-heading-alist
+  '(("= " . 1)
+    ("== " . 2)
+    ("=== " . 3)
+    ("==== " . 4)
+    ("===== " . 5)))
+
 (defun typst-ts-mode-heading--at-point-p ()
   "Whether the current line is a heading.
 Return the heading node when yes otherwise nil."
@@ -580,136 +600,29 @@ Return the heading node when yes otherwise nil."
         node
       nil)))
 
-(defun typst-ts-mode-heading--increase/decrease (direction node)
-  "Increase or decrease the heading level.
-DIRECTION right means increase the level while DIRECTION right means decrease.
-NODE is the heading node.
-This does not handle #heading function."
-  (let ((heading-string "")
-             (heading-level 0))
-    (setq heading-level
-               (length (setq heading-string (treesit-node-text node))))
-    (when (and (= heading-level 1) (eq direction 'left))
-      (user-error "Cannot decrease level 1 heading"))
-    (replace-region-contents
-     (treesit-node-start node)
-     (treesit-node-end node)
-     (lambda ()
-       (pcase direction
-              ('right (concat heading-string "="))
-              ('left (substring-no-properties heading-string 1 heading-level))
-              (_ (error "%s is not one of: `left' `right'" direction)))))))
-
-(defun typst-ts-mode-heading--find-same-or-higher (node traverse-fn)
-  "Return the first heading that is the same level or higher than NODE.
-`car' will be the found heading node.
-`cdr' will say if it is the same level or not.
-TRAVERSE-FN dictates in which direction to search.
-`treesit-node-next-sibling' for down.
-`treesit-node-prev-sibling' for up."
-  (let ((iterate (funcall traverse-fn node))
-             (level (typst-ts-mode-heading--level node))
-             (iterate-level nil))
-    (while (and iterate
-                           (not (and (string= (treesit-node-type iterate) 
"heading")
-                                           (or (= (setq
-                                                         iterate-level ;; hack 
to make it not eval twice
-                                                         
(typst-ts-mode-heading--level iterate))
-                                                        level)
-                                               ;; parent heading or NODE was a 
leaf
-                                               (< iterate-level level)))))
-      (setq iterate (funcall traverse-fn iterate)))
-    ;; there are no level 0 heading
-    (cons iterate (= (if iterate-level iterate-level 0) level))))
-
-(defun typst-ts-mode-heading--level (node)
-  "Get the level of the heading NODE.
-This functions does not check if NODE is actually a heading."
-  (length (treesit-node-text (treesit-node-child node 0))))
-
-(defun typst-ts-mode-heading--find-same-level (node traverse-fn)
-  "Return the first node with the same level as NODE.
-It will report a user-error when it could not find a node
-or it was blocked by its parent heading.
-See `typst-ts-mode-heading--find-same-or-higher' for TRAVERSE-FN."
-  (let* ((other-heading/level
-               (typst-ts-mode-heading--find-same-or-higher node traverse-fn)))
-    (if (cdr other-heading/level)
-             (car other-heading/level)
-      (user-error "Could not find another heading"))))
-
+;;;###autoload
 (defun typst-ts-mode-heading-up ()
   "Switch the current heading with the heading above."
   (interactive)
   (typst-ts-mode-meta--dwim 'up))
 
+;;;###autoload
 (defun typst-ts-mode-heading-down ()
   "Switch the current heading with the heading below."
   (interactive)
   (typst-ts-mode-meta--dwim 'down))
 
-(defun typst-ts-mode-heading--down (current-heading)
-  "Switch two heading of same level.
-CURRENT-HEADING and its content with above heading and its content."
-  (let* ((current-heading-start (treesit-node-start current-heading))
-         (other-heading
-          (typst-ts-mode-heading--find-same-level
-           current-heading
-           #'treesit-node-next-sibling))
-         (other-heading-start (treesit-node-start other-heading))
-         (other-heading-end (car (typst-ts-mode-heading--find-same-or-higher
-                                  other-heading
-                                  #'treesit-node-next-sibling)))
-         (current-heading-end (1- other-heading-start))
-         (current-heading-content (buffer-substring current-heading-start
-                                                    current-heading-end))
-         (other-heading-content nil))
-    (setq other-heading-end (if other-heading-end
-                                (1- (treesit-node-start other-heading-end))
-                              (point-max)))
-    (setq other-heading-content
-          (buffer-substring other-heading-start
-                            other-heading-end))
-    (save-excursion
-      (delete-region current-heading-start other-heading-end)
-      (goto-char current-heading-start)
-      (insert other-heading-content)
-      (unless (= ?\n (aref other-heading-content
-                           (1- (length other-heading-content))))
-        (newline))
-      (insert current-heading-content))))
-
-(defun typst-ts-mode-heading--up (current-heading)
-  "Switch two heading of same level.
-CURRENT-HEADING and its content with above heading and its content."
-  (let* ((current-heading-start (treesit-node-start current-heading))
-         (other-heading
-               (typst-ts-mode-heading--find-same-level
-                current-heading
-                #'treesit-node-prev-sibling))
-              (other-heading-start (treesit-node-start other-heading))
-              (other-heading-end (1- current-heading-start))
-              (current-heading-end (car 
(typst-ts-mode-heading--find-same-or-higher
-                                                           current-heading
-                                                           
#'treesit-node-next-sibling)))
-              (current-heading-content nil)
-              (other-heading-content
-               (buffer-substring other-heading-start
-                                             other-heading-end)))
-    (setq current-heading-end (if current-heading-end
-                                                         (1- 
(treesit-node-start current-heading-end))
-                                                       (point-max)))
-    (setq current-heading-content
-               (buffer-substring current-heading-start
-                                             current-heading-end))
-    (save-excursion
-      (delete-region other-heading-start current-heading-end)
-      (goto-char other-heading-start)
-      (insert current-heading-content)
-      (unless (= ?\n (aref current-heading-content
-                                            (1- (length 
current-heading-content))))
-             (newline))
-      (insert other-heading-content))))
+;;;###autoload
+(defun typst-ts-mode-heading-increase ()
+  "Increase the heading level."
+  (interactive)
+  (typst-ts-mode-meta--dwim 'right))
+
+;;;###autoload
+(defun typst-ts-mode-heading-decrease ()
+  "Decrease heading level."
+  (interactive)
+  (typst-ts-mode-meta--dwim 'left))
 
 (defun typst-ts-mode-meta--dwim (direction)
   "Do something depending on the context with meta key + DIRECTION.
@@ -720,42 +633,27 @@ CURRENT-HEADING and its content with above heading and 
its content."
 When there is no relevant action to do it will execute the relevant function in
 the `GLOBAL-MAP' (example: `right-word')."
   (let ((heading (typst-ts-mode-heading--at-point-p))
-             ;; car function, cdr string of function for 
`substitute-command-keys'
-             (call-me/string
-              (pcase direction
-                ('left
-                 (cons (lambda (node) (typst-ts-mode-heading--increase/decrease
-                                                         direction 
(treesit-node-child node 0)))
-                             "\\[typst-ts-mode-heading-decrease]"))
-                ('right
-                 (cons (lambda (node) (typst-ts-mode-heading--increase/decrease
-                                                         direction 
(treesit-node-child node 0)))
-                             "\\[typst-ts-mode-heading-decrease]"))
-                ('up
-                 (cons (lambda (node) (typst-ts-mode-heading--up node))
-                             "\\[typst-ts-mode-heading-up]"))
-                ('down
-                 (cons (lambda (node) (typst-ts-mode-heading--down node))
-                             "\\[typst-ts-mode-heading-down]"))
-                (_ (error "%s is not one of: `right' `left'" direction)))))
+       ;; car function, cdr string of function for `substitute-command-keys'
+       (call-me/string
+        (pcase direction
+          ('left
+           (cons #'outline-promote
+                 "\\[typst-ts-mode-heading-decrease]"))
+          ('right
+           (cons #'outline-demote
+                 "\\[typst-ts-mode-heading-decrease]"))
+          ('up
+           (cons #'outline-move-subtree-up
+                 "\\[typst-ts-mode-heading-up]"))
+          ('down
+           (cons #'outline-move-subtree-down
+                 "\\[typst-ts-mode-heading-down]"))
+          (_ (error "%s is not one of: `right' `left'" direction)))))
     (if heading
-             (funcall (car call-me/string) heading)
+       (call-interactively (car call-me/string))
       (call-interactively
-       (keymap-lookup global-map (substitute-command-keys (cdr 
call-me/string)))))))
-
-;;;###autoload
-(defun typst-ts-mode-heading-increase ()
-  "Increase the heading level."
-  (interactive)
-  (typst-ts-mode-meta--dwim 'right))
+        (keymap-lookup global-map (substitute-command-keys (cdr 
call-me/string)))))))
 
-;;;###autoload
-(defun typst-ts-mode-heading-decrease ()
-  "Decrease heading level."
-  (interactive)
-  (typst-ts-mode-meta--dwim 'left))
-
-;;;###autoload
 (defun typst-ts-mode-compile ()
   "Compile current typst file."
   (interactive)
@@ -921,6 +819,8 @@ PROC: process; OUTPUT: new output from PROC."
     (define-key map (kbd "C-c C-c p") #'typst-ts-mode-preview)
     (define-key map (kbd "M-<left>") #'typst-ts-mode-heading-decrease)
     (define-key map (kbd "M-<right>") #'typst-ts-mode-heading-increase)
+    (define-key map (kbd "M-<down>") #'typst-ts-mode-heading-down)
+    (define-key map (kbd "M-<up>") #'typst-ts-mode-heading-up)
     map))
 
 ;;;###autoload
@@ -971,6 +871,10 @@ PROC: process; OUTPUT: new output from PROC."
                       (file-name-nondirectory buffer-file-name)
                       typst-ts-mode-compile-options))
 
+  ;; Outline
+  (setq-local outline-regexp typst-ts-mode-outline-regexp)
+  (setq-local outline-level #'typst-ts-mode-outline-level)
+  (setq-local outline-heading-alist typst-ts-mode-outline-heading-alist)
   (treesit-major-mode-setup))
 
 ;;;###autoload

Reply via email to