Skip to content

Commit

Permalink
Add jtsx-typescript-mode for pure TS files.
Browse files Browse the repository at this point in the history
  • Loading branch information
llemaitre19 committed Feb 15, 2024
1 parent bb1e99e commit a411e85
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 35 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ jobs:
- name: Check out the source code
uses: actions/checkout@v4

- name: Install treesit javascript language
- name: Install treesit Javascript language
run: emacs --batch -l jtsx.el --eval "(jtsx-install-treesit-language 'javascript)"

- name: Install treesit Typescript language
- name: Install treesit TSX language
run: emacs --batch -l jtsx.el --eval "(jtsx-install-treesit-language 'tsx)"

- name: Install treesit Typescript language
run: emacs --batch -l jtsx.el --eval "(jtsx-install-treesit-language 'typescript)"

- name: Run package lint check
run: |
emacs --batch \
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## Master

* Add `jtsx-typescript-mode` based on `typescript-ts-mode` for pure `TS` files.
* Fix wrong cursor position after wrapping.

## 0.3.5 (2024-02-10)
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Summary of features:
* Code folding
* Some additional indentation options

Note that `jtsx-jsx-mode` and `jtsx-tsx-mode` work as well respectively for standart `JS` and `TS` files.
Note that while `jtsx-jsx-mode` is fully compatible with pure `JS` files, `jtsx-tsx-mode` has some rare conflicts with `TS` files (e.g. type assertions). It is thus recommanded to use `jtsx-typescript-mode` (based on `typescript-ts-mode`) for plain `TS` files.

## Requirements

Expand All @@ -46,20 +46,23 @@ For more advanced usages, see `M-x treesit-install-language-grammar` command, or
Here an example of configuration using [use-package](https://github.com/jwiegley/use-package), to put in the `Emacs` `init.el`:

* attach `jtsx-jsx-mode` to `JSX` and `JS` files
* attach `jtsx-tsx-mode` to `TSX` and `TS` files
* attach `jtsx-tsx-mode` to `TSX` files
* attach `jtsx-typescript-mode` to `TS` files
* bind `jtsx` functions to the same shortcuts for `jtsx-jsx-mode` and `jtsx-tsx-mode`
* set indention offsets for `JSX` and `TSX` modes (use base mode variables)
* set indention offsets for `JSX`/`JS` and `TSX`/`TS` modes (use base mode variables)
* customize `jtsx` behaviour through provided variables
* enable `hideshow` minor mode for code folding

``` elisp
(use-package jtsx
:ensure t
:mode (("\\.jsx?\\'" . jtsx-jsx-mode)
("\\.tsx?\\'" . jtsx-tsx-mode))
("\\.tsx\\'" . jtsx-tsx-mode)
("\\.ts\\'" . jtsx-typescript-mode))
:commands jtsx-install-treesit-language
:hook ((jtsx-jsx-mode . hs-minor-mode)
(jtsx-tsx-mode . hs-minor-mode))
(jtsx-tsx-mode . hs-minor-mode)
(jtsx-typescript-mode . hs-minor-mode))
:custom
;; Optional customizations
;; (js-indent-level 2)
Expand Down
76 changes: 54 additions & 22 deletions jtsx.el
Original file line number Diff line number Diff line change
Expand Up @@ -864,14 +864,9 @@ If ADD-FIRST is not nil, preprend the RULE in the list for priority purpose."
(new-rules (remove rule original-rules)))
(setf (jtsx-ts-indent-rules-for-key ts-lang-key) new-rules)))

(defun jtsx-configure-mode-base (mode
mode-map
ts-lang-key
indent-var-name)
"Base function for JSX/TSX Major mode configuration.
MODE, MODE-MAP, TS-LANG-KEY, INDENT-VAR-NAME variables allow customization
depending on the mode."
;; Patch indentation
(defun jtsx-customize-indent-rules (ts-lang-key indent-var-name)
"Customize treesit indent rules for TS-LANG-KEY language.
INDENT-VAR-NAME is the name of the indent offset variable."
(jtsx-ts-add-indent-rule ts-lang-key
`((parent-is "switch_body") parent-bol ,jtsx-switch-indent-offset))
(when jtsx-indent-statement-block-regarding-standalone-parent
Expand All @@ -889,7 +884,17 @@ MODE, MODE-MAP, TS-LANG-KEY, INDENT-VAR-NAME variables allow customization
`((node-is "statement_block") standalone-parent ,indent-var-name))
(jtsx-ts-remove-indent-rule ts-lang-key
`((node-is "statement_block") parent-bol ,indent-var-name)))
(setq-local treesit-simple-indent-rules jtsx-ts-indent-rules)
(setq-local treesit-simple-indent-rules jtsx-ts-indent-rules))

(defun jtsx-configure-mode-base (mode
mode-map
ts-lang-key
indent-var-name)
"Base function for JSX/TSX Major mode configuration.
MODE, MODE-MAP, TS-LANG-KEY, INDENT-VAR-NAME variables allow customization
depending on the mode."
;; Customize indentation
(jtsx-customize-indent-rules ts-lang-key indent-var-name)

;; Use maximum level of syntax highlighting if enabled
(when jtsx-enable-all-syntax-highlighting-features
Expand Down Expand Up @@ -1350,23 +1355,32 @@ WHEN indicates when the mode starts to be obsolete."
;; Keep old jsx-mode for backward compatibility but mark it as obsolete.
(jtsx-define-obsolete-mode-alias 'jsx-mode 'jtsx-jsx-mode "jtsx 0.2.1")

(defun jtsx-add-support-for-switch-indent-option (ts-lang-key)
"Add support for switch/case indentation option for TS-LANG-KEY language."
;; Remove specific indent rule for `case' and `default' (introduced by commit ab12628) which
;; defeats `jtsx-switch-indent-offset' option.
(jtsx-ts-remove-indent-rule ts-lang-key '((or (node-is "case")
(node-is "default"))
parent-bol typescript-ts-mode-indent-offset)))

(defun jtsx-typescript-tsx-configure-mode-common(ts-lang-key)
"Common part of jtsx-typescript-mode and jtsx-tsx-mode.
TS-LANG-KEY is the treesit language key."
(setq-local jtsx-ts-indent-rules (typescript-ts-mode--indent-rules ts-lang-key))
(jtsx-add-support-for-switch-indent-option ts-lang-key)
(when (version<= emacs-version "29.2")
;; Fix a font lock bug
;; (see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69024)
(setq-local treesit-font-lock-settings
(jtsx-tsx-mode-font-lock-settings ts-lang-key))))

;;;###autoload
(define-derived-mode jtsx-tsx-mode tsx-ts-mode "TSX"
"Major mode extending `tsx-ts-mode'."
:group 'jtsx
(let ((ts-lang-key 'tsx))
(when (treesit-ready-p ts-lang-key)
(setq-local jtsx-ts-indent-rules (typescript-ts-mode--indent-rules ts-lang-key))
;; Remove specific indent rule for `case' and `default' (introduced by commit ab12628) which
;; defeats `jtsx-switch-indent-offset' option.
(jtsx-ts-remove-indent-rule ts-lang-key '((or (node-is "case")
(node-is "default"))
parent-bol typescript-ts-mode-indent-offset))
(when (version<= emacs-version "29.2")
;; Fix a font lock bug
;; (see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69024)
(setq-local treesit-font-lock-settings
(jtsx-tsx-mode-font-lock-settings ts-lang-key)))
(jtsx-typescript-tsx-configure-mode-common ts-lang-key)
(jtsx-configure-mode-base 'jtsx-tsx-mode jtsx-tsx-mode-map ts-lang-key
'typescript-ts-mode-indent-offset))))

Expand All @@ -1376,15 +1390,30 @@ WHEN indicates when the mode starts to be obsolete."
;; typescript-ts-mode package sets auto-mode-alist when loaded
(jtsx-prioritize-mode-if-present 'jtsx-tsx-mode)

;;;###autoload
(define-derived-mode jtsx-typescript-mode typescript-ts-mode "TS"
"Major mode extending `typescript-ts-mode'."
:group 'jtsx
(let ((ts-lang-key 'typescript))
(when (treesit-ready-p ts-lang-key)
(jtsx-typescript-tsx-configure-mode-common ts-lang-key)
(jtsx-customize-indent-rules ts-lang-key 'typescript-ts-mode-indent-offset)
(when jtsx-enable-all-syntax-highlighting-features
(setq-local treesit-font-lock-level 4))
(treesit-major-mode-setup))))

;; typescript-ts-mode package sets auto-mode-alist when loaded
(jtsx-prioritize-mode-if-present 'jtsx-typescript-mode)

;;;###autoload
(defun jtsx-install-treesit-language (ts-lang-key)
"Wrapper around `treesit-install-language-grammar' with preset sources.
TS-LANG-KEY is the language to be installed."
;; Known bug : calling `treesit-install-language-grammar' multiple times for the same language
;; seems buggy.
;; Fixed by commit 1098c114b74 in emacs 30.0.50
;; Fixed by commit 1098c114b74 in emacs 29.2
;; (bug https://debbugs.gnu.org/cgi/bugreport.cgi?bug=66673)
(interactive (list (intern (completing-read "Language: " '(javascript tsx)))))
(interactive (list (intern (completing-read "Language: " '(javascript tsx typescript)))))
(unless (alist-get ts-lang-key treesit-language-source-alist)
(let ((source (pcase ts-lang-key
('javascript '("https://github.com/tree-sitter/tree-sitter-javascript"
Expand All @@ -1393,6 +1422,9 @@ TS-LANG-KEY is the language to be installed."
('tsx '("https://github.com/tree-sitter/tree-sitter-typescript"
"master"
"tsx/src"))
('typescript '("https://github.com/tree-sitter/tree-sitter-typescript"
"master"
"typescript/src"))
(_ nil))))
(cl-assert source (format "Not expected language: %s" ts-lang-key))
(let ((lang-source (cons ts-lang-key source)))
Expand Down
16 changes: 10 additions & 6 deletions tests/jtsx-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -293,33 +293,37 @@ Turn this buffer in MODE mode if supplied or defaults to jtsx-tsx-mode."
(should (equal (comment-dwim-into-buffer content set-point #'jtsx-tsx-mode) result))))

;; TEST INDENTATION
(ert-deftest jtsx-test-no-indent-jsx-switch-case ()
(ert-deftest jtsx-test-no-indent-switch-case ()
(let ((jtsx-switch-indent-offset 0)
(content "switch (x) {\ncase true:\nbreak;\ndefault:\nbreak;\n};")
(result "switch (x) {\ncase true:\n break;\ndefault:\n break;\n};"))
(should (equal (indent-all-into-buffer content #'jtsx-jsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-typescript-mode) result))))

(ert-deftest jtsx-test-indent-jsx-switch-case ()
(ert-deftest jtsx-test-indent-switch-case ()
(let ((jtsx-switch-indent-offset indent-offset)
(content "switch (x) {\ncase true:\nbreak;\ndefault:\nbreak;\n};")
(result "switch (x) {\n case true:\n break;\n default:\n break;\n};"))
(should (equal (indent-all-into-buffer content #'jtsx-jsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-typescript-mode) result))))

(ert-deftest jtsx-test-indent-statement-block-regarding-parent ()
(let ((jtsx-indent-statement-block-regarding-standalone-parent nil)
(content "function test(a,\nb) {\nreturn a + b;\n}")
(result "function test(a,\n b) {\n return a + b;\n }"))
(should (equal (indent-all-into-buffer content #'jtsx-jsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-typescript-mode) result))))

(ert-deftest jtsx-test-indent-statement-block-regarding-standalone-parent ()
(let ((jtsx-indent-statement-block-regarding-standalone-parent t)
(content "function test(a,\nb) {\nreturn a + b;\n}")
(result "function test(a,\n b) {\n return a + b;\n}"))
(should (equal (indent-all-into-buffer content #'jtsx-jsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))))
(should (equal (indent-all-into-buffer content #'jtsx-tsx-mode) result))
(should (equal (indent-all-into-buffer content #'jtsx-typescript-mode) result))))

;; TEST JUMPS
(ert-deftest jtsx-test-jump-opening-element-starting-from-closing ()
Expand Down
1 change: 1 addition & 0 deletions tests/run-tests.bash
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ for version in ${emacs_versions[@]}; do
if ! $emacs_nix --init-directory=$init_dir --batch -l ert -l ../jtsx.el -l jtsx-tests.el \
--eval "(jtsx-install-treesit-language 'javascript)" \
--eval "(jtsx-install-treesit-language 'tsx)" \
--eval "(jtsx-install-treesit-language 'typescript)" \
-f ert-run-tests-batch-and-exit; then
printf "TESTS FAILURE ON '%s'.\n" $version
exit
Expand Down

0 comments on commit a411e85

Please sign in to comment.