branch: elpa/typst-ts-mode commit 770916c8a58d5181b34722e05ce2bdc5453193df Author: Meow King <mr.meowk...@anche.no> Commit: Meow King <mr.meowk...@anche.no>
refactor: extract compilation related functions into typst-ts-compile.el --- .gitignore | 1 + typst-ts-compile.el | 139 +++++++++++++++++++++++++++++ typst-ts-embedding-lang-settings.el | 4 + typst-ts-mode.el | 172 ++++++++---------------------------- typst-ts-utils.el | 10 ++- typst-ts-watch-mode.el | 28 ++++-- 6 files changed, 209 insertions(+), 145 deletions(-) diff --git a/.gitignore b/.gitignore index 05c1625e98..2f7f1f10ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.elc indentation-test.typ +/makem.sh # Added by cargo diff --git a/typst-ts-compile.el b/typst-ts-compile.el new file mode 100644 index 0000000000..dbcac7be47 --- /dev/null +++ b/typst-ts-compile.el @@ -0,0 +1,139 @@ +;;; typst-ts-compile.el --- Compile Typst Files -*- lexical-binding: t; -*- +;; Copyright (C) 2024 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: + +;;; Code: +(require 'compile) + +(defgroup typst-ts-compile nil + "Typst TS Compilation." + :prefix "typst-ts-compile" + :group 'typst-ts) + +(defcustom typst-ts-compile-executable-location "typst" + "The location or name(if in variable `exec-path') for Typst executable." + :type 'string + :group 'typst-ts-compile) + +(defcustom typst-ts-compile-options "" + "User defined compile options for `typst-ts-compile'. +The compile options will be passed to the end of +`<typst-executable> compile <current-file>' command." + :type 'string + :group 'typst-ts) + +(defcustom typst-ts-compile-before-compilation-hook nil + "Hook runs after compile." + :type 'hook + :group 'typst-ts) + +(defcustom typst-ts-compile-after-compilation-hook nil + "Hook runs after compile. +Note the requirement of this hook is the same as `compilation-finish-functions'. +Also note that this hook runs with typst buffer(the buffer you are editing) as +the current buffer." + :type 'hook + :group 'typst-ts) + +(defun typst-ts-compile--compilation-finish-function (cur-buffer) + "Compilation finish function. +For `typst-ts-compile-after-compilation-hook' and +`compilation-finish-functions'. CUR-BUFFER: original typst buffer, in case +user set `display-buffer-alist' option for compilation buffer to switch to +compilation buffer before compilation." + (lambda (compilation-buffer msg) + (unwind-protect + (with-current-buffer cur-buffer + (run-hook-with-args 'typst-ts-compile-after-compilation-hook compilation-buffer msg)) + (remove-hook 'compilation-finish-functions + (typst-ts-compile--compilation-finish-function cur-buffer))))) + +(defun typst-ts-compile () + "Compile current typst file." + (interactive) + (run-hooks typst-ts-compile-before-compilation-hook) + + ;; The reason to take such a awkward solution is that `compilation-finish-functions' + ;; should be a global variable and also its functions. It doesn't work if we + ;; define them inside a let binding. + (add-hook 'compilation-finish-functions + (typst-ts-compile--compilation-finish-function (current-buffer))) + (compile + (format "%s compile %s %s" + typst-ts-compile-executable-location + (file-name-nondirectory buffer-file-name) + typst-ts-compile-options) + 'typst-ts-compilation-mode)) + +(defun typst-ts-compile-get-result-pdf-filename (&optional buffer check) + "Get the result PDF filename based on the name of BUFFER. +If BUFFER is nil, it means use the current buffer. +CHECK: non-nil mean check the file existence. +Return nil if the BUFFER has not associated file or the there is +no compiled pdf file when CHECK is non-nil." + (when buffer-file-name + (let ((res (concat (file-name-base (buffer-file-name buffer)) ".pdf"))) + (if check + (when (file-exists-p res) + res) + res)))) + + +(defun typst-ts-mode-compile-and-preview--compilation-finish-function (cur-buffer) + "For `typst-ts-compile-and-preview' and `compilation-finish-functions'. +CUR-BUFFER: original typst buffer, in case user set +`display-buffer-alist' option for compilation buffer to switch to compilation +buffer before compilation." + (lambda (_b _msg) + (unwind-protect + (browse-url (typst-ts-compile-get-result-pdf-filename cur-buffer)) + (remove-hook 'compilation-finish-functions + (typst-ts-mode-compile-and-preview--compilation-finish-function cur-buffer))))) + +;;;###autoload +(defun typst-ts-compile-and-preview () + "Compile & Preview. +Assuming the compile output file name is in default style." + (interactive) + ;; use a local variable version of `compilation-finish-functions' to shadow + ;; global version doesn't work + (add-hook 'compilation-finish-functions + (typst-ts-mode-compile-and-preview--compilation-finish-function + (current-buffer))) + (typst-ts-compile)) + +(defvar typst-ts-compilation-mode-error + (cons (rx bol "error:" (+ not-newline) "\n" (+ blank) "┌─ " + (group (+ not-newline)) ":" ;; file + (group (+ num)) ":" ;; start-line + (group (+ num)) "\n") ;; start-col + '(1 2 3)) + "Regexp for Error in compilation buffer.") + +;;;###autoload +(define-compilation-mode typst-ts-compilation-mode "Typst Compilation" + "Customized major mode for typst watch compilation." + (setq-local compilation-error-regexp-alist-alist nil) + (add-to-list 'compilation-error-regexp-alist-alist + (cons 'typst-error typst-ts-compilation-mode-error)) + (setq-local compilation-error-regexp-alist nil) + (add-to-list 'compilation-error-regexp-alist 'typst-error)) + +(provide 'typst-ts-compile) + +;;; typst-ts-compile.el ends here diff --git a/typst-ts-embedding-lang-settings.el b/typst-ts-embedding-lang-settings.el index dcba2183e6..7372350db5 100644 --- a/typst-ts-embedding-lang-settings.el +++ b/typst-ts-embedding-lang-settings.el @@ -835,6 +835,8 @@ Use this function as one notifier of `treesit-parser-notifiers'." ;;; synchronizely) ============================================================= (defun typst-ts-els--get-lang-input (lang) + "Get lowercase symbol of LANG. +LANG should be either a symbol or string." (if (symbolp lang) (intern (downcase (symbol-name lang))) (if (stringp lang) @@ -842,6 +844,8 @@ Use this function as one notifier of `treesit-parser-notifiers'." (error "LANG should be either symbol or string")))) (defun typst-ts-els--get-tags-input (tags) + "Get lowercase string(s) of TAGS. +TAGS should be either a string or a list of strings." (if (stringp tags) (list (downcase tags)) (if (and (listp tags) diff --git a/typst-ts-mode.el b/typst-ts-mode.el index feae32afc2..a91271e6c2 100644 --- a/typst-ts-mode.el +++ b/typst-ts-mode.el @@ -30,12 +30,12 @@ ;;; Code: (require 'treesit) -(require 'compile) (require 'outline) (require 'typst-ts-embedding-lang-settings) (require 'typst-ts-utils) (require 'typst-ts-faces) +(require 'typst-ts-compile) (require 'typst-ts-watch-mode) (defgroup typst-ts nil @@ -85,31 +85,6 @@ Note: this may take some time for documents with lot of raw blocks." :type 'boolean :group 'typst-ts) -(defcustom typst-ts-mode-executable-location "typst" - "The location or name(if in variable `exec-path') for Typst executable." - :type 'string - :group 'typst-ts) - -(defcustom typst-ts-mode-compile-options "" - "User defined compile options for `typst-ts-mode-compile'. -The compile options will be passed to the end of -`<typst-executable> compile <current-file>' command." - :type 'string - :group 'typst-ts) - -(defcustom typst-ts-mode-before-compile-hook nil - "Hook runs after compile." - :type 'hook - :group 'typst-ts) - -(defcustom typst-ts-mode-after-compile-hook nil - "Hook runs after compile. -Note the requirement of this hook is the same as `compilation-finish-functions'. -Also note that this hook runs with typst buffer(the buffer you are editing) as -the current buffer." - :type 'hook - :group 'typst-ts) - ;; ============================================================================== ;; TODO typst has three modes (namely 'markup', 'code' and 'math') ;; Currently only add common settings to syntax table @@ -126,7 +101,7 @@ the current buffer." "You can customize this variable to override the whole default font lock rules. Like this: -(setq typst-ts-mode-font-lock-rules + (setq typst-ts-mode-font-lock-rules (append (typst-ts-mode-font-lock-rules) \='( @@ -175,7 +150,7 @@ BTW, if you want to enable/disable specific font lock feature, please change "See variable `typst-ts-mode-font-lock-rules'.") (defun typst-ts-mode-highlight-raw-block-fn (node _override _start _end) - "A function used in `typst-ts-mode-font-lock-rules'. + "A function used in function `typst-ts-mode-font-lock-rules'. This function assign `typst-ts-markup-rawblock-blob-face' to those raw block whose language cannot be found or be loaded. NODE." @@ -666,18 +641,6 @@ NODE, PARENT and BOL see `treesit-indent-function'." "Generate name of NODE for displaying in Imenu." (treesit-node-text node)) -(defun typst-ts-mode-compile--compilation-finish-function (cur-buffer) - "For `typst-ts-mode-after-compile-hook' and `compilation-finish-functions'. -CUR-BUFFER: original typst buffer, in case user set -`display-buffer-alist' option for compilation buffer to switch to compilation -buffer before compilation." - (lambda (compilation-buffer msg) - (unwind-protect - (with-current-buffer cur-buffer - (run-hook-with-args 'typst-ts-mode-after-compile-hook compilation-buffer msg)) - (remove-hook 'compilation-finish-functions - (typst-ts-mode-compile--compilation-finish-function cur-buffer))))) - ;; outline-minor-mode ================================================================================ (defconst typst-ts-mode-outline-regexp "^[[:space:]]*\\(=+\\) " @@ -736,46 +699,27 @@ Return the heading node when yes otherwise nil." 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))))) + ;; 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 (car call-me/string)) (call-interactively (keymap-lookup global-map (substitute-command-keys (cdr call-me/string))))))) -(defun typst-ts-mode-compile () - "Compile current typst file." - (interactive) - (run-hooks typst-ts-mode-before-compile-hook) - - ;; The reason to take such a awkward solution is that `compilation-finish-functions' - ;; should be a global variable and also its functions. It doesn't work if we - ;; define them inside a let binding. - (add-hook 'compilation-finish-functions - (typst-ts-mode-compile--compilation-finish-function (current-buffer))) - (compile - (format "%s compile %s %s" - typst-ts-mode-executable-location - (file-name-nondirectory buffer-file-name) - typst-ts-mode-compile-options) - 'typst-ts-compilation-mode)) - -;; RETURN ================================================================================ - (defun typst-ts-mode--item-on-line-p () "Does the current line have an item node? Return the node when yes otherwise @@ -895,58 +839,10 @@ When there is no section it will insert a heading below point." (insert heading-level " ") (indent-according-to-mode))) -;;;###autoload -(defun typst-ts-mode-preview (file) - "Open the result compile file. -FILE: file path for the result compile file." - (interactive (list (concat (file-name-base buffer-file-name) ".pdf"))) - ;; don't use `browse-url-of-file', which cannot open non-english documents - (browse-url file)) - -(defun typst-ts-mode-compile-and-preview--compilation-finish-function (cur-buffer) - "For `typst-ts-mode-compile-and-preview' and `compilation-finish-functions'. -CUR-BUFFER: original typst buffer, in case user set -`display-buffer-alist' option for compilation buffer to switch to compilation -buffer before compilation." - (lambda (_b _msg) - (unwind-protect - (with-current-buffer cur-buffer - (call-interactively #'typst-ts-mode-preview)) - (remove-hook 'compilation-finish-functions - (typst-ts-mode-compile-and-preview--compilation-finish-function cur-buffer))))) - -;;;###autoload -(defun typst-ts-mode-compile-and-preview () - "Compile & Preview. -Assuming the compile output file name is in default style." - (interactive) - ;; use a local variable version of `compilation-finish-functions' to shadow - ;; global version doesn't work - (add-hook 'compilation-finish-functions - (typst-ts-mode-compile-and-preview--compilation-finish-function - (current-buffer))) - (typst-ts-mode-compile)) - -(defvar typst-ts-compilation-mode-error - (cons (rx bol "error:" (+ not-newline) "\n" (+ blank) "┌─ " - (group (+ not-newline)) ":" ;; file - (group (+ num)) ":" ;; start-line - (group (+ num)) "\n") ;; start-col - '(1 2 3)) - "Regexp for Error in compilation buffer.") - -;;;###autoload -(define-compilation-mode typst-ts-compilation-mode "Typst Compilation" - "Customized major mode for typst watch compilation." - (setq-local compilation-error-regexp-alist-alist nil) - (add-to-list 'compilation-error-regexp-alist-alist - (cons 'typst-error typst-ts-compilation-mode-error)) - (setq-local compilation-error-regexp-alist nil) - (add-to-list 'compilation-error-regexp-alist 'typst-error)) - -(defun typst-ts-mode-column-at-pos (pos) +(defun typst-ts-mode-column-at-pos (point) + "Get the column at position POINT." (save-excursion - (goto-char pos) + (goto-char point) (current-column))) ;;;###autoload @@ -1025,11 +921,19 @@ Assuming the compile output file name is in default style." (unless (eq execute-result 'success) (call-interactively (global-key-binding (kbd "TAB")))))) +;;;###autoload +(defun typst-ts-mode-preview (file) + "Open the result compile file. +FILE: file path for the result compile file." + (interactive (typst-ts-compile-get-result-pdf-filename)) + ;; don't use `browse-url-of-file', which cannot open non-english documents + (browse-url file)) + ;;;###autoload (defvar typst-ts-mode-map (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-c c") #'typst-ts-mode-compile-and-preview) - (define-key map (kbd "C-c C-c C") #'typst-ts-mode-compile) + (define-key map (kbd "C-c C-c c") #'typst-ts-compile-and-preview) + (define-key map (kbd "C-c C-c C") #'typst-ts-compile) (define-key map (kbd "C-c C-c w") #'typst-ts-watch-mode) (define-key map (kbd "C-c C-c p") #'typst-ts-mode-preview) (define-key map (kbd "M-<left>") #'typst-ts-mode-heading-decrease) @@ -1197,10 +1101,12 @@ typst tree sitter grammar (at least %s)!" (current-time-string min-time)) ;; Compile Command (ignore-errors - (format "%s compile %s %s" - typst-ts-mode-executable-location - (file-name-nondirectory buffer-file-name) - typst-ts-mode-compile-options)) + (setq-local + compile-command + (format "%s compile %s %s" + typst-ts-compile-executable-location + (file-name-nondirectory buffer-file-name) + typst-ts-compile-options))) (when (>= emacs-major-version 30) (if (not typst-ts-mode-enable-raw-blocks-highlight) diff --git a/typst-ts-utils.el b/typst-ts-utils.el index aa6311ba1a..f94ce2c750 100644 --- a/typst-ts-utils.el +++ b/typst-ts-utils.el @@ -23,13 +23,15 @@ (require 'treesit) +(declare-function treesit-parser-list "treesit.c") + (defun typst-ts-utils-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 use their base buffer's parsers. -If LANGUAGE is non-nil, only return parsers for that language. " +If LANGUAGE is non-nil, only return parsers for that language." (if (>= emacs-major-version 30) (funcall #'treesit-parser-list buffer language) (let ((parsers (treesit-parser-list buffer))) @@ -43,7 +45,7 @@ If LANGUAGE is non-nil, only return parsers for that language. " "Return all the local parsers at POS. It's a copy of Emacs 30's `treesit-local-parsers-at' function. POS LANGUAGE WITH-HOST." - (if (>= emacs-major-version 30) + (if (fboundp 'treesit-local-parsers-at) (funcall #'treesit-local-parsers-at pos language with-host) (let ((res nil)) (dolist (ov (overlays-at (or pos (point)))) @@ -60,7 +62,7 @@ POS 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." - (if (>= emacs-major-version 30) + (if (fboundp 'treesit-local-parsers-on) (funcall #'treesit-local-parsers-on beg end language with-host) (let ((res nil)) (dolist (ov (overlays-in (or beg (point-min)) (or end (point-max)))) @@ -76,7 +78,7 @@ BEG END LANGUAGE WITH-HOST." "Get things from NODE by INSTRUCTIONS. It's a copy of Emacs 30's `treesit-node-get' function." (declare (indent 1)) - (if (>= emacs-major-version 30) + (if (fboundp 'treesit-node-get) (treesit-node-get node instructions) (while (and node instructions) (pcase (pop instructions) diff --git a/typst-ts-watch-mode.el b/typst-ts-watch-mode.el index a6c4d9385e..3b0cb3b95f 100644 --- a/typst-ts-watch-mode.el +++ b/typst-ts-watch-mode.el @@ -15,12 +15,19 @@ ;; 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: Minor mode for watching(hot compile) current typst file. +;;; Commentary: -;; +;; Minor mode for watching(hot compile) current typst file. ;;; Code: +(require 'typst-ts-compile) + +(defgroup typst-ts-watch nil + "Typst TS Watch." + :prefix "typst-ts-watch" + :group 'typst-ts) + (define-minor-mode typst-ts-watch-mode "Watch(hot compile) current typst file." :lighter " [Watch]" @@ -33,27 +40,32 @@ "User defined compile options for `typst-ts-watch'. The compile options will be passed to the `<typst-executable> watch <current-file>' sub-command." - :type 'string) + :type 'string + :group 'typst-ts-watch) (defcustom typst-ts-watch-process-name "*Typst-Watch*" "Process name for `typst watch' sub-command." - :type 'string) + :type 'string + :group 'typst-ts-watch) (defcustom typst-ts-watch-process-buffer-name "*Typst-Watch*" "Process buffer name for `typst watch' sub-command." - :type 'string) + :type 'string + :group 'typst-ts-watch) (defcustom typst-ts-display-watch-process-bufer-automatically t "Whether the typst watch process buffer should be displayed automatically. This means the buffer will be displayed when error occurs, hide when error is eliminated." - :type 'boolean) + :type 'boolean + :group 'typst-ts-watch) (defcustom typst-ts-display-watch-process-buffer-parameters `(display-buffer-at-bottom (window-height . fit-window-to-buffer)) "Display buffer parameters." - :type 'symbol) + :type 'symbol + :group 'typst-ts-watch) (defvar typst-ts-before-watch-hook nil "Hook runs before compile.") @@ -116,7 +128,7 @@ PROC: process; OUTPUT: new output from PROC." (start-process-shell-command typst-ts-watch-process-name typst-ts-watch-process-buffer-name (format "%s watch %s %s" - typst-ts-mode-executable-location + typst-ts-compile-executable-location (file-name-nondirectory buffer-file-name) typst-ts-watch-options)) 'typst-ts--watch-process-filter)