Skip to content

Commit

Permalink
WIP: Refactor tags file and some changes to workspace for xref
Browse files Browse the repository at this point in the history
  • Loading branch information
gmlarumbe committed Aug 17, 2023
1 parent 46cb4b4 commit 1c2fdb9
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 196 deletions.
15 changes: 8 additions & 7 deletions ts-mode/verilog-ts-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ Defaults to .v, .vh, .sv and .svh."

(defun verilog-ts--node-identifier-name (node)
"Return identifier name of NODE."
(cond ((string-match "class_constructor" (treesit-node-type node))
"new")
((string-match "class_method" (treesit-node-type node))
(or (treesit-node-text (treesit-search-subtree node "\\(function\\|task\\)_identifier") :no-prop)
"new"))
(t
(treesit-node-text (treesit-search-subtree node "simple_identifier") :no-prop))))
(when node
(cond ((string-match "class_constructor" (treesit-node-type node))
"new")
((string-match "class_method" (treesit-node-type node))
(or (treesit-node-text (treesit-search-subtree node "\\(function\\|task\\)_identifier") :no-prop)
"new"))
(t
(treesit-node-text (treesit-search-subtree node "simple_identifier") :no-prop)))))

(defun verilog-ts--highest-node-at-pos (pos)
"Return highest node in the hierarchy that starts at POS.
Expand Down
223 changes: 83 additions & 140 deletions verilog-ext-tags.el
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@

(require 'verilog-ext-nav)

;;;; Common
(defun verilog-ext-tags-table-add-entry (table tag type desc &optional file parent)
"Add entry for TAG in hash-table TABLE.
It is needed to provide TYPE, description DESC and FILE properties to add the
entry in the table.
Optional arg PARENT is the module where TAG is defined/instantiated for dot
completion.
If there is no entry in the table for TAG add one. Otherwise update the
existing one with current location properties."
(let ((tag-value (gethash tag table))
locs-plist loc-new parent-value parent-items)
(if (not tag-value)
;; Add tag with properties
(puthash tag `(:items nil :locs (,(verilog-ext-tags-tag-properties type desc file))) table)
;; Otherwise update existing tag properties
(setq locs-plist (plist-get tag-value :locs))
(setq loc-new (verilog-ext-tags-tag-properties type desc file))
(unless (member loc-new locs-plist)
(push loc-new locs-plist)
(plist-put tag-value :locs locs-plist)
(puthash tag `(:items ,(plist-get tag-value :items) :locs ,locs-plist) table)))
(when parent
(setq parent-value (or (gethash parent table)
(puthash parent (list :items nil :locs nil) table)))
(setq parent-items (plist-get parent-value :items))
(unless (member tag parent-items)
(plist-put parent-value :items (append parent-items `(,tag)))
(puthash parent parent-value table)))
table))

(defmacro verilog-ext-tags-table-push-tag (table tag type desc &optional file parent)
"Push TAG in hash table TABLE.
Expand All @@ -40,6 +72,21 @@ completion."
(declare (indent 0) (debug t))
`(setq ,table (verilog-ext-tags-table-add-entry ,table ,tag ,type ,desc ,file ,parent)))

(defun verilog-ext-tags-tag-properties (type desc &optional file)
"Return :locs properties for current tag.
These include tag TYPE and description DESC as well as FILE and current line."
`(:type ,type
:desc ,desc
:file ,(or file buffer-file-name)
:line ,(line-number-at-pos)))

(defun verilog-ext-tags-desc ()
"Return description for current TAG.
Meant to be used for `xref' backend."
(string-trim (buffer-substring-no-properties (line-beginning-position) (line-end-position))))


;;;; Builtin
(defmacro verilog-ext-tags-table-push-definitions (tag-type table &optional file start limit parent)
"Push definitions of TAG-TYPE inside hash table TABLE.
Expand All @@ -65,46 +112,9 @@ buffer."
(declare (indent 0) (debug t))
`(setq ,table (verilog-ext-tags-get-references ,table ,defs-table ,file ,start ,limit)))

(defun verilog-ext-tags-tag-properties (type desc &optional file)
"Return :locs properties for current tag.
These include tag TYPE and description DESC as well as FILE and current line."
`(:type ,type
:desc ,desc
:file ,(or file buffer-file-name)
:line ,(line-number-at-pos)))

(defun verilog-ext-tags-table-add-entry (table tag type desc &optional file parent)
"Add entry for TAG in hash-table TABLE.
It is needed to provide TYPE, description DESC and FILE properties to add the
entry in the table.
Optional arg PARENT is the module where TAG is defined/instantiated for dot
completion.
If there is no entry in the table for TAG add one. Otherwise update the
existing one with current location properties."
(let ((tag-value (gethash tag table))
locs-plist loc-new parent-value parent-items)
(if (not tag-value)
;; Add tag with properties
(puthash tag `(:items nil :locs (,(verilog-ext-tags-tag-properties type desc file))) table)
;; Otherwise update existing tag properties
(setq locs-plist (plist-get tag-value :locs))
(setq loc-new (verilog-ext-tags-tag-properties type desc file))
(unless (member loc-new locs-plist)
(push loc-new locs-plist)
(plist-put tag-value :locs locs-plist)
(puthash tag `(:items ,(plist-get tag-value :items) :locs ,locs-plist) table)))
(when parent
(setq parent-value (or (gethash parent table)
(puthash parent (list :items nil :locs nil) table)))
(setq parent-items (plist-get parent-value :items))
(unless (member tag parent-items)
(plist-put parent-value :items (append parent-items `(,tag)))
(puthash parent parent-value table)))
table))

;; TODO: Do not add instances as definitions. Just add items to its parent!
;; Add arg to `verilog-ext-tags-table-add-entry' to only add items in the second part of the function
;; and set it to non-nil in caller function if it's a "module|interface_instantiation"
(defun verilog-ext-tags-get-definitions (tag-type table &optional file start limit parent)
"Add definitions of TAG-TYPE to hash-table TABLE for FILE.
Expand Down Expand Up @@ -229,14 +239,9 @@ Limit search between START and LIMIT if provided."
;; Return updated table
table))

(defun verilog-ext-tags-desc ()
"Return description for current TAG.
Meant to be used for `xref' backend."
(string-trim (buffer-substring-no-properties (line-beginning-position) (line-end-position))))


;;; Tree-sitter
(defconst verilog-ext-tags-definitions-tree-sitter-re
;;;; Tree-sitter
(defconst verilog-ext-tags-definitions-ts-re
(regexp-opt
'("module_declaration"
"interface_declaration"
Expand All @@ -248,126 +253,64 @@ Meant to be used for `xref' backend."
"class_constructor_declaration"
"local_parameter_declaration"
"data_declaration"
"class_property"
;; INFO: These two below are only included add items to the defs table for capf
"module_instantiation"
"interface_instantiation"
)))

;; TODO: Instead of traversing it sequentially, do similarly to how it's done for imenu with the sparse tree
;; and use the parent argument accordingly. This way should be much easier and probably more efficient.
(defun verilog-ext-tags-table-push-definitions-tree-sitter (table &optional file)
""
(let ((nodes (verilog-ts-nodes-current-buffer verilog-ext-tags-definitions-tree-sitter-re)))
(dolist (node nodes)
(goto-char (treesit-node-start node))
(verilog-ext-tags-table-push-tag table
;; TODO: Might not work for some cases: e.g: @ test/files/common/uvm_component.svh:1498
;; TODO: Doesn't work either for external defined methods, fetches the class_qualifier instead of the
;; e.g function void uvm_component::do_end_tr (uvm_transaction tr, -> fetches uvm_component instead of do_end_tr
(verilog-ts--node-identifier-name node)
(treesit-node-type node)
(verilog-ext-tags-desc)
file
;; TODO: This last argument is the parent. It is used in the builtin recursive function
;; for dot completion to populate the :items property, with elements of modules and class items/methods
(verilog-ts--node-identifier-name (verilog-ts--node-has-parent-recursive "\\(\\(class\\|package\\)_declaration\\|\\(module\\|interface\\)_"))
;; (treesit-node-text (treesit-node-parent node) :no-prop)
))))

(defun verilog-ext-tags-table-push-references-tree-sitter (table &optional file)
""
(let ((nodes (verilog-ts-nodes-current-buffer "simple_identifier"))
tag type desc data)
(dolist (node nodes)
(goto-char (treesit-node-start node))
"interface_instantiation")))

(verilog-ext-tags-table-push-tag table
(treesit-node-text node :no-prop)
nil
(verilog-ext-tags-desc)
file)))
;; TODO: Filter references that already exist as definitions!
;; Same as in `verilog-ext-tags-get-references'
)


(defconst verilog-ext-tags-tree-sitter-re
(eval-when-compile
(regexp-opt
'("module_declaration"
"interface_declaration"
"program_declaration"
"package_declaration"
"class_declaration"
"function_declaration"
"task_declaration"
"class_constructor_declaration"
"class_property"
"module_instantiation"
"interface_instantiation"
"generate_region"))))

(setq verilog-ext-tags-tree-sitter-defs-table (make-hash-table :test #'equal))

(defun verilog-ext-tags-table-push-definitions-tree-sitter ()
"Imenu index builder function for `verilog-ts-mode'.
NODE is the root node of the subtree you want to build an index
of. If nil, use the root node of the whole parse tree.
Copied from Python's `python-imenu-treesit-create-index' and adapted
to SystemVerilog parser."
;; TODO: Do not add instances as definitions. Just add items to its parent!
;; Add arg to `verilog-ext-tags-table-add-entry' to only add items in the second part of the function
;; and set it to non-nil in caller function if it's a "module|interface_instantiation"
(defun verilog-ext-tags-table-push-definitions-ts (table &optional file)
"Push definitions found in FILE inside hash table TABLE using tree-sitter."
(let* ((node (treesit-buffer-root-node 'verilog))
(tree (treesit-induce-sparse-tree
node
verilog-ext-tags-tree-sitter-re
verilog-ext-tags-definitions-ts-re
nil 1000)))
(verilog-ext-tags-table-push-definitions-tree-sitter-1 tree nil)))

(defun verilog-ext-tags-table-push-definitions-tree-sitter-1 (node parent)
"Given a sparse tree, create an imenu alist.
NODE is the root node of the tree returned by
`treesit-induce-sparse-tree' (not a tree-sitter node, its car is
a tree-sitter node). Walk that tree and return an imenu alist.
Return a list of ENTRY where
(verilog-ext-tags-table-push-definitions-ts--recurse table tree nil file)))

ENTRY := (NAME . MARKER)
| (NAME . ((JUMP-LABEL . MARKER)
ENTRY
...)
(defun verilog-ext-tags-table-push-definitions-ts--recurse (table node parent &optional file)
"Push definitions found in FILE inside hash table TABLE using tree-sitter.
NAME is the function/class's name, JUMP-LABEL is like \"*function
definition*\".
Traverse the tree starting at NODE.
Copied from Python's `python--imenu-treesit-create-index-1' and adapted to
SystemVerilog parser."
PARENT is passed as an argument to build the :items prop list of TABLE."
(let* ((ts-node (car node))
(children (cdr node))
(subtrees (mapc (lambda (leaf)
(verilog-ext-tags-table-push-definitions-tree-sitter-1 leaf ts-node))
(verilog-ext-tags-table-push-definitions-ts--recurse table leaf ts-node file))
children))
(type (treesit-node-type ts-node)))
(cond
((null ts-node)
subtrees)
(t
(goto-char (treesit-node-start ts-node))
(setq verilog-ext-tags-tree-sitter-defs-table (verilog-ext-tags-table-add-entry
;; ,table ,tag ,type ,desc ,file ,parent))
;; (verilog-ext-tags-table-push-tag
verilog-ext-tags-tree-sitter-defs-table
(verilog-ext-tags-table-push-tag table
;; TODO: Might not work for some cases: e.g: @ test/files/common/uvm_component.svh:1498
;; TODO: Doesn't work either for external defined methods, fetches the class_qualifier instead of the
;; e.g function void uvm_component::do_end_tr (uvm_transaction tr, -> fetches uvm_component instead of do_end_tr
(verilog-ts--node-identifier-name ts-node)
type
(verilog-ext-tags-desc)
file
(verilog-ts--node-identifier-name parent))))))

(defun verilog-ext-tags-table-push-references-ts (table &optional file)
"Push references found in FILE inside hash table TABLE using tree-sitter."
(let ((nodes (verilog-ts-nodes-current-buffer "simple_identifier")))
(dolist (node nodes)
(goto-char (treesit-node-start node))
;; TODO: Filter references that already exist as definitions!
;; Same as in `verilog-ext-tags-get-references'
(verilog-ext-tags-table-push-tag table
(treesit-node-text node :no-prop)
nil
(verilog-ts--node-identifier-name parent)
;; file
))))))
(verilog-ext-tags-desc)
file))))



(provide 'verilog-ext-tags)

Expand Down
51 changes: 4 additions & 47 deletions verilog-ext-workspace.el
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ With current-prefix or VERBOSE, dump output log."
(cond (;; Tree-sitter
(eq verilog-ext-workspace-tags-backend 'tree-sitter)
(verilog-ts-mode)
(verilog-ext-tags-table-push-definitions-tree-sitter table file))
(verilog-ext-tags-table-push-definitions-ts table file))
(;; Builtin
(eq verilog-ext-workspace-tags-backend 'builtin)
(verilog-mode)
Expand Down Expand Up @@ -303,7 +303,7 @@ With current-prefix or VERBOSE, dump output log."
(cond (;; Tree-sitter
(eq verilog-ext-workspace-tags-backend 'tree-sitter)
(verilog-ts-mode)
(verilog-ext-tags-table-push-references-tree-sitter table file))
(verilog-ext-tags-table-push-references-ts table file))
(;; Builtin
(eq verilog-ext-workspace-tags-backend 'builtin)
(verilog-mode)
Expand All @@ -326,7 +326,8 @@ With current-prefix or VERBOSE, dump output log."
`(lambda ()
,(async-inject-variables verilog-ext-async-inject-variables-re)
(require 'verilog-ext)
(require 'verilog-ts-mode)
(when (eq verilog-ext-workspace-tags-backend 'tree-sitter)
(require 'verilog-ts-mode))
(verilog-ext-workspace-get-tags ,verbose))
(lambda (result)
(message "Finished collection tags!")
Expand Down Expand Up @@ -678,50 +679,6 @@ INFO: Enabling this feature will override the value of
(setq verilog-align-typedef-regexp verilog-ext-workspace-cache-typedefs))


;;;; Tree-sitter pending
(defvar verilog-ts-capf-table nil)
(defconst verilog-ts-async-inject-variables-re "\\`\\(load-path\\|buffer-file-name\\)")

;; TODO: Probably it is needed to update `verilog-ext-tags-get-definitions' and
;; `verilog-ext-tags-get-references' so that tables are populated via
;; tree-sitter instead of via built-in processing.
;;

(defun verilog-ts-capf-create-table ()
"Verilog tree-sitter create completion at point table synchronously."
(let (completions)
(dolist (file (directory-files-recursively (project-root (project-current)) "\\.[s]?v[h]?$"))
(with-temp-buffer
(message "Processing %s" file)
(insert-file-contents file)
(verilog-ts-mode)
(setq completions (append (verilog-ts-nodes-current-buffer "simple_identifier") completions))))
(delete-dups completions)
(setq verilog-ts-capf-table completions)))

(defun verilog-ts-capf-create-table-async ()
"Verilog tree-sitter create completion at point table asynchronously."
(message "Starting tag collection for %s" (project-root (project-current)))
(async-start
`(lambda ()
,(async-inject-variables verilog-ts-async-inject-variables-re)
(require 'verilog-mode)
(require 'verilog-ts-mode)
(verilog-ts-capf-create-table))
(lambda (result)
(message "Finished collection tags!")
(setq verilog-ts-capf-table result))))

(defun verilog-ts-completion-at-point ()
"Verilog tree-sitter completion at point.
Complete with identifiers of current project."
(interactive)
(let* ((bds (bounds-of-thing-at-point 'symbol))
(start (car bds))
(end (cdr bds)))
(list start end verilog-ts-capf-table . nil)))



(provide 'verilog-ext-workspace)

Expand Down
4 changes: 2 additions & 2 deletions verilog-ext-xref.el
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ instead of to `verilog-mode', since the first one is loaded later and overwrites
the hook value. Otherwise, hooks are not ran in a specific order, and rely on
the priority argument."
(if disable
(remove-hook 'verilog-ext-mode-hook #'verilog-ext-xref-backend-enable :local)
(add-hook 'verilog-ext-mode-hook #'verilog-ext-xref-backend-enable nil :local)))
(remove-hook 'verilog-ext-mode-hook #'verilog-ext-xref-backend-enable)
(add-hook 'verilog-ext-mode-hook #'verilog-ext-xref-backend-enable)))



Expand Down

0 comments on commit 1c2fdb9

Please sign in to comment.