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

    feat: block editing
---
 typst-ts-edit-indirect.el | 116 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/typst-ts-edit-indirect.el b/typst-ts-edit-indirect.el
new file mode 100644
index 0000000000..e912946f45
--- /dev/null
+++ b/typst-ts-edit-indirect.el
@@ -0,0 +1,116 @@
+;;; typst-ts-watch-mode.el --- Edit blocks in separate buffer  -*- 
lexical-binding: t; -*-
+;; Copyright (C) 2023 Meow King <mr.meowk...@anche.no>
+
+;; 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:
+
+;; Integration with edit-indirect.  Get it from 
<https://github.com/Fanael/edit-indirect/>.
+
+;;; Code:
+
+(require 'edit-indirect)
+
+(defun typst-ts-edit-indirect--guess-mode (parent-buffer beg _end)
+  "Guess the mode for `edit-indirect-guess-mode-function'.
+BEG in the PARENT-BUFFER will be used to traverse the treesitter tree to
+lang: (ident).
+
+By default the treesit mode will be preferred.
+If the user does not have the grammar installed it will fallback to the
+non treesitter mode.
+If there is no fitting mode or no lang it will be `normal-mode'."
+  (let* ((lang (treesit-node-text
+                (treesit-node-child-by-field-name
+                 (treesit-node-parent
+                  (with-current-buffer parent-buffer
+                    (treesit-node-at beg)))
+                 "lang")))
+         (lang (if (string= lang "cpp") "c++" lang))
+         (ts-mode (intern-soft
+                   (concat lang "-ts-mode")))
+         (non-ts-mode (intern-soft
+                       (concat lang "-mode"))))
+    (cond
+     ((not lang) (normal-mode))
+     ((and (treesit-language-available-p (intern lang) nil)
+           (fboundp ts-mode))
+      (funcall ts-mode))
+     ((fboundp non-ts-mode) (funcall non-ts-mode))
+     (t (normal-mode)))))
+
+(defun typst-ts-edit-indirect ()
+  "Edit the block at point with `edit-indirect-region'."
+  (interactive)
+  (let* ((block (treesit-parent-until
+                 (treesit-node-at (point))
+                 (lambda (node)
+                   (string= (treesit-node-type node) "raw_blck"))
+                 t))
+         (_ (unless block (user-error "Point is not on a raw block")))
+         (blob (car (treesit-filter-child
+                     block
+                     (lambda (node)
+                       (string= (treesit-node-type node) "blob")))))
+         (beg (treesit-node-start blob))
+         (end (treesit-node-end blob)))
+    ;; when beg and end are on the same line it will look like:
+    ;; ```lang content```
+    ;; although it is valid syntax, I think it should be handled because if you
+    ;; want to edit a raw block in a separate buffer you are
+    ;; probably going to insert newlines
+    ;; it is handled by inserting 2 newlines
+    (if (= (line-number-at-pos beg) (line-number-at-pos end))
+        (progn (save-excursion
+                 (goto-char end)
+                 (newline)
+                 (goto-char beg)
+                 ;; when there is content
+                 ;; it will include the space between lang and content
+                 (unless (= beg end)
+                   (delete-char 1))
+                 (newline))
+               (typst-ts-edit-indirect))
+      ;; beg sadly starts at the line with the lang
+      ;; beg needs to be the next line
+      (setq beg (1+ beg))
+      ;; when beg and end are the same now it will look like
+      ;; ```lang
+      ;; ```
+      ;; when this is the case a newline must be added
+      ;; end will be at the beginning of the closing ```
+      (when (= beg end)
+        (save-excursion
+          (goto-char beg)
+          (newline)
+          (setq end (line-beginning-position))))
+      ;; in the block, there needs to be a space when it is empty
+      ;; `edit-indirect-region' does not like editing empty blocks
+      (save-excursion
+        (goto-char beg)
+        (insert " "))
+      (edit-indirect-region beg end t)
+      ;; delete the inserted space
+      (save-excursion
+        (goto-char (point-min))
+        (delete-char 1)))))
+
+(add-hook 'typst-ts-mode-hook (lambda ()
+                                (setq-local edit-indirect-guess-mode-function
+                                            
#'typst-ts-edit-indirect--guess-mode)))
+
+(provide 'typst-ts-edit-indirect)
+;;; typst-ts-edit-indirect.el ends here
+

Reply via email to