branch: elpa/typst-ts-mode commit 96d82bc4ba38a6d2d1fcabca5e413c58cb278a80 Author: Meow King <mr.meowk...@anche.no> Commit: Meow King <mr.meowk...@anche.no>
refactor: separate editing commands into another file --- README.md | 7 + typst-ts-utils.el => typst-ts-core.el | 21 ++- typst-ts-editing.el | 294 ++++++++++++++++++++++++++++++++++ typst-ts-embedding-lang-settings.el | 10 +- typst-ts-mode.el | 287 +-------------------------------- 5 files changed, 327 insertions(+), 292 deletions(-) diff --git a/README.md b/README.md index e654c8a718..c7beaefeb9 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,13 @@ and `defcustom' forms reset their default values." (eval-defun nil)))) ``` +### How to use different version of Emacs to test + +``` shell +# change `emacs` to `emacs29`, `emacs30` or the path of your emacs executable +emacs -Q -L . --eval "(require 'typst-ts-mode)" +``` + #### How to do lint I recommend you to use [makem.sh](https://github.com/alphapapa/makem.sh). However, you can choose anything you like. To use it, first `cd` into the project root directory, then: diff --git a/typst-ts-utils.el b/typst-ts-core.el similarity index 86% rename from typst-ts-utils.el rename to typst-ts-core.el index 8113b71138..bdc6195173 100644 --- a/typst-ts-utils.el +++ b/typst-ts-core.el @@ -1,4 +1,4 @@ -;;; typst-ts-utils.el --- utility functions for typst-ts-mode -*- lexical-binding: t; -*- +;;; typst-ts-core.el --- core functions for typst-ts-mode -*- lexical-binding: t; -*- ;; Copyright (C) 2023 The typst-ts-mode Project Contributors ;; This file is NOT part of Emacs. @@ -27,7 +27,14 @@ ;; code from Emacs binary (declare-function treesit-parser-list "treesit" t t) -(defun typst-ts-utils-parser-list (&optional buffer language) +(defun typst-ts-core-get-node-bol (node) + "Get the NODE's indentation offset (at node beginning)." + (save-excursion + (goto-char (treesit-node-start node)) + (back-to-indentation) + (point))) + +(defun typst-ts-core-parser-list (&optional buffer language) "An comptibility function for Emacs 29's `treesit-parser-list' function. BUFFER defaults to the current buffer. If that buffer is an indirect buffer, its base buffer is used instead. That is, indirect buffers @@ -43,7 +50,7 @@ If LANGUAGE is non-nil, only return parsers for that language." parsers)))) ;; code is from treesit.el inside Emacs Source -(defun typst-ts-utils-local-parsers-at (&optional pos language with-host) +(defun typst-ts-core-local-parsers-at (&optional pos language with-host) "Return all the local parsers at POS. It's a copy of Emacs 30's `treesit-local-parsers-at' function. POS LANGUAGE WITH-HOST." @@ -60,7 +67,7 @@ POS LANGUAGE WITH-HOST." (nreverse res)))) ;; code is from treesit.el inside Emacs Source -(defun typst-ts-utils-local-parsers-on (&optional beg end language with-host) +(defun typst-ts-core-local-parsers-on (&optional beg end language with-host) "Return all the local parsers between BEG END. It's a copy of Emacs 30's `treesit-local-parsers-on' function. BEG END LANGUAGE WITH-HOST." @@ -76,7 +83,7 @@ BEG END LANGUAGE WITH-HOST." (push (if with-host (cons parser host-parser) parser) res)))) (nreverse res)))) -(defun typst-ts-utils-node-get (node instructions) +(defun typst-ts-core-node-get (node instructions) "Get things from NODE by INSTRUCTIONS. It's a copy of Emacs 30's `treesit-node-get' function." (declare (indent 1)) @@ -98,6 +105,6 @@ It's a copy of Emacs 30's `treesit-node-get' function." (treesit-node-prev-sibling node named))))))) node)) -(provide 'typst-ts-utils) +(provide 'typst-ts-core) -;;; typst-ts-utils.el ends here +;;; typst-ts-core.el ends here diff --git a/typst-ts-editing.el b/typst-ts-editing.el new file mode 100644 index 0000000000..7abcad9124 --- /dev/null +++ b/typst-ts-editing.el @@ -0,0 +1,294 @@ +;;; typst-ts-editing.el --- Helper functions for editing Typst documents -*- lexical-binding: t; -*- +;; Copyright (C) 2023 The typst-ts-mode Project Contributors + +;; This file is NOT part of Emacs. +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +(require 'outline) +(require 'typst-ts-core) + +;;;###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)) + +;;;###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-heading--at-point-p () + "Whether the current line is a heading. +Return the heading node when yes otherwise nil." + (let ((node (treesit-node-parent + (treesit-node-at + (save-excursion + (beginning-of-line-text) + (point)))))) + (if (string= (treesit-node-type node) "heading") + node + nil))) + +(defun typst-ts-mode-meta--dwim (direction) + "Do something depending on the context with meta key + DIRECTION. +`left': `typst-ts-mode-heading-decrease', +`right': `typst-ts-mode-heading-increase', +`up': `typst-ts-mode-heading-up', +`down': `typst-ts-mode-heading-down'. +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 #'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 + (call-interactively (car call-me/string)) + (call-interactively + (keymap-lookup global-map (substitute-command-keys (cdr call-me/string))))))) + +(defun typst-ts-mode--item-on-line-p () + "Does the current line have an item node? +Return the node when yes otherwise +return the node that is one character left from the end of line." + (treesit-node-parent + (treesit-node-at + (save-excursion + ;; starting from the beginning because line could be 1. wow. + (beginning-of-line) + (condition-case nil + (progn + (search-forward-regexp (rx (or "+" "-" ".")) + (pos-eol) + nil + nil) + (left-char)) + (search-failed + ;; need to go to the end of line and then one left because end of line is the next node + (goto-char (1- (pos-eol))))) + (point))))) + +(defun typst-ts-mode-meta-return (&optional arg) + "Depending on context, insert a heading or insert an item. +The new heading is created after the ending of current heading. +Using ARG argument will ignore the context and it will insert a heading instead." + (interactive "P") + (let ((node (typst-ts-mode--item-on-line-p))) + (cond + (arg (typst-ts-mode-insert--heading nil)) + ((string= (treesit-node-type node) "item") + (typst-ts-mode-insert--item node)) + (t + (typst-ts-mode-insert--heading node))))) + +(defun typst-ts-mode-return (&optional arg) + "Handle RET depends on condition. +When prefix ARG is non-nil, call global return function." + (interactive "P") + (let (execute-result node) + (setq + execute-result + (catch 'execute-result + (when-let* ((cur-pos (point)) + (cur-node (treesit-node-at cur-pos)) + (cur-node-type (treesit-node-type cur-node)) + (parent-node (treesit-node-parent cur-node)) ; could be nil + (parent-node-type (treesit-node-type parent-node))) + (cond + (arg (throw 'execute-result 'default)) + ;; on item node end + ((and (eolp) + (setq node (typst-ts-mode--item-on-line-p)) + (string= (treesit-node-type node) "item") + (not (string= (typst-ts-core-node-get node '((child -1 nil) (type))) "linebreak"))) + (if (> (treesit-node-child-count node) 1) + (typst-ts-mode-insert--item node) + ;; no text means delete the item on current line + (beginning-of-line) + (kill-line) + (indent-according-to-mode)) + (throw 'execute-result 'success)) + )))) + ;; execute default action if not successful + (unless (eq execute-result 'success) + (let ((global-ret-function + (global-key-binding (kbd "RET")))) + (if (and current-prefix-arg + (yes-or-no-p + (format + "Execute function `%s' with the given prefix argument?" + global-ret-function))) + (call-interactively global-ret-function) + (let ((current-prefix-arg nil)) + (call-interactively global-ret-function))))))) + +(defun typst-ts-mode-insert--item (node) + "Insert an item after NODE. +NODE must be an item node! +This function respects indentation." + (let* (;; +, -, or <num>. + (item-type (treesit-node-text + (treesit-node-child node 0))) + (item-number (string-to-number item-type)) + (item-end (treesit-node-end node)) + (node-bol-column (typst-ts-mode-column-at-pos + (typst-ts-core-get-node-bol node)))) + (goto-char item-end) + (newline) + (indent-line-to node-bol-column) + (insert (if (= item-number 0) + item-type + (concat (number-to-string (1+ item-number)) ".")) + " "))) + +(defun typst-ts-mode-insert--heading (node) + "Insert a heading after the section that NODE is part of. +When there is no section it will insert a heading below point." + (let* ((section + (treesit-parent-until + node + (lambda (node) + (string= (treesit-node-type node) "section")) + t)) + ;; first child is heading + (heading (treesit-node-child section 0)) + (heading-level (treesit-node-type (treesit-node-child heading 0)))) + (if section + (goto-char (treesit-node-end section)) + ;; no headings so far + (setq heading-level "=") + (forward-line 1)) + ;; something can be in the next line/section, the heading needs be on its own line + ;; this has to be done after `goto-char' because it will invalidate the node + (newline) + (forward-line -1) + ;; insert the heading and indent + (insert heading-level " ") + (indent-according-to-mode))) + +(defun typst-ts-mode-column-at-pos (point) + "Get the column at position POINT." + (save-excursion + (goto-char point) + (current-column))) + +;;;###autoload +(defun typst-ts-mode-cycle (&optional _arg) + "Cycle." + (interactive "P") + (let (execute-result) + (setq + execute-result + ;; plz manually throw `\'success' to `execute-result' + (catch 'execute-result + (when-let* ((cur-pos (point)) + (cur-node (treesit-node-at cur-pos)) + (cur-node-type (treesit-node-type cur-node)) + (parent-node (treesit-node-parent cur-node)) ; could be nil + (parent-node-type (treesit-node-type parent-node))) + (cond + ((equal parent-node-type "raw_blck") + (insert-tab) + (throw 'execute-result 'success)) + + ((or (equal cur-node-type "parbreak") + (equal parent-node-type "item") + ;; please turn on whitespace-mode to test the following conditions + (eobp) + (eq (point) (1- (point-max)))) + (when-let* ((cur-line-bol + (save-excursion + (back-to-indentation) + (point))) + (prev-nonwhite-pos (save-excursion + (goto-char cur-line-bol) + (skip-chars-backward "\s\r\n\t") + (1- (point)))) + ((and (not (eq prev-nonwhite-pos 0)) ; first line + (not (eq ; has previous sibling + (line-number-at-pos prev-nonwhite-pos) + (line-number-at-pos (point)))))) + (prev-nonwhite-line-node + (treesit-node-at prev-nonwhite-pos)) + (prev-nonwhite-line-bol + ;; TODO typst-ts-core-get-node-bol + (save-excursion + (goto-char prev-nonwhite-pos) + (back-to-indentation) + (point))) + (prev-nonwhite-line-heading-node + (treesit-node-at prev-nonwhite-line-bol)) + (prev-nonwhite-line-top-node (treesit-node-parent + prev-nonwhite-line-heading-node)) + (cur-line-bol-column (typst-ts-mode-column-at-pos cur-line-bol)) + (prev-nonwhite-line-bol-column + (typst-ts-mode-column-at-pos prev-nonwhite-line-bol))) + (cond + ;; 1. el + ;; 2. psy| <- can toggle indent + ((and + (equal (treesit-node-type prev-nonwhite-line-top-node) "item") + (equal (treesit-node-type prev-nonwhite-line-heading-node) "-") + ;; previous nonwhite-line ending is not '\' character + (not (equal (treesit-node-type prev-nonwhite-line-node) "linebreak"))) + ;; TODO cycle all its children + (let (point) + (if (not (eq cur-line-bol-column prev-nonwhite-line-bol-column)) + (progn + (setq point (point)) + (indent-line-to prev-nonwhite-line-bol-column) + (goto-char (- point typst-ts-mode-indent-offset))) + (setq point (point)) + (indent-line-to (+ typst-ts-mode-indent-offset + prev-nonwhite-line-bol-column)) + (goto-char (+ typst-ts-mode-indent-offset point))) + (throw 'execute-result 'success)))))) + (t nil))))) + ;; execute default action if not successful + (unless (eq execute-result 'success) + (call-interactively (global-key-binding (kbd "TAB")))))) + +(provide 'typst-ts-editing) + +;;; typst-ts-editing.el ends here diff --git a/typst-ts-embedding-lang-settings.el b/typst-ts-embedding-lang-settings.el index f5d6039a88..c663fb8c53 100644 --- a/typst-ts-embedding-lang-settings.el +++ b/typst-ts-embedding-lang-settings.el @@ -21,7 +21,7 @@ ;;; Code: (require 'treesit) -(require 'typst-ts-utils) +(require 'typst-ts-core) (defcustom typst-ts-highlight-raw-block-langs-not-in-predefined-settings t "Whether to highlight raw block of language that is not in settings. @@ -773,10 +773,10 @@ Use this function as one notifier of `treesit-parser-notifiers'." ;; parsers created by `treesit-language-at-point-function' ( ;; `typst-ts-mode--language-at-point'.) ;; i.e. parsers cannot be created by `treesit-range-settings' - (mapcar #'treesit-parser-language (typst-ts-utils-parser-list)) + (mapcar #'treesit-parser-language (typst-ts-core-parser-list)) ;; parsers created by `treesit-range-settings' (mapcar #'treesit-parser-language - (typst-ts-utils-local-parsers-on (point-min) (point-max)))))) + (typst-ts-core-local-parsers-on (point-min) (point-max)))))) lang-ts-mode settings) (dolist (lang parser-langs) (unless (member lang typst-ts-els--include-languages) @@ -789,7 +789,7 @@ Use this function as one notifier of `treesit-parser-notifiers'." (typst-ts-els-merge-lang-settings lang) ;; some feature like cmake-ts-mode will create a parser when ;; the feature is required, so we need to clean thease parsers - (mapc #'treesit-parser-delete (typst-ts-utils-parser-list nil lang)) + (mapc #'treesit-parser-delete (typst-ts-core-parser-list nil lang)) (message "Load %s language settings from configuration." lang)) (error ;; if language not in setting or encounter error during loading, @@ -801,7 +801,7 @@ Use this function as one notifier of `treesit-parser-notifiers'." (typst-ts-els--add-treesit-range-rules lang) ;; delete top level parsers, so range rules works (i.e. local parsers) ;; so that highlighting will not exceed the desired range - (mapc #'treesit-parser-delete (typst-ts-utils-parser-list nil lang)) + (mapc #'treesit-parser-delete (typst-ts-core-parser-list nil lang)) ;; find and merge settings (setq lang-ts-mode diff --git a/typst-ts-mode.el b/typst-ts-mode.el index 37c24aca3c..15896330fb 100644 --- a/typst-ts-mode.el +++ b/typst-ts-mode.el @@ -34,11 +34,12 @@ (require 'outline) (require 'typst-ts-embedding-lang-settings) -(require 'typst-ts-utils) +(require 'typst-ts-core) (require 'typst-ts-faces) (require 'typst-ts-compile) (require 'typst-ts-watch-mode) (require 'typst-ts-edit-indirect) +(require 'typst-ts-editing) (defgroup typst-ts nil "Tree Sitter enabled Typst Writing." @@ -396,13 +397,6 @@ If you want to customize the rules, please customize the same name variable (regexp-opt '("block" "content" "group" "math" "_math_group")) "Container node types regexp.") -(defun typst-ts-mode--get-node-bol (node) - "Get the NODE's indentation offset (at node beginning)." - (save-excursion - (goto-char (treesit-node-start node)) - (back-to-indentation) - (point))) - (defun typst-ts-mode--identation-item-linebreak (_node _parent bol) "Where the current line is underneath a item with linebreak as ending. Ignore whitespaces. @@ -483,7 +477,7 @@ Used in `typst-ts-mode-indent-rules'." (bol-col (typst-ts-mode-column-at-pos bol)) (raw-block-bol - (typst-ts-mode--get-node-bol (treesit-node-parent parent))) + (typst-ts-core-get-node-bol (treesit-node-parent parent))) (raw-block-bol-col (typst-ts-mode-column-at-pos raw-block-bol))) (if (equal "blob" prev-line-node-type) @@ -595,14 +589,14 @@ NODE, PARENT and BOL see `treesit-indent-function'." (error "Variable `typst-ts-mode-indent-function' shouldn't be null!")) (let ((res (funcall typst-ts-mode-indent-function node parent bol))) ;; if it is a highlighted raw block region (i.e. contains at least one local parser) - (when (typst-ts-utils-local-parsers-at (treesit-node-start node)) + (when (typst-ts-core-local-parsers-at (treesit-node-start node)) ;; when there is no matching rules (unless (car res) (setcar res bol) (setcdr res 0)) (let* ((blob_node (treesit-node-at bol 'typst)) (raw_block_node (treesit-node-parent blob_node)) - (raw_block_bol (typst-ts-mode--get-node-bol raw_block_node)) + (raw_block_bol (typst-ts-core-get-node-bol raw_block_node)) (raw_block_bol_column (typst-ts-mode-column-at-pos raw_block_bol)) (res-column (+ (typst-ts-mode-column-at-pos (car res)) (cdr res)))) (when (> raw_block_bol_column res-column) @@ -635,8 +629,7 @@ NODE, PARENT and BOL see `treesit-indent-function'." "Generate name of NODE for displaying in Imenu." (treesit-node-text node)) -;; outline-minor-mode ================================================================================ - +;; outline-minor-mode (defconst typst-ts-mode-outline-regexp "^[[:space:]]*\\(=+\\) " "Regexp identifying Typst header.") @@ -648,272 +641,6 @@ NODE, PARENT and BOL see `treesit-indent-function'." (- (match-end 1) (match-beginning 1)) 0))) -(defun typst-ts-mode-heading--at-point-p () - "Whether the current line is a heading. -Return the heading node when yes otherwise nil." - (let ((node (treesit-node-parent - (treesit-node-at - (save-excursion - (beginning-of-line-text) - (point)))))) - (if (string= (treesit-node-type node) "heading") - node - nil))) - -;;;###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)) - -;;;###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. -`left': `typst-ts-mode-heading-decrease', -`right': `typst-ts-mode-heading-increase', -`up': `typst-ts-mode-heading-up', -`down': `typst-ts-mode-heading-down'. -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 #'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 - (call-interactively (car call-me/string)) - (call-interactively - (keymap-lookup global-map (substitute-command-keys (cdr call-me/string))))))) - -(defun typst-ts-mode--item-on-line-p () - "Does the current line have an item node? -Return the node when yes otherwise -return the node that is one character left from the end of line." - (treesit-node-parent - (treesit-node-at - (save-excursion - ;; starting from the beginning because line could be 1. wow. - (beginning-of-line) - (condition-case nil - (progn - (search-forward-regexp (rx (or "+" "-" ".")) - (pos-eol) - nil - nil) - (left-char)) - (search-failed - ;; need to go to the end of line and then one left because end of line is the next node - (goto-char (1- (pos-eol))))) - (point))))) - -(defun typst-ts-mode-meta-return (&optional arg) - "Depending on context, insert a heading or insert an item. -The new heading is created after the ending of current heading. -Using ARG argument will ignore the context and it will insert a heading instead." - (interactive "P") - (let ((node (typst-ts-mode--item-on-line-p))) - (cond - (arg (typst-ts-mode-insert--heading nil)) - ((string= (treesit-node-type node) "item") - (typst-ts-mode-insert--item node)) - (t - (typst-ts-mode-insert--heading node))))) - -(defun typst-ts-mode-return (&optional arg) - "Handle RET depends on condition. -When prefix ARG is non-nil, call global return function." - (interactive "P") - (let (execute-result node) - (setq - execute-result - (catch 'execute-result - (when-let* ((cur-pos (point)) - (cur-node (treesit-node-at cur-pos)) - (cur-node-type (treesit-node-type cur-node)) - (parent-node (treesit-node-parent cur-node)) ; could be nil - (parent-node-type (treesit-node-type parent-node))) - (cond - (arg (throw 'execute-result 'default)) - ;; on item node end - ((and (eolp) - (setq node (typst-ts-mode--item-on-line-p)) - (string= (treesit-node-type node) "item") - (not (string= (typst-ts-utils-node-get node '((child -1 nil) (type))) "linebreak"))) - (if (> (treesit-node-child-count node) 1) - (typst-ts-mode-insert--item node) - ;; no text means delete the item on current line - (beginning-of-line) - (kill-line) - (indent-according-to-mode)) - (throw 'execute-result 'success)) - )))) - ;; execute default action if not successful - (unless (eq execute-result 'success) - (let ((global-ret-function - (global-key-binding (kbd "RET")))) - (if (and current-prefix-arg - (yes-or-no-p - (format - "Execute function `%s' with the given prefix argument?" - global-ret-function))) - (call-interactively global-ret-function) - (let ((current-prefix-arg nil)) - (call-interactively global-ret-function))))))) - -(defun typst-ts-mode-insert--item (node) - "Insert an item after NODE. -NODE must be an item node! -This function respects indentation." - (let* (;; +, -, or <num>. - (item-type (treesit-node-text - (treesit-node-child node 0))) - (item-number (string-to-number item-type)) - (item-end (treesit-node-end node)) - (node-bol-column (typst-ts-mode-column-at-pos - (typst-ts-mode--get-node-bol node)))) - (goto-char item-end) - (newline) - (indent-line-to node-bol-column) - (insert (if (= item-number 0) - item-type - (concat (number-to-string (1+ item-number)) ".")) - " "))) - -(defun typst-ts-mode-insert--heading (node) - "Insert a heading after the section that NODE is part of. -When there is no section it will insert a heading below point." - (let* ((section - (treesit-parent-until - node - (lambda (node) - (string= (treesit-node-type node) "section")) - t)) - ;; first child is heading - (heading (treesit-node-child section 0)) - (heading-level (treesit-node-type (treesit-node-child heading 0)))) - (if section - (goto-char (treesit-node-end section)) - ;; no headings so far - (setq heading-level "=") - (forward-line 1)) - ;; something can be in the next line/section, the heading needs be on its own line - ;; this has to be done after `goto-char' because it will invalidate the node - (newline) - (forward-line -1) - ;; insert the heading and indent - (insert heading-level " ") - (indent-according-to-mode))) - -(defun typst-ts-mode-column-at-pos (point) - "Get the column at position POINT." - (save-excursion - (goto-char point) - (current-column))) - -;;;###autoload -(defun typst-ts-mode-cycle (&optional _arg) - "Cycle." - (interactive "P") - (let (execute-result) - (setq - execute-result - ;; plz manually throw `\'success' to `execute-result' - (catch 'execute-result - (when-let* ((cur-pos (point)) - (cur-node (treesit-node-at cur-pos)) - (cur-node-type (treesit-node-type cur-node)) - (parent-node (treesit-node-parent cur-node)) ; could be nil - (parent-node-type (treesit-node-type parent-node))) - (cond - ((equal parent-node-type "raw_blck") - (insert-tab) - (throw 'execute-result 'success)) - - ((or (equal cur-node-type "parbreak") - (equal parent-node-type "item") - ;; please turn on whitespace-mode to test the following conditions - (eobp) - (eq (point) (1- (point-max)))) - (when-let* ((cur-line-bol - (save-excursion - (back-to-indentation) - (point))) - (prev-nonwhite-pos (save-excursion - (goto-char cur-line-bol) - (skip-chars-backward "\s\r\n\t") - (1- (point)))) - ((and (not (eq prev-nonwhite-pos 0)) ; first line - (not (eq ; has previous sibling - (line-number-at-pos prev-nonwhite-pos) - (line-number-at-pos (point)))))) - (prev-nonwhite-line-node - (treesit-node-at prev-nonwhite-pos)) - (prev-nonwhite-line-bol - ;; TODO typst-ts-mode--get-node-bol - (save-excursion - (goto-char prev-nonwhite-pos) - (back-to-indentation) - (point))) - (prev-nonwhite-line-heading-node - (treesit-node-at prev-nonwhite-line-bol)) - (prev-nonwhite-line-top-node (treesit-node-parent - prev-nonwhite-line-heading-node)) - (cur-line-bol-column (typst-ts-mode-column-at-pos cur-line-bol)) - (prev-nonwhite-line-bol-column - (typst-ts-mode-column-at-pos prev-nonwhite-line-bol))) - (cond - ;; 1. el - ;; 2. psy| <- can toggle indent - ((and - (equal (treesit-node-type prev-nonwhite-line-top-node) "item") - (equal (treesit-node-type prev-nonwhite-line-heading-node) "-") - ;; previous nonwhite-line ending is not '\' character - (not (equal (treesit-node-type prev-nonwhite-line-node) "linebreak"))) - ;; TODO cycle all its children - (let (point) - (if (not (eq cur-line-bol-column prev-nonwhite-line-bol-column)) - (progn - (setq point (point)) - (indent-line-to prev-nonwhite-line-bol-column) - (goto-char (- point typst-ts-mode-indent-offset))) - (setq point (point)) - (indent-line-to (+ typst-ts-mode-indent-offset - prev-nonwhite-line-bol-column)) - (goto-char (+ typst-ts-mode-indent-offset point))) - (throw 'execute-result 'success)))))) - (t nil))))) - ;; execute default action if not successful - (unless (eq execute-result 'success) - (call-interactively (global-key-binding (kbd "TAB")))))) ;;;###autoload (defun typst-ts-mode-preview (file) @@ -1042,7 +769,7 @@ typst tree sitter grammar (at least %s)!" (current-time-string min-time)) (typst-ts-els-merge-settings config) ;; some feature like cmake-ts-mode will create a parser when ;; the feature is required, so we need to clean thease parsers - (mapc #'treesit-parser-delete (typst-ts-utils-parser-list nil lang)) + (mapc #'treesit-parser-delete (typst-ts-core-parser-list nil lang)) (add-to-list 'typst-ts-els--include-languages lang)))) (typst-ts-mode-check-grammar-version))