Finish xref backend
This commit is contained in:
@@ -108,6 +108,7 @@ command.")
|
||||
(special_variable_name) @font-lock-variable-use-face
|
||||
(expansion_with_modifier (simple_variable_name)
|
||||
@font-lock-variable-use-face)
|
||||
(expansion_pattern (simple_variable_name) @font-lock-variable-use-face)
|
||||
(expansion_substring (simple_variable_name) @font-lock-variable-use-face)
|
||||
(variable_ref (simple_variable_name) @font-lock-variable-use-face)
|
||||
;; variables in math expansions
|
||||
@@ -261,6 +262,13 @@ Return non-nil if NODE is not an anonymous function."
|
||||
name: ([(simple_variable_name)
|
||||
(special_variable_name)]
|
||||
@var))
|
||||
(variable_ref ([(simple_variable_name)
|
||||
(special_variable_name)]
|
||||
@var))
|
||||
(expansion_pattern
|
||||
name: ([(simple_variable_name)
|
||||
(special_variable_name)]
|
||||
@var))
|
||||
((word) @func
|
||||
(:pred zsh-ts-mode--not-let-predicate @func))))
|
||||
"Query for `zsh-ts-mode' `xref-backend-identifier-at-point'.")
|
||||
@@ -284,23 +292,48 @@ Return non-nil if NODE is not an anonymous function."
|
||||
(when-let* ((func (alist-get 'func nodes)))
|
||||
(propertize (treesit-node-text func t) 'type 'function))))))
|
||||
|
||||
(defun zsh-ts-mode--treesit-node-location (node)
|
||||
"Return an xref location pointing to NODE."
|
||||
(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql zsh-ts)))
|
||||
"Return nil."
|
||||
nil)
|
||||
|
||||
(cl-defmethod xref-backend-identifier-completion-ignore-case
|
||||
((_backend (eql zsh-ts)))
|
||||
"Return `completion-ignore-case'."
|
||||
completion-ignore-case)
|
||||
|
||||
(defun zsh-ts-mode--treesit-node-summary (node)
|
||||
"Return the contents of the line of NODE."
|
||||
(save-excursion
|
||||
(goto-char (treesit-node-start node))
|
||||
(make-xref-file-location :file (buffer-file-name)
|
||||
:line (line-number-at-pos)
|
||||
:column (- (point) (pos-bol)))))
|
||||
(buffer-substring (pos-bol) (pos-eol))))
|
||||
|
||||
(defun zsh-ts-mode--make-function-definition-xref (identifier node)
|
||||
"Make an xref object pointing to IDENTIFIER.
|
||||
NODE is IDENTIFIER's tree-sitter node."
|
||||
(xref-make (format "%s %s()"
|
||||
(propertize "function"
|
||||
'face 'font-lock-keyword-face)
|
||||
(propertize identifier
|
||||
'face 'font-lock-function-name-face))
|
||||
(zsh-ts-mode--treesit-node-location node)))
|
||||
(cl-defstruct zsh-ts-mode--location
|
||||
"Xref locaton object for tree-sitter nodes."
|
||||
node)
|
||||
|
||||
(cl-defmethod xref-location-line ((location zsh-ts-mode--location))
|
||||
"Return the line number of LOCATION."
|
||||
(line-number-at-pos (treesit-node-start
|
||||
(zsh-ts-mode--location-node location))))
|
||||
|
||||
(cl-defmethod xref-location-group ((location zsh-ts-mode--location))
|
||||
"Return the file name of the buffer of LOCATION."
|
||||
(with-slots (node) location
|
||||
(or (buffer-file-name (treesit-node-buffer node))
|
||||
"")))
|
||||
|
||||
(cl-defmethod xref-location-marker ((location zsh-ts-mode--location))
|
||||
"Return a marker for LOCATION."
|
||||
(let ((node (zsh-ts-mode--location-node location))
|
||||
(marker (make-marker)))
|
||||
(set-marker marker (treesit-node-start node)
|
||||
(treesit-node-buffer node))
|
||||
marker))
|
||||
|
||||
(defun zsh-ts-mode--make-xref-for-node (node)
|
||||
"Create an xref for a tree-sitter node NODE."
|
||||
(xref-make (zsh-ts-mode--treesit-node-summary node)
|
||||
(make-zsh-ts-mode--location :node node)))
|
||||
|
||||
(defvar zsh-ts-mode--xref-function-definitions-query
|
||||
(treesit-query-compile 'zsh '((function_definition
|
||||
@@ -314,11 +347,7 @@ NODE is IDENTIFIER's tree-sitter node."
|
||||
zsh-ts-mode--xref-function-definitions-query
|
||||
nil nil t)
|
||||
when (equal identifier (treesit-node-text node))
|
||||
collect (zsh-ts-mode--make-function-definition-xref identifier node)
|
||||
into out
|
||||
finally return (if (length= out 1)
|
||||
(car out)
|
||||
out)))
|
||||
collect (zsh-ts-mode--make-xref-for-node node)))
|
||||
|
||||
(defvar zsh-ts-mode--xref-variable-definitions-query
|
||||
(treesit-query-compile
|
||||
@@ -331,58 +360,6 @@ NODE is IDENTIFIER's tree-sitter node."
|
||||
@let-arg)))))
|
||||
"Variable query for `zsh-ts-mode--xref-variable-definitions'.")
|
||||
|
||||
(defvar zsh-ts-mode--non-let-declaration-type-query
|
||||
(treesit-query-compile 'zsh '((declaration_command :anchor _ @name)))
|
||||
"Helper query for `zsh-ts-mode--get-non-let-assignment-type'.")
|
||||
|
||||
(defun zsh-ts-mode--get-assignment-parent (node)
|
||||
"Get the node that is a parent of NODE that is a variable assignment.
|
||||
This only looks up two levels."
|
||||
(cond ((equal (treesit-node-type
|
||||
(treesit-node-parent node))
|
||||
"declaration_command")
|
||||
(treesit-node-parent node))
|
||||
((equal (treesit-node-type
|
||||
(treesit-node-parent (treesit-node-parent node)))
|
||||
"declaration_command")
|
||||
(treesit-node-parent (treesit-node-parent node)))))
|
||||
|
||||
(defun zsh-ts-mode--get-non-let-assignment-type (node)
|
||||
"Return the assignment command for NODE."
|
||||
(when-let* ((type-node (zsh-ts-mode--get-assignment-parent node)))
|
||||
(propertize (treesit-node-text
|
||||
(car (treesit-query-capture
|
||||
type-node
|
||||
zsh-ts-mode--non-let-declaration-type-query
|
||||
nil nil t))
|
||||
t)
|
||||
'face 'font-lock-keyword-face)))
|
||||
|
||||
(defun zsh-ts-mode--make-xref-for-non-let-assignment (node)
|
||||
"Create an xref for an \"other\" assignment for NODE.
|
||||
An \"other\" assignment is an assignment using local, typeset, etc."
|
||||
(let ((type (zsh-ts-mode--get-non-let-assignment-type node)))
|
||||
(xref-make (format "%s%s" (if type (concat type " ") "")
|
||||
(propertize (treesit-node-text node t)
|
||||
'face 'font-lock-variable-name-face))
|
||||
(zsh-ts-mode--treesit-node-location node))))
|
||||
|
||||
(defun zsh-ts-mode--make-xref-for-let-assignment (node)
|
||||
"Create an xref node for a list assingment.
|
||||
NODE is the argument to let (possibly with a = in it)."
|
||||
(save-excursion
|
||||
(goto-char (treesit-node-start node))
|
||||
(xref-make
|
||||
(format "%s %s" (propertize "let" 'face 'font-lock-keyword-face)
|
||||
(propertize (buffer-substring-no-properties
|
||||
(treesit-node-start node)
|
||||
(if (re-search-forward (rx (or "=" "["))
|
||||
(treesit-node-end node)
|
||||
t)
|
||||
(1- (point))
|
||||
(treesit-node-end node)))))
|
||||
(zsh-ts-mode--treesit-node-location node))))
|
||||
|
||||
(defun zsh-ts-mode--xref-variable-definitions (identifier)
|
||||
"Find all definitions of variable IDENTIFIER in the current buffer."
|
||||
(let (out)
|
||||
@@ -393,45 +370,96 @@ NODE is the argument to let (possibly with a = in it)."
|
||||
(cl-case tag
|
||||
(non-let
|
||||
(when (equal (treesit-node-text node) identifier)
|
||||
(push (zsh-ts-mode--make-xref-for-non-let-assignment node) out)))
|
||||
(push (zsh-ts-mode--make-xref-for-node node) out)))
|
||||
(let-arg
|
||||
(when (string-match-p (rx bos (literal identifier) (or eos "="))
|
||||
(treesit-node-text node))
|
||||
(push (zsh-ts-mode--make-xref-for-let-assignment node) out))))))
|
||||
(if (length= out 1) (car out) out)))
|
||||
(push (zsh-ts-mode--make-xref-for-node node) out))))))
|
||||
out))
|
||||
|
||||
(cl-defmethod xref-backend-definitions ((_backend (eql zsh-ts)) identifier)
|
||||
"`zsh-ts-mode' implementation for finding xref definitions for IDENTIFIER."
|
||||
(cl-case (get-text-property 0 'type identifier)
|
||||
(function (zsh-ts-mode--xref-function-definitions identifier))
|
||||
(variable (zsh-ts-mode--xref-variable-definitions identifier))))
|
||||
(when identifier
|
||||
(let ((type (get-text-property 0 'type identifier)))
|
||||
(append
|
||||
(when (memq type '(nil function))
|
||||
(zsh-ts-mode--xref-function-definitions identifier))
|
||||
(when (memq type '(nil variable))
|
||||
(zsh-ts-mode--xref-variable-definitions identifier))))))
|
||||
|
||||
(defvar zsh-ts-mode--function-references-query
|
||||
(treesit-query-compile
|
||||
'zsh '((command name: (command_name (word) @name))))
|
||||
"Query used by `zsh-ts-mode--xref-function-references'.")
|
||||
|
||||
(defun zsh-ts-mode--xref-function-references (identifier)
|
||||
"Find all references for the function IDENTIFIER in the current buffer."
|
||||
(let ((nodes (treesit-query-capture
|
||||
(treesit-buffer-root-node)
|
||||
`((command name: (command_name (word) @name)))
|
||||
zsh-ts-mode--function-references-query
|
||||
nil nil t))
|
||||
(ident-copy (copy-sequence identifier)))
|
||||
(set-text-properties 0 (length ident-copy) nil ident-copy)
|
||||
(ident-copy (copy-sequence identifier))
|
||||
(quote-ident (regexp-quote identifier)))
|
||||
(set-text-properties 0 (length ident-copy) () ident-copy)
|
||||
(cl-loop for node in nodes
|
||||
collect (xref-make
|
||||
(propertize ident-copy 'face
|
||||
'font-lock-function-name-face)
|
||||
(zsh-ts-mode--treesit-node-location node)))))
|
||||
when (save-excursion
|
||||
(goto-char (treesit-node-start node))
|
||||
(looking-at-p quote-ident))
|
||||
collect (zsh-ts-mode--make-xref-for-node node))))
|
||||
|
||||
(defvar zsh-ts-mode--variable-references-query
|
||||
(treesit-query-compile
|
||||
'zsh '(([(simple_variable_name) (special_variable_name)] @var)))
|
||||
"Query used by `zsh-ts-mode--xref-variable-references'.")
|
||||
|
||||
(defun zsh-ts-mode--xref-variable-references (identifier)
|
||||
"Find all references for the variable IDENTIFIER in the current buffer."
|
||||
())
|
||||
(let ((ident-copy (copy-sequence identifier))
|
||||
(quote-ident (regexp-quote identifier)))
|
||||
(set-text-properties 0 (length ident-copy) () ident-copy)
|
||||
(mapcan
|
||||
#'(lambda (node)
|
||||
(when (save-excursion
|
||||
(goto-char (treesit-node-start node))
|
||||
(looking-at-p quote-ident))
|
||||
(list (zsh-ts-mode--make-xref-for-node node))))
|
||||
(treesit-query-capture (treesit-buffer-root-node)
|
||||
zsh-ts-mode--variable-references-query
|
||||
nil nil t))))
|
||||
|
||||
(cl-defmethod xref-backend-references ((_backend (eql zsh-ts)) identifier)
|
||||
"`zsh-ts-mode' implementation for finding xref references for IDENTIFIER."
|
||||
(let ((type (get-text-property 0 'type identifier)))
|
||||
(append
|
||||
;; definitions are references
|
||||
(xref-backend-definitions 'zsh-ts identifier)
|
||||
(cl-case (get-text-property 0 'type identifier)
|
||||
(function (zsh-ts-mode--xref-function-references identifier))
|
||||
(variable (zsh-ts-mode--xref-variable-references identifier)))))
|
||||
(ensure-list (xref-backend-definitions 'zsh-ts identifier))
|
||||
(when (memq type '(nil function))
|
||||
(zsh-ts-mode--xref-function-references identifier))
|
||||
(when (memq type '(nil variable))
|
||||
(zsh-ts-mode--xref-variable-references identifier)))))
|
||||
|
||||
(defvar zsh-ts-mode--xref-apropos-query
|
||||
(treesit-query-compile
|
||||
'zsh '(((simple_variable_name) @ident)
|
||||
((special_variable_name) @ident)
|
||||
((variable_name) @ident)
|
||||
(command name: (command_name ( (word) @cname (:equal @cname "let")))
|
||||
argument: (word) @ident)
|
||||
(declaration_command argument: (word) @ident)
|
||||
((variable_name) @ident)
|
||||
(function_definition name: (word) @ident)))
|
||||
"Query used by `xref-backend-apropos'.")
|
||||
|
||||
(cl-defmethod xref-backend-apropos ((_backend (eql zsh-ts)) pattern)
|
||||
"Search though the buffer for PATTERN."
|
||||
(let (out)
|
||||
(dolist (ent (treesit-query-capture (treesit-buffer-root-node)
|
||||
zsh-ts-mode--xref-apropos-query))
|
||||
(cl-destructuring-bind (type . node) ent
|
||||
(when (and (eq type 'ident)
|
||||
(string-match-p pattern (treesit-node-text node)))
|
||||
(push (zsh-ts-mode--make-xref-for-node node) out))))
|
||||
out))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode zsh-ts-mode sh-base-mode "Zsh"
|
||||
@@ -457,6 +485,7 @@ not written in Zsh."
|
||||
(setq-local treesit-simple-imenu-settings zsh-ts-mode--simple-imenu-settings
|
||||
treesit-defun-name-function #'zsh-ts-mode--defun-name
|
||||
imenu-create-index-function #'treesit-simple-imenu)
|
||||
(add-hook 'xref-backend-functions #'zsh-ts-mode--xref-backend nil t)
|
||||
(treesit-major-mode-setup)))
|
||||
|
||||
(provide 'zsh-ts-mode)
|
||||
|
||||
Reference in New Issue
Block a user