((node-is "/") parent 0)
((parent-is "jsx_self_closing_element") parent ,js-indent-level))))
+(defvar js--treesit-cc-indent-rules
+ (let ((function-re (rx (or "function_declaration"
+ "method_definition"
+ "function")))
+ (if-statement-like (rx (or "if" "try" "while" "do")
+ "_statement"))
+ (else-clause-like (rx (or "else" "catch" "finally")
+ "_clause"))
+ (switch-case-re (rx "switch_" (or "case" "default"))))
+ `((javascript
+ ;; Function declaration.
+ ;; "{"
+ ((match "statement_block" ,function-re) parent defun-open)
+ ((n-p-gp "}" "statement_block" ,function-re) grand-parent defun-close)
+
+ ((and (node-is ,function-re) (parent-is "program"))
+ parent topmost-intro)
+ ((parent-is ,function-re) parent topmost-intro-cont)
+ ((and (n-p-gp nil "statement_block" ,function-re)
+ (match nil nil nil 0 0))
+ parent-bol defun-block-intro)
+
+ ;; Class
+ ((node-is "class_declaration") parent topmost-intro)
+ ((parent-is "class_declaration") parent topmost-intro-cont)
+ ((match "{" "class_declaration") parent class-open)
+ ((match "}" "class_declaration") parent class-close)
+
+ ((node-is "class_heritage") parent inher-intro)
+ ((parent-is "class_heritage") parent inher-cont)
+
+ ;; Javascript doesn't have class access keywords
+ ;; (`member-init-intro', `member-init-cont') I think?
+
+ ((match "{" "class_declaration") parent class-open)
+ ((match "}" "class_body") parent class-close)
+
+ ((parent-is "class_body") parent inclass)
+ ((parent-is "member_definition") parent topmost-intro-cont)
+
+ ((match "statement_block" "member_definition") parent defun-open)
+ ((n-p-gp "}" "statement_block" "member_definition")
+ grand-parent defun-close)
+
+ ;; Javascript doesn't have class parameters
+ ;; (`template-args-cont').
+
+ ;; Conditional.
+ ((match "statement_block" ,if-statement-like)
+ parent substatement-open)
+ ((match "statement_block" ,else-clause-like)
+ grand-parent substatement-open)
+ ;; Javascript doesn't have `substatement-label'.
+ ((node-is ,else-clause-like) parent else-clause)
+ ((parent-is ,else-clause-like) grand-parent substatement)
+ ((match "while" "do_statement") parent do-while-closure)
+ ((field-is "consequence") parent substatement)
+ ((field-is "condition") parent statement-cont)
+
+ ;; Switch.
+ ((node-is ,switch-case-re) grand-parent case-label)
+ ((match "statement_block" ,switch-case-re) parent statement-case-open)
+ ((match nil ,switch-case-re "body") parent statement-case-intro)
+ ((parent-is ,switch-case-re) first-sibling statement-case-cont)
+ ((node-is "switch_body") parent substatement-open)
+ ((match "}" "switch_body") grand-parent substatement-close)
+
+ ;; Brace list.
+ ((node-is "object") parent-bol brace-list-open)
+ ((match "}" "object") parent brace-list-close)
+ ;; ((match nil "object" nil 0 0) parent brace-list-intro)
+ ((match nil "object" nil 0 0) parent ,js-indent-level)
+ ((match nil "object" nil 1) first-sibling brace-list-entry)
+
+ ;; Pair.
+ ((match nil "pair" "value") parent ,js-indent-level)
+
+ ;; Javascript doesn't have extern.
+
+ ;; Parenthesis. These syntax symbols uses line-up functions,
+ ;; which don't work with tree-sitter, so we roll our own:
+ ;; `arglist-close', `arglist-intro', `arglist-cont',
+ ;; `arglist-cont-nonempty'.
+ ((parent-is "formal_parameters") first-sibling 1)
+ ((node-is "formal_parameters") parent statement-cont)
+
+ ;; Misc.
+ ((parent-is "function_declaration") parent func-decl-cont)
+ ((parent-is "string-fragment") grand-parent string)
+ ((parent-is "comment") grand-parent c)
+
+ ;; Fallback for top-level statements.
+ ((parent-is "program") parent 0)
+
+ ;; Fallback for blocks & statements.
+ ((parent-is "expression_statement") parent statement-cont)
+ ((match "}" "statement_block") parent-bol block-close)
+ ((match "statement_block" "statement_block") parent block-open)
+ ((match nil "statement_block" nil 0 0) parent-bol statement-block-intro)
+ ((parent-is "statement_block") prev-sibling statement)
+
+ ;; Template substitution.
+ ((match "}" "template_substitution") parent-bol block-close)
+ ((match nil "template_substitution" nil 0 0) parent-bol statement-block-intro)
+ ((parent-is "template_substitution") prev-sibling statement)
+
+ ;; JSX, copied from `js--treesit-indent-rules' (TODO).
+ ((parent-is "jsx_opening_element") parent ,js-indent-level)
+ ((node-is "jsx_closing_element") parent 0)
+ ((node-is "jsx_text") parent ,js-indent-level)
+ ((parent-is "jsx_element") parent ,js-indent-level)
+ ((node-is "/") parent 0)
+ ((parent-is "jsx_self_closing_element") parent ,js-indent-level)
+
+ (catch-all parent-bol statement-cont)))))
+
(defvar js--treesit-keywords
'("as" "async" "await" "break" "case" "catch" "class" "const" "continue"
"debugger" "default" "delete" "do" "else" "export" "extends" "finally"
((treesit-ready-p 'js-mode 'javascript)
(treesit-parser-create 'javascript)
;; Indent.
- (setq-local treesit-simple-indent-rules js--treesit-indent-rules)
+ (setq-local treesit-simple-indent-rules js--treesit-cc-indent-rules)
;; Navigation.
(setq-local treesit-defun-type-regexp
(rx (or "class_declaration"
node-index-min node-index-max)
`(lambda (node parent bol &rest _)
(and (or (null ,node-type)
- (equal (treesit-node-type node)
- ,node-type))
+ (string-match-p
+ ,node-type (or (treesit-node-type node) "")))
(or (null ,parent-type)
- (equal (treesit-node-type parent)
- ,parent-type))
+ (string-match-p
+ ,parent-type (treesit-node-type parent)))
(or (null ,node-field)
- (equal (treesit-node-field-name node)
- ,node-field))
+ (string-match-p
+ ,node-field
+ (or (treesit-node-field-name node) "")))
(or (null ,node-index-min)
(>= (treesit-node-index node t)
,node-index-min))
(or (null ,node-index-max)
(<= (treesit-node-index node t)
,node-index-max))))))
+ (n-p-gp . (lambda (node-t parent-t grand-parent-t)
+ `(lambda (node parent bol &rest _)
+ (and (or (null ,node-t)
+ (string-match-p
+ ,node-t (or (treesit-node-type node) "")))
+ (or (null ,parent-t)
+ (string-match-p
+ ,parent-t (treesit-node-type parent)))
+ (or (null ,grand-parent-t)
+ (string-match-p
+ ,grand-parent-t
+ (treesit-node-type
+ (treesit-node-parent parent))))))))
(no-node . (lambda (node parent bol &rest _) (null node)))
(parent-is . (lambda (type)
`(lambda (node parent bol &rest _)
- (equal ,type (treesit-node-type parent)))))
+ (string-match-p
+ ,type (treesit-node-type parent)))))
(node-is . (lambda (type)
`(lambda (node parent bol &rest _)
- (equal ,type (treesit-node-type node)))))
+ (string-match-p
+ ,type (or (treesit-node-type node) "")))))
+ (field-is . (lambda (name)
+ `(lambda (node parent bol &rest _)
+ (string-match-p
+ ,name (or (treesit-node-field-name node) "")))))
+ (catch-all . (lambda (&rest _) t))
(query . (lambda (pattern)
`(lambda (node parent bol &rest _)
finally return nil))))
(first-sibling . (lambda (node parent bol &rest _)
(treesit-node-start
- (treesit-node-child parent 0 t))))
-
+ (treesit-node-child parent 0))))
+ (nth-sibling . (lambda (n &optional named)
+ `(lambda (node parent bol &rest _)
+ (treesit-node-start
+ (treesit-node-child parent ,n ,named)))))
(parent . (lambda (node parent bol &rest _)
(treesit-node-start parent)))
+ (grand-parent . (lambda (node parent bol &rest _)
+ (treesit-node-start (treesit-node-parent parent))))
(parent-bol . (lambda (node parent bol &rest _)
(save-excursion
(goto-char (treesit-node-start parent))
(save-excursion
(goto-char bol)
(forward-line -1)
- (skip-chars-forward " \t")))))
+ (skip-chars-forward " \t"))))
+ (and . (lambda (&rest fns)
+ `(lambda (node parent bol &rest _)
+ (cl-reduce (lambda (a b) (and a b))
+ (mapcar (lambda (fn)
+ (funcall fn node parent bol))
+ ',fns)))))
+ (or . (lambda (&rest fns)
+ `(lambda (node parent bol &rest _)
+ (cl-reduce (lambda (a b) (or a b))
+ (mapcar (lambda (fn)
+ (funcall fn node parent bol))
+ ',fns)))))
+ (not . (lambda (fn)
+ `(lambda (node parent bol &rest _)
+ (debug)
+ (not (funcall ,fn node parent bol)))))
+ (list . (lambda (&rest fns)
+ `(lambda (node parent bol &rest _)
+ (mapcar (lambda (fn)
+ (funcall fn node parent bol))
+ ',fns)))))
"A list of presets.
These presets that can be used as MATHER and ANCHOR in
`treesit-simple-indent-rules'.
The first non-whitespace charater on the previous line.")
-(defun treesit--simple-apply (fn args)
- "Apply ARGS to FN.
+(defun treesit--simple-indent-eval (exp)
+ "Evaluate EXP.
-If FN is a key in `treesit-simple-indent-presets', use the
-corresponding value as the function."
+If EXPis an application and the function is a key in
+`treesit-simple-indent-presets', use the corresponding value as
+the function."
;; We don't want to match uncompiled lambdas, so make sure this cons
;; is not a function. We could move the condition functionp
;; forward, but better be explicit.
- (cond ((and (consp fn) (not (functionp fn)))
- (apply (treesit--simple-apply (car fn) (cdr fn))
- ;; We don't evaluate ARGS with `simple-apply', i.e.,
- ;; no composing, better keep it simple.
- args))
- ((and (symbolp fn)
- (alist-get fn treesit-simple-indent-presets))
- (apply (alist-get fn treesit-simple-indent-presets)
- args))
- ((functionp fn) (apply fn args))
- (t (error "Couldn't find the function corresponding to %s" fn))))
+ (cond ((and (consp exp) (not (functionp exp)))
+ (apply (treesit--simple-indent-eval (car exp))
+ (mapcar #'treesit--simple-indent-eval
+ (cdr exp))))
+ ;; Presets override functions, so this condition comes before
+ ;; `functionp'.
+ ((alist-get exp treesit-simple-indent-presets)
+ (alist-get exp treesit-simple-indent-presets))
+ ((functionp exp) exp)
+ ((symbolp exp)
+ (if (null exp)
+ exp
+ ;; Matchers only return lambdas, anchors only return
+ ;; integer, so we should never see a variable.
+ (error "Couldn't find the preset corresponding to %s" exp)))
+ (t exp)))
;; This variable might seem unnecessary: why split
;; `treesit-indent' and `treesit-simple-indent' into two
(indent-line-to col))
(indent-line-to col)))))))
+(declare-function c-calc-offset "cc-engine")
+
(defun treesit-simple-indent (node parent bol)
"Calculate indentation according to `treesit-simple-indent-rules'.
for pred = (nth 0 rule)
for anchor = (nth 1 rule)
for offset = (nth 2 rule)
- if (treesit--simple-apply
- pred (list node parent bol))
+ if (treesit--simple-indent-eval
+ (list pred node parent bol))
do (when treesit--indent-verbose
(message "Matched rule: %S" rule))
and
- return (cons (treesit--simple-apply
- anchor (list node parent bol))
- offset)))))
+ return
+ (let ((anchor-pos
+ (treesit--simple-indent-eval
+ (list anchor node parent bol))))
+ (cons anchor-pos
+ (if (integerp offset)
+ offset
+ (c-calc-offset (list offset anchor-pos)))))))))
(defun treesit-check-indent (mode)
"Check current buffer's indentation against a major mode MODE.