It will treat `#if false {` blocks as comment-face for clarity:
Please let me know if you see anything wrong (with a bit of a repro code) and I’ll do my best to fix it.
A Major mode for Emacs >= 29.1 powered by tree-sitter for editing Jai files.
Emacs >= 29.1 compiled with tree-sitter.
The tree-sitter grammar can be found at https://github.com/constantitus/tree-sitter-jai
To install it you can add this line to your `treesit-language-source-alist`:
(defvar treesit-language-source-alist
'(...
(jai "https://github.com/constantitus/tree-sitter-jai")
...))
Afterwards run M-x treesit-install-language-grammar RET jai RET.
Clone this repo
git clone https://github.com/cpoile/jai-ts-mode.git
and add this to your init.el file:
(load-file "/path/to/jai-ts-mode.el")
You can also use use-package to install this package, simply add this to your init.el file (you can skip the usage step below with this):
(use-package jai-ts-mode
:ensure (:host github :repo "cpoile/jai-ts-mode")
:mode "\\.jai\\'")
Add this to your configuration:
(add-to-list 'auto-mode-alist '("\\.jai\\'" . jai-ts-mode))
Here are some conveniences that you might find useful.
;; For Doom Emacs:
(map! :map jai-ts-mode-map
"C-M-a" #'jai-ts-mode--prev-defun
"C-M-e" #'jai-ts-mode--next-defun
"C-M-l" #'align-regexp
"C-M-S-l" #'jai-ts-mode--align-struct)
;; Or regular Emacs:
(define-key jai-ts-mode-map (kbd "C-M-a") 'jai-ts-mode--prev-defun)
(define-key jai-ts-mode-map (kbd "C-M-e") 'jai-ts-mode--next-defun)
(define-key jai-ts-mode-map (kbd "C-M-l") 'align-regexp)
(define-key jai-ts-mode-map (kbd "C-M-S-l") 'jai-ts-mode--align-struct)
Dumb-jump seems to do almost everything I need an LSP for.
Setup steps:
- Install https://github.com/jacktasia/dumb-jump
- Add to your init.el:
(with-eval-after-load dumb-jump (setq dumb-jump-prefer-searcher 'rg dumb-jump-force-searcher 'rg dumb-jump-rg-search-args "--pcre2 --type-add \"jai:*.jai\"") (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
- put a `.dumbjump` file in your project root containing the paths to directories you want to include, e.g.:
+~/Jai/modules
- The following are convenience functions; use if you’d like. When I press `M-.` I want it to jump to the definition of the symbol (not references), but if I am already at the definition I want it to show references.
Sometimes it shows references before the definition, so I’m still working on it. Maybe it’s because `+lookup/references` and `+lookup/definition` sometimes don’t work? /shrug
(defun treesit-enabled-p () "Checks if the current buffer has a treesit parser." (and (fboundp 'treesit-available-p) (treesit-available-p) (treesit-language-at (point)))) ;; Add language node types that are considered declarations: (setq declaration-node-types '("procedure_declaration" "variable_declaration" "struct_declaration" "function_declaration")) (defun string-contains-any-substring-p (haystack targets) "Check if HAYSTACK string contains any string from the TARGETS list. HAYSTACK is the string to search within. TARGETS is a list of strings to search for. Search is case-sensitive by default (respects `case-fold-search`). Target strings are treated literally (regex metacharacters are quoted). Returns t if any string in TARGETS is found as a substring within HAYSTACK, nil otherwise." (seq-some (lambda (target-string) (string-match-p (regexp-quote target-string) haystack)) targets)) (defun cp/check-inspect-name-against-declarations () "Calls treesit-inspect-node-at-point and then checks if the internal variable treesit--inspect-name exactly matches any type in a predefined list." (interactive) (when (treesit-enabled-p) (call-interactively #'treesit-inspect-node-at-point) (if (boundp 'treesit--inspect-name) (string-contains-any-substring-p treesit--inspect-name declaration-node-types)))) (defun cp/go-to-def-or-ref () (interactive) (let ((cur (line-number-at-pos)) (cur-pt (point))) (if (cp/check-inspect-name-against-declarations) (call-interactively '+lookup/references) (call-interactively '+lookup/definition))))
- Then I add that to my prog-mode-map:
;; For Doom Emacs: (map! :map prog-mode-map "M-." #'cp/go-to-def-or-ref) ;; Or regular Emacs: (define-key prog-mode-map (kbd "M-.") 'cp/go-to-def-or-ref)
https://github.com/alphapapa/topsy.el
(add-hook 'prog-mode-hook #'topsy-mode)
(defun topsy--jai-beginning-of-defun ()
"Return the line moved to by `jai-ts-mode--prev-defun'."
(when (> (window-start) 1)
(save-excursion
(goto-char (window-start))
(jai-ts-mode--prev-defun)
(font-lock-ensure (point) (pos-eol))
(buffer-substring (point) (pos-eol)))))
(add-to-list 'topsy-mode-functions '(jai-ts-mode . topsy--jai-beginning-of-defun))
I often want to rename a variable, but only within the current function. This is tedious, but with multiple cursors you can do it with some special logic. To set this up:
- Install https://github.com/magnars/multiple-cursors.el
- Put this in your `init.el`:
(defun jai-narrow-to-defun () "Narrow to the function/method definition at point using treesit." (let ((node (treesit-node-at (point)))) (when-let ((defun-node (treesit-parent-until node (lambda (n) (member (treesit-node-type n) jai-ts-mode--defun-function-type-list))))) (narrow-to-region (treesit-node-start defun-node) (treesit-node-end defun-node))))) (defun cp/mark-all-symbols-like-this-in-defun () (interactive) (mc--select-thing-at-point-or-bark 'symbol) (if (eq major-mode 'jai-ts-mode) (save-restriction (widen) (jai-ts-mode--narrow-to-defun) (mc/mark-all-symbols-like-this)) (save-restriction (widen) (narrow-to-defun) (mc/mark-all-symbols-like-this)))) (global-set-key (kbd "C-c C-.") 'cp/mark-all-symbols-like-this-in-defun)
- [-] Syntax Highlighting [2/3]
- [X] Get something working
- [X] Make things good enough
- [ ] unknown unknowns
- [-] Indentation [2/3]
- [X] Get something working
- [X] Make sure it’s good enough
- [ ] unknown unknowns
- [X] Imenu
- [X] Forward/Backward defun
- [X] Align struct fields with a keybinding