Skip to content

Instantly share code, notes, and snippets.

@lesteral
Created May 19, 2024 19:36
Show Gist options
  • Save lesteral/007afd6f0cf2505efcacaaf640edb698 to your computer and use it in GitHub Desktop.
Save lesteral/007afd6f0cf2505efcacaaf640edb698 to your computer and use it in GitHub Desktop.
Angular templating setup with typescript-ts-mode
; This gist is intended to be guidance for a developer of Angular “Components”,
; which may include embedded HTML in inline “template” strings
; (see https://angular.io/guide/component-overview#defining-a-components-template for an example),
; to achieve proper formatting of both the Typescript and HTML code,
; in Emacs, when using `typescript-ts-mode' (a “tree-sitter” mode).
; The approach is based on the methodology described at
; https://www.gnu.org/software/emacs/manual/html_node/elisp/Multiple-Languages.html - 37.6 Parsing Text in Multiple Languages
; I’ve used this approach successfully with this software combination:
; (1) Emacs 29.3, compiled with tree-sitter support
; (I’m using https://packages.debian.org/bookworm-backports/emacs-pgtk , which also includes `typescript-ts-mode'.)
; (2) https://github.com/tree-sitter/tree-sitter-typescript/releases/tag/v0.20.3 - both typescript (and tsx) parsers must be separately installed
; (I haven’t tried 'v0.20.4', released Jan. 31, 2024 - the latest as of May 19, 2024.)
; (3) https://github.com/tree-sitter/tree-sitter-html/releases/tag/v0.20.1 - also separately installed
; (4) https://github.com/mickeynp/html-ts-mode/blob/master/html-ts-mode.el - Mickey Petersen’s “html ts mode for Combobulate”
; Mickey Petersen’s articles:
; * https://www.masteringemacs.org/article/how-to-get-started-tree-sitter - “How to Get Started with Tree-Sitter”
; * https://www.masteringemacs.org/article/lets-write-a-treesitter-major-mode - “Let’s Write a Tree-Sitter Major Mode”
; are very helpful in teaching how to set up and use tree-sitter (with Emacs 29).
;
; I also found Mickey’s “Combobulate” package ( https://github.com/mickeynp/combobulate )
; to be quite useful in constructing the tree-sitter query used below.
; Thanks especially to Dmitry Gutov for the suggestions & guidance in https://github.com/dgutov/mmm-mode/issues/142 .
; (“mmm-mode” is itself targeted at multi-language support.
; I found “mmm-mode” to be very effective for this purpose, with `typescript-mode' https://github.com/emacs-typescript/typescript.el .)
(require 'typescript-ts-mode)
(require 'html-ts-mode)
(add-hook 'typescript-ts-mode-hook
(lambda ()
(when (and (treesit-available-p) (treesit-ready-p 'typescript) (treesit-ready-p 'html))
(setq treesit-range-settings
(treesit-range-rules
(lambda (beg end)
(treesit-parser-set-included-ranges (treesit-parser-create 'html)
(seq-map
; using "1+" & "1-" to skip leading/trailing backquotes which delimit Angular “template” strings
(lambda (elt) (let ((node (cdr elt))) (cons (1+ (treesit-node-start node)) (1- (treesit-node-end node)))))
(seq-filter (lambda (elt) (eq (car elt) 'templ))
(treesit-query-capture 'typescript '(((pair key: (property_identifier) @propid value: (template_string) @templ) (:equal "template" @propid))) beg end) ))))))
; as per https://www.masteringemacs.org/article/lets-write-a-treesitter-major-mode#:~:text=you%20can%20safely%20append%20to%20treesit%2Dfont%2Dlock%2Dsettings%20at%20any%20point
(setq treesit-font-lock-settings (append (apply #'treesit-font-lock-rules html-ts-font-lock-rules) treesit-font-lock-settings))
; The following is also needed in order for indenting to work properly in Typescript + HTML code,
; esp. on account of this behavior of `treesit-language-at':
; “... It returns the return value of `treesit-language-at-point-function' if it’s non-nil,
; otherwise it returns the language of the first parser in `treesit-parser-list', or nil if there is no parser.”
; In the context of this gist, `treesit-parser-list' returns: (#<treesit-parser for html> #<treesit-parser for typescript>),
; for which case the “first” parser is 'html, not 'typescript, which leads to various issues, if not configured as shown below.
(setq-local treesit-language-at-point-function
(lambda (pos)
(if (string= "template_string" (treesit-node-type (treesit-node-parent (treesit-node-at pos 'typescript))))
'html
'typescript)
))
; It’s also useful to add HTML indenting rules to `treesit-simple-indent-rules', so that indenting also works in the embedded HTML template strings.
; I found that Mickey Petersen’s rules work well for this purpose:
; https://github.com/mickeynp/html-ts-mode/blob/master/html-ts-mode.el#:~:text=%28setq%2Dlocal-,treesit%2Dsimple%2Dindent%2Drules
; e.g.: (setq treesit-simple-indent-rules (append <HTML-TS-MODE--INDENT-RULES> treesit-simple-indent-rules))
)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment