diff --git a/test-hdl b/test-hdl index 927207b..d9857a2 160000 --- a/test-hdl +++ b/test-hdl @@ -1 +1 @@ -Subproject commit 927207b72caab510cf1a3cc441fcfb2b08133cce +Subproject commit d9857a244428d2f4ed64187dbd7647c42e243f1b diff --git a/verilog-ext-capf.el b/verilog-ext-capf.el index 1179495..e2a084e 100644 --- a/verilog-ext-capf.el +++ b/verilog-ext-capf.el @@ -24,7 +24,7 @@ ;;; Code: -(require 'verilog-ext-utils) +(require 'verilog-ext-tags) ;; Fetched from IEEE 1800-2012 ;; https://ece.uah.edu/~gaede/cpe526/2012%20System%20Verilog%20Language%20Reference%20Manual.pdf @@ -278,14 +278,20 @@ Return value is a cons with (start . end) bounds." (when (and start end) (cons start end)))) -(defun verilog-ext-capf-annotation-function (cand defs-table inst-table) +(defun verilog-ext-capf-annotation-function (cand) "Completion annotation function for candidate CAND. -Get candidate type from DEFS-TABLE, or if not found, from INST-TABLE." - (let* ((entry (or (and defs-table (gethash cand defs-table)) +Get candidate type from `verilog-ext-tags-defs-table' or if not found, from +`verilog-ext-tags-inst-table'. + +See available types in `verilog-ext-tags-definitions-ts-re'." + (let* ((proj (verilog-ext-buffer-proj)) + (defs-table (alist-get proj verilog-ext-tags-defs-table nil nil #'string=)) + (inst-table (alist-get proj verilog-ext-tags-inst-table nil nil #'string=)) + (entry (or (and defs-table (gethash cand defs-table)) (and inst-table (gethash cand inst-table)))) (locs (plist-get entry :locs)) - (type (plist-get (car locs) :type))) ; TODO: Getting the type of the first appearance + (type (plist-get (car locs) :type))) ; INFO: Getting the type of the first appearance (cond (;; Type type (pcase type @@ -310,15 +316,20 @@ Get candidate type from DEFS-TABLE, or if not found, from INST-TABLE." (t ;; Default nil)))) -(cl-defun verilog-ext-capf (&key defs-table inst-table refs-table annotation-fn) - "Complete with identifiers present in DEFS-TABLE, INST-TABLE and REFS-TABLE. +(defun verilog-ext-capf () + "Complete with identifiers present in various hash tables. -Show annotations using function ANNOTATION-FN. +Tables used: `verilog-ext-tags-defs-table', `verilog-ext-tags-inst-table' and +`verilog-ext-tags-refs-table'. -Verilog-ext `completion-at-point' function to be called by a wrapper function in -the workspace." +Show annotations using function `verilog-ext-capf-annotation-function'." (interactive) - (let (bounds start end completions) + (let* ((proj (verilog-ext-buffer-proj)) + (defs-table (alist-get proj verilog-ext-tags-defs-table nil 'remove #'string=)) + (refs-table (alist-get proj verilog-ext-tags-refs-table nil 'remove #'string=)) + (inst-table (alist-get proj verilog-ext-tags-inst-table nil 'remove #'string=)) + (annotation-fn #'verilog-ext-capf-annotation-function) + bounds start end completions) (cond (;; Dot completion for object methods/attributes and hierarchical references (setq bounds (verilog-ext-capf--dot-completion-bounds)) (let (table-entry-value block-type) @@ -364,6 +375,12 @@ the workspace." :annotation-function annotation-fn :company-docsig #'identity))) +(defun verilog-ext-capf-set (&optional disable) + "Enable or DISABLE builtin capf function." + (if disable + (remove-hook 'completion-at-point-functions #'verilog-ext-capf :local) + (add-hook 'completion-at-point-functions #'verilog-ext-capf nil :local))) + (provide 'verilog-ext-capf) diff --git a/verilog-ext-compile.el b/verilog-ext-compile.el index 7506f7a..c30870f 100644 --- a/verilog-ext-compile.el +++ b/verilog-ext-compile.el @@ -38,13 +38,14 @@ ;;; Code: (require 'verilog-mode) +(require 'verilog-ext-template) +(require 'make-mode) + (defgroup verilog-ext-compile nil "Verilog-ext compilation." :group 'verilog-ext) -(defconst verilog-ext-compile-filename-re "[a-zA-Z0-9-_\\.\\/]+") - (defconst verilog-ext-compile-msg-code-face 'verilog-ext-compile-msg-code-face) (defface verilog-ext-compile-msg-code-face '((t (:inherit font-lock-comment-face))) @@ -57,6 +58,62 @@ "Face for compilation binaries." :group 'verilog-ext-compile) + +;;;; Preprocess +(defun verilog-ext-preprocess () + "Preprocess current file. +Choose among different available programs and update `verilog-preprocessor' +variable." + (interactive) + (let ((tools-available (seq-filter (lambda (bin) + (executable-find bin)) + '("verilator" "iverilog" "vppreproc")))) + (pcase (completing-read "Select tool: " tools-available) + ;; Verilator + ("verilator" (setq verilog-preprocessor "verilator -E __FLAGS__ __FILE__")) + ;; Verilog-Perl + ("vppreproc" (setq verilog-preprocessor "vppreproc __FLAGS__ __FILE__")) + ;; Icarus Verilog: `iverilog' command syntax requires writing to an output file (defaults to a.out). + ("iverilog" (let* ((filename-sans-ext (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) + (iver-out-file (concat (temporary-file-directory) filename-sans-ext "_pp_iver.sv"))) + (setq verilog-preprocessor (concat "iverilog -E -o" iver-out-file " __FILE__ && " + "echo \"\" && " ; Add blank line between run command and first preprocessed line + "cat " iver-out-file))))) + (verilog-preprocess) + (pop-to-buffer "*Verilog-Preprocessed*"))) + + +;;;; Makefile +(defun verilog-ext-makefile-create () + "Create Iverilog/Verilator Yasnippet based Makefile. +Create it only if in a project and the Makefile does not already exist." + (interactive) + (let ((project-root (verilog-ext-buffer-proj-root)) + file) + (if project-root + (if (file-exists-p (setq file (file-name-concat project-root "Makefile"))) + (error "File %s already exists!" file) + (find-file file) + (verilog-ext-template-insert-yasnippet "verilog")) + (error "Not in a project!")))) + +(defun verilog-ext-makefile-compile () + "Prompt to available Makefile targets and compile. +Compiles them with various verilog regexps." + (interactive) + (let ((makefile (file-name-concat (verilog-ext-buffer-proj-root) "Makefile")) + (makefile-need-target-pickup t) ; Force refresh of makefile targets + target cmd) + (unless (file-exists-p makefile) + (error "%s does not exist!" makefile)) + (with-temp-buffer + (insert-file-contents makefile) + (makefile-pickup-targets) + (setq target (completing-read "Target: " makefile-target-table))) + (setq cmd (concat "cd " (verilog-ext-buffer-proj-root) " && make " target)) + (compile cmd))) + + (defmacro verilog-ext-compile-define-mode (name &rest args) "Macro to define a compilation derived mode for a Verilog error regexp. @@ -97,6 +154,8 @@ ARGS is a property list." ;;;; Compilation-re +(defconst verilog-ext-compile-filename-re "[a-zA-Z0-9-_\\.\\/]+") + (defconst verilog-ext-compile-verilator-re `((verilator-error ,(concat "^%\\(?1:Error: Internal Error\\): \\(?2:" verilog-ext-compile-filename-re "\\):\\(?3:[0-9]+\\):\\(?4:[0-9]+\\)") 2 3 4 2 nil (1 compilation-error-face)) (verilator-error2 ,(concat "^%\\(?1:Error\\): \\(?2:" verilog-ext-compile-filename-re "\\):\\(?3:[0-9]+\\):\\(?4:[0-9]+\\): ") 2 3 4 2 nil (1 compilation-error-face)) @@ -223,29 +282,44 @@ ARGS is a property list." :comp-mode verilog-ext-compile-surelog-mode) -;;;; Other compilation commands -(defun verilog-ext-preprocess () - "Preprocess current file. -Choose among different available programs and update `verilog-preprocessor' -variable." - (interactive) - (let ((tools-available (seq-filter (lambda (bin) - (executable-find bin)) - '("verilator" "iverilog" "vppreproc")))) - (pcase (completing-read "Select tool: " tools-available) - ;; Verilator - ("verilator" (setq verilog-preprocessor "verilator -E __FLAGS__ __FILE__")) - ;; Verilog-Perl - ("vppreproc" (setq verilog-preprocessor "vppreproc __FLAGS__ __FILE__")) - ;; Icarus Verilog: `iverilog' command syntax requires writing to an output file (defaults to a.out). - ("iverilog" (let* ((filename-sans-ext (file-name-sans-extension (file-name-nondirectory (buffer-file-name)))) - (iver-out-file (concat (temporary-file-directory) filename-sans-ext "_pp_iver.sv"))) - (setq verilog-preprocessor (concat "iverilog -E -o" iver-out-file " __FILE__ && " - "echo \"\" && " ; Add blank line between run command and first preprocessed line - "cat " iver-out-file))))) - (verilog-preprocess) - (pop-to-buffer "*Verilog-Preprocessed*"))) +(defun verilog-ext-compile () + "Compile using command of :compile-cmd of `verilog-ext-project-alist' project. + +Depending on the command, different syntax highlight will be applied. +The function will detect any of the supported compilation error parsers and will +set the appropriate mode." + (interactive) + (let* ((proj (verilog-ext-buffer-proj)) + (compile-cmd (verilog-ext-proj-compile-cmd proj)) + (cmd-list (if (not compile-cmd) + (error "You first need to set `:compile-cmd' for current project [%s] in `verilog-ext-project-alist'" proj) + (split-string (verilog-ext-proj-compile-cmd)))) + (cmd-args (cdr cmd-list)) + (cmd-bin (car cmd-list)) + (fn (pcase cmd-bin + ("verilator" #'verilog-ext-compile-verilator) + ("iverilog" #'verilog-ext-compile-iverilog) + ("slang" #'verilog-ext-compile-slang) + ("svlint" #'verilog-ext-compile-svlint) + ("surelog" #'verilog-ext-compile-surelog) + ("verible-verilog-lint" #'verilog-ext-compile-verible) + (_ #'compile))) + (cmd-processed (cond (;; For svlint, make sure the -1 arg is present + (string= cmd-bin "svlint") + (if (member "-1" cmd-args) + compile-cmd + (mapconcat #'identity `(,cmd-bin "-1" ,@cmd-args) " "))) + ;; For slang make sure that there is no colored output + ((string= cmd-bin "slang") + (if (member "--color-diagnostics=false" cmd-args) + compile-cmd + (mapconcat #'identity `(,cmd-bin "--color-diagnostics=false" ,@cmd-args) " "))) + ;; For the rest use the provided command + (t + compile-cmd))) + (cmd (concat "cd " (verilog-ext-buffer-proj-root proj) " && " cmd-processed))) + (funcall fn cmd))) (provide 'verilog-ext-compile) diff --git a/verilog-ext-font-lock.el b/verilog-ext-font-lock.el index 6559174..aeb51b1 100644 --- a/verilog-ext-font-lock.el +++ b/verilog-ext-font-lock.el @@ -271,7 +271,7 @@ obj.method();" '("begin" "end" "this")))) ;; Once UVM dir has been set, obtained through: -;; (verilog-ext-workspace-typedef-batch-update (verilog-ext-dir-files "/home/user/UVM/src/")) +;; (verilog-ext-typedef-batch-update (verilog-ext-dir-files "/home/user/UVM/src/")) ;; And check value of: `verilog-ext-typedef-align-words' (defconst verilog-ext-font-lock-uvm-classes (eval-when-compile diff --git a/verilog-ext-hierarchy.el b/verilog-ext-hierarchy.el index 571e91b..0ee7b9a 100644 --- a/verilog-ext-hierarchy.el +++ b/verilog-ext-hierarchy.el @@ -48,53 +48,49 @@ (const :tag "Hierarchy" hierarchy)) :group 'verilog-ext-hierarchy) -(defcustom verilog-ext-hierarchy-vhier-use-open-buffers t - "Set to non-nil to use list of open Verilog files/dirs with vhier backend." - :type 'boolean - :group 'verilog-ext-hierarchy) - -(defcustom verilog-ext-hierarchy-vhier-dirs nil - "List of library directories to search for with vhier backend." - :type '(repeat directory) - :group 'verilog-ext-hierarchy) - -(defcustom verilog-ext-hierarchy-vhier-files nil - "List of additional files to parse before `current-buffer' with vhier backend. -They will be parsed in the order they are included in the list." - :type '(repeat file) - :group 'verilog-ext-hierarchy) - -(defcustom verilog-ext-hierarchy-vhier-command-file nil - "Verilog-ext vhier command file." - :type 'string - :group 'verilog-ext-hierarchy) - -(defcustom verilog-ext-hierarchy-builtin-dirs nil - "Verilog-ext list of directories for builtin hierarchy extraction. - -If set to nil default to search for current workspace files. - -It is a list of strings containing directories that will be searched for Verilog -files to obtain a flat hierarchy used for hierarchy extraction with the builtin -backend." - :type '(repeat directory) - :group 'verilog-ext-hierarchy) - (defcustom verilog-ext-hierarchy-twidget-init-expand nil "Set to non-nil to initially expand the hierarchy using hierarchy.el frontend." :group 'verilog-ext-hierarchy :type 'boolean) +(defcustom verilog-ext-hierarchy-vhier-use-open-buffers nil + "Set to non-nil to use list of open Verilog files/dirs with vhier backend." + :type 'boolean + :group 'verilog-ext-hierarchy) + ;;;; Utils +(defvar verilog-ext-hierarchy-module-alist nil + "Per project module alist.") + +(defconst verilog-ext-hierarchy-async-inject-variables-re + (eval-when-compile + (regexp-opt '("load-path" + "buffer-file-name" + "default-directory" + "verilog-ext-feature-list" + "verilog-ext-project-alist" + "verilog-ext-hierarchy-backend") + 'symbols))) + ;;;;; hierarchy.el -(defvar verilog-ext-hierarchy-current-flat-hierarchy nil +(defconst verilog-ext-hierarchy-module-cache-file (file-name-concat verilog-ext-cache-dir "module") + "The file where Verilog-ext modules will be written to. +Used to navigate definitions with `verilog-ext-hierarchy-twidget-nav-open'.") +(defconst verilog-ext-hierarchy-internal-cache-file (file-name-concat verilog-ext-cache-dir "hierarchy-builtin") + "The file where Verilog-ext builtin/tree-sitter hierarchies will be written to.") + +(defvar verilog-ext-hierarchy-internal-alist nil + "Per project flat hierarchy alist. +Used by builtin and tree-sitter backends.") +(defvar verilog-ext-hierarchy-current-flat-hier nil "Current flat hierarchy. -Used by `verilog-ext-hierarchy-extract--internal' and its subroutines. +Used by `verilog-ext-hierarchy-extract--internal', +`verilog-ext-hierarchy-ghdl-extract' and their subroutines. +Needed since `verilog-ext-hierarchy-extract--childrenfn' can only +have one argument (item).") -Needed since `verilog-ext-hierarchy-extract--childrenfn' can only have one -argument (item).") (defun verilog-ext-hierarchy--get-node-leaf (node) "Return leaf name of hierarchical reference NODE. @@ -113,14 +109,14 @@ E.g: return \"top.block.subblock\" for \"top.block.subblock.leaf\"." Arg ITEM are hierarchy nodes." (let* ((prefix (verilog-ext-hierarchy--get-node-prefix item)) (leaf (verilog-ext-hierarchy--get-node-leaf item)) - (children (cdr (assoc (car (split-string leaf ":")) verilog-ext-hierarchy-current-flat-hierarchy)))) + (children (cdr (assoc (car (split-string leaf ":")) verilog-ext-hierarchy-current-flat-hier)))) (mapcar (lambda (child) (concat (when prefix (concat prefix ".")) leaf "." child)) children))) (defun verilog-ext-hierarchy-extract--construct-node (node hierarchy) "Recursively build HIERARCHY for NODE using childrenfn." (let ((children (mapcar (lambda (child) (concat node "." child)) - (cdr (assoc (verilog-ext-hierarchy--get-node-leaf node) verilog-ext-hierarchy-current-flat-hierarchy))))) + (cdr (assoc (verilog-ext-hierarchy--get-node-leaf node) verilog-ext-hierarchy-current-flat-hier))))) (when children (hierarchy-add-tree hierarchy node nil #'verilog-ext-hierarchy-extract--childrenfn) (dolist (child children) @@ -130,24 +126,35 @@ Arg ITEM are hierarchy nodes." (defun verilog-ext-hierarchy-extract--internal (module) "Construct hierarchy struct for MODULE. -Modules and instances will be analyzed from the value of -`verilog-ext-hierarchy-current-flat-hierarchy'. -This alist must be populated before calling the function! +Entities and instances will be analyzed from corresponding entry in +`verilog-ext-hierarchy-current-flat-hier'. These entries will have an +associated project present `verilog-ext-project-alist' and will be of the form: +\(module instance1:NAME1 instance2:NAME2 ...\). -`verilog-ext-hierarchy-current-flat-hierarchy' is an alist of the form: - ((moduleA instanceA1:NAME_A1 instanceA2:NAME_A2 ...) - (moduleB instanceB1:NAME_B1 instanceB2:NAME_B2 ...) - ..) +With current prefix, force refreshing of hierarchy database for active project. Return populated `hierarchy' struct." - ;; Some checks - (unless verilog-ext-hierarchy-current-flat-hierarchy - (user-error "Empty hierarchy database, maybe run first `verilog-ext-workspace-hierarchy-parse'?")) - (unless (assoc module verilog-ext-hierarchy-current-flat-hierarchy) - (user-error "Could not find %s in the flat-hierarchy" module)) - (if (not (cdr (assoc module verilog-ext-hierarchy-current-flat-hierarchy))) - (user-error "Current module has no instances") - ;; Construct node + (let* ((proj (verilog-ext-buffer-proj)) + (hierarchy-alist (if current-prefix-arg + nil + (verilog-ext-aget verilog-ext-hierarchy-internal-alist proj)))) + ;; Error checking + (unless hierarchy-alist + (cond (current-prefix-arg + (message "Forcing refresh of hierarchy database for [%s]" proj) + (verilog-ext-hierarchy-parse) + (setq hierarchy-alist (verilog-ext-aget verilog-ext-hierarchy-internal-alist proj))) + ((y-or-n-p (format "Empty hierarchy database for [%s]. Run `verilog-ext-hierarchy-parse'?" proj)) + (verilog-ext-hierarchy-parse) + (setq hierarchy-alist (verilog-ext-aget verilog-ext-hierarchy-internal-alist proj))) + (t + (user-error "Aborting")))) + (unless (assoc module hierarchy-alist) + (user-error "Could not find %s in the flat-hierarchy for project [%s]" module proj)) + (unless (cdr (assoc module hierarchy-alist)) + (user-error "Current module has no instances")) + ;; Extract hierarchy + (setq verilog-ext-hierarchy-current-flat-hier hierarchy-alist) (verilog-ext-hierarchy-extract--construct-node module (hierarchy-new)))) @@ -230,6 +237,43 @@ Alist will be of the form (module instance1:NAME1 instance2:NAME2 ...)." ;; Return value hierarchy-alist)) +;;;;; Module-alist +(defun verilog-ext-hierarchy-build-module-alist (files proj) + "Build alist of modules for FILES in project PROJ. + +Used for hierarchy.el frontend to visit file of module at point." + (let (module-alist) + (dolist (file files) + (cond (;; Builtin or vhier without tree-sitter support + (or (eq verilog-ext-hierarchy-backend 'builtin) + (and (eq verilog-ext-hierarchy-backend 'vhier) + (not (treesit-language-available-p 'verilog)))) + (with-temp-buffer + (insert-file-contents file) + (verilog-ext-with-no-hooks + (verilog-mode)) + (dolist (module-and-pos (verilog-ext-scan-buffer-modules)) + (push `(,(car module-and-pos) + ,file + ,(cadr module-and-pos)) + module-alist)))) + (;; Tree-sitter or vhier with tree-sitter support + (or (eq verilog-ext-hierarchy-backend 'tree-sitter) + (and (eq verilog-ext-hierarchy-backend 'vhier) + (treesit-language-available-p 'verilog))) + (with-temp-buffer + (insert-file-contents file) + (treesit-parser-create 'verilog) + (dolist (module-node (verilog-ts-nodes "\\")) + (push `(,(verilog-ts--node-identifier-name module-node) + ,file + ,(line-number-at-pos (treesit-node-start module-node))) + module-alist)))) + ;; Default, wrong backend + (t + (error "Wrong backend selected")))) + (setf (alist-get proj verilog-ext-hierarchy-module-alist nil 'remove 'string=) module-alist))) + ;;;; Backends/extraction ;;;;; Vhier @@ -244,36 +288,68 @@ Alist will be of the form (module instance1:NAME1 instance2:NAME2 ...)." (defvar verilog-ext-hierarchy-vhier-open-dirs nil "List of open dirs for `verilog-ext-hierarchy-vhier-extract'.") (defvar verilog-ext-hierarchy-vhier-open-files nil "List of open files for `verilog-ext-hierarchy-vhier-extract'.") +(defconst verilog-ext-hierarchy-vhier-cache-file (file-name-concat verilog-ext-cache-dir "hierarchy-vhier") + "The file where Verilog-ext Vhier hierarchy will be written to.") + +(defvar verilog-ext-hierarchy-vhier-alist nil) + (defun verilog-ext-hierarchy-vhier-extract (module) "Extract hierarchy of MODULE using Verilog-Perl vhier as a backend. Return hierarchy as an indented string." (unless (executable-find "vhier") (error "Executable vhier not found")) - (let* ((vhier-args (mapconcat #'identity verilog-ext-hierarchy-vhier-bin-args " ")) + (let* ((proj (verilog-ext-buffer-proj)) + (proj-files `(,@(verilog-ext-proj-files proj) ,@verilog-ext-hierarchy-vhier-open-files)) + (proj-lib-search-path (verilog-ext-proj-lib-search-path proj)) + (cached-hierarchy-alist (if current-prefix-arg + nil + (verilog-ext-aget verilog-ext-hierarchy-vhier-alist proj))) + (vhier-args (mapconcat #'identity verilog-ext-hierarchy-vhier-bin-args " ")) (library-args (concat "+libext+" (mapconcat #'concat verilog-library-extensions "+") " " (mapconcat (lambda (dir) (concat "-y " dir)) - `(,@verilog-ext-hierarchy-vhier-dirs ,@verilog-ext-hierarchy-vhier-open-dirs) + `(,@proj-lib-search-path ,@verilog-ext-hierarchy-vhier-open-dirs) " "))) - (input-files (mapconcat #'identity - `(,@verilog-ext-hierarchy-vhier-files ,@verilog-ext-hierarchy-vhier-open-files) - " ")) + (input-files (mapconcat #'identity proj-files " ")) + (command-file (verilog-ext-proj-command-file proj)) (buf verilog-ext-hierarchy-vhier-buffer-name) (buf-err verilog-ext-hierarchy-vhier-shell-cmds-buffer-name) (err-msg (format "vhier returned with errors\nCheck %s buffer" buf-err)) (cmd (mapconcat #'identity `("vhier" ,vhier-args ,library-args - ,(when verilog-ext-hierarchy-vhier-command-file - (mapconcat #'identity `("-f " ,verilog-ext-hierarchy-vhier-command-file))) - ,input-files ,buffer-file-name "--top-module" ,module) - " "))) - (unless (= 0 (shell-command cmd buf buf-err)) - (pop-to-buffer buf-err) - (error err-msg)) - (with-current-buffer buf - ;; Perform a bit of postprocessing to get the format module:INSTANCE - (verilog-ext-replace-regexp-whole-buffer (concat "\\(?1:" verilog-identifier-sym-re "\\) \\(?2:" verilog-identifier-sym-re "\\)") "\\2:\\1") - (buffer-substring-no-properties (point-min) (point-max))))) + ,(when command-file (mapconcat #'identity `("-f " ,command-file))) + ,input-files ,buffer-file-name + "--top-module" ,module) + " ")) + hierarchy-string hierarchy-alist) + ;; Basic checks + (unless proj + (user-error "Not in a Verilog project buffer, check `verilog-ext-project-alist'")) + ;; Use cache if already available instead of running vhier command + (if cached-hierarchy-alist + (setq verilog-ext-hierarchy-current-flat-hier cached-hierarchy-alist) + ;; Otherwise run vhier command to extract project hierarchy + (unless (= 0 (shell-command cmd buf buf-err)) + (pop-to-buffer buf-err) + (error err-msg)) + (with-current-buffer buf + ;; Perform a bit of postprocessing to get the format module:INSTANCE + (verilog-ext-replace-regexp-whole-buffer (concat "\\(?1:" verilog-identifier-sym-re "\\) \\(?2:" verilog-identifier-sym-re "\\)") "\\2:\\1") + (setq hierarchy-string (buffer-substring-no-properties (point-min) (point-max)))) + ;; Convert indented string to alist for caching and default hierarchy.el displaying + (setq hierarchy-alist (verilog-ext-hierarchy--convert-string-to-alist hierarchy-string)) + (verilog-ext-proj-setcdr proj verilog-ext-hierarchy-vhier-alist hierarchy-alist) + (verilog-ext-hierarchy-build-module-alist proj-files proj) + (setq verilog-ext-hierarchy-current-flat-hier hierarchy-alist) + (when verilog-ext-cache-enable + (verilog-ext-serialize verilog-ext-hierarchy-vhier-alist verilog-ext-hierarchy-vhier-cache-file) + (verilog-ext-serialize verilog-ext-hierarchy-module-alist verilog-ext-hierarchy-module-cache-file))) + ;; Construct hierarchy struct after setting `verilog-ext-hierarchy-current-flat-hier' + (unless (assoc module verilog-ext-hierarchy-current-flat-hier) + (user-error "Could not find %s in the flat-hierarchy for project [%s].\nTry running `verilog-ext-hierarchy-current-buffer' with prefix arg on current buffer" module proj)) + (unless (cdr (assoc module verilog-ext-hierarchy-current-flat-hier)) + (user-error "Current module has no instances")) + (verilog-ext-hierarchy-extract--construct-node module (hierarchy-new)))) ;;;;; Tree-sitter @@ -289,7 +365,7 @@ declaration." (let (module-nodes instances module-instances-alist) (with-temp-buffer (insert-file-contents file) - (verilog-ts-mode) + (treesit-parser-create 'verilog) (setq module-nodes (verilog-ts-module-declarations-nodes-current-buffer)) (dolist (module-node module-nodes module-instances-alist) (setq instances nil) @@ -301,7 +377,7 @@ declaration." (defun verilog-ext-hierarchy-tree-sitter-extract (module) "Extract hierarchy of MODULE using tree-sitter as a backend. -Populate `verilog-ext-hierarchy-current-flat-hierarchy' with alist of modules +Populate `verilog-ext-hierarchy-current-flat-hier' with alist of modules and instances." (unless (eq verilog-ext-hierarchy-backend 'tree-sitter) (error "Wrong backend!")) @@ -321,7 +397,8 @@ declaration." (let (modules instances module-instances-alist) (with-temp-buffer (insert-file-contents file) - (verilog-mode) + (verilog-ext-with-no-hooks + (verilog-mode)) (setq modules (verilog-ext-scan-buffer-modules)) (dolist (module modules module-instances-alist) (setq instances nil) @@ -333,7 +410,7 @@ declaration." (defun verilog-ext-hierarchy-builtin-extract (module) "Extract hierarchy of MODULE using builtin Elisp backend. -Populate `verilog-ext-hierarchy-current-flat-hierarchy' with alist of modules +Populate `verilog-ext-hierarchy-current-flat-hier' with alist of modules and instances." (unless (eq verilog-ext-hierarchy-backend 'builtin) (error "Wrong backend!")) @@ -342,23 +419,43 @@ and instances." ;;;; Frontends/navigation ;;;;; hierarchy.el +(defun verilog-ext-hierarchy-twidget-buf--name () + "Return buffer name for twidget hierarchy buffer." + (concat "*" (verilog-ext-buffer-proj) "*")) + +(defun verilog-ext-hierarchy-twidget--buf-project () + "Return current project from twidget buffer name. + +Assumes that hierarchy buffer name is `verilog-ext-buffer-proj' with stars. +See `verilog-ext-hierarchy-twidget-buf--name'." + (string-remove-prefix "*" (string-remove-suffix "*" (buffer-name)))) + (defun verilog-ext-hierarchy-twidget-nav-open (&optional other-window) "Find definition of node/module at point. -Requires having active some backend on `xref-backend-functions', -e.g. lsp/eglot/ggtags. + +Looks at value of `verilog-ext-hierarchy-module-alist' to check definition place +of modules. If optional arg OTHER-WINDOW is non-nil find definition in other window." (interactive) (let ((module (save-excursion (widget-end-of-line) (backward-sexp) - (thing-at-point 'symbol :no-props)))) + (thing-at-point 'symbol :no-props))) + modules-files file line) (when module - (widget-end-of-line) - (backward-sexp) - (if other-window - (xref-find-definitions-other-window module) - (xref-find-definitions module))))) + (setq modules-files (verilog-ext-aget verilog-ext-hierarchy-module-alist (verilog-ext-hierarchy-twidget--buf-project))) + (setq file (nth 1 (assoc module modules-files))) + (setq line (nth 2 (assoc module modules-files))) + (if (and file line) + (progn + (if other-window + (find-file-other-window file) + (find-file file)) + (goto-char (point-min)) + (forward-line (1- line)) + (recenter '(4) t)) + (user-error "Could not find %s in `verilog-ext-hierarchy-module-alist'" module))))) (defun verilog-ext-hierarchy-twidget-nav-open-other-window () "Find definition of node/module at point in other window." @@ -399,19 +496,17 @@ INFO: Assumes it's initially collapsed, which is the case by default." :lighter " vH" (message "Navigating hierarchy...")) -(defun verilog-ext-hierarchy-twidget-display (hierarchy &optional module) +(defun verilog-ext-hierarchy-twidget-display (hierarchy) "Display HIERARCHY using builtin `hierarchy' and `tree-widget' packages. -Show only module name, discard instance name after colon (mod:INST). - -Optional arg MODULE will set the name of the display buffer, if provided." +Show only module name, discard instance name after colon (mod:INST)." (unless (hierarchy-p hierarchy) (error "Hierarchy must be of hierarchy struct type")) (pop-to-buffer (hierarchy-tree-display hierarchy (lambda (item _) (insert (car (split-string (verilog-ext-hierarchy--get-node-leaf item) ":")))) - (get-buffer-create (concat "*" module "*")))) + (get-buffer-create (verilog-ext-hierarchy-twidget-buf--name)))) ;; Navigation mode and initial expansion (verilog-ext-hierarchy-twidget-nav-mode) (when verilog-ext-hierarchy-twidget-init-expand @@ -480,12 +575,10 @@ Makes use of processed output under `outline-minor-mode' and `outshine'." (setq buffer-read-only t) (view-mode -1)) -(defun verilog-ext-hierarchy-outshine-display (hierarchy &optional module) +(defun verilog-ext-hierarchy-outshine-display (hierarchy) "Display HIERARCHY using `outshine'. -Expects HIERARCHY to be a indented string. -Optional arg MODULE will set the name of the display buffer, if provided." - (let ((buf (or (concat "*" module "*") - "*Verilog-outshine*"))) +Expects HIERARCHY to be a indented string." + (let ((buf "*Verilog-outshine*")) (with-current-buffer (get-buffer-create buf) (setq buffer-read-only nil) (erase-buffer) @@ -516,7 +609,19 @@ Optional arg MODULE will set the name of the display buffer, if provided." (verilog-ext-hierarchy-outshine-nav-mode)) (pop-to-buffer buf))) -;;;; Common +;;;; Core +(defun verilog-ext-hierarchy-serialize () + "Write variables to their cache files." + (verilog-ext-serialize verilog-ext-hierarchy-internal-alist verilog-ext-hierarchy-internal-cache-file) + (verilog-ext-serialize verilog-ext-hierarchy-vhier-alist verilog-ext-hierarchy-vhier-cache-file) + (verilog-ext-serialize verilog-ext-hierarchy-module-alist verilog-ext-hierarchy-module-cache-file)) + +(defun verilog-ext-hierarchy-unserialize () + "Read cache files into their corresponding variables." + (setq verilog-ext-hierarchy-internal-alist (verilog-ext-unserialize verilog-ext-hierarchy-internal-cache-file)) + (setq verilog-ext-hierarchy-vhier-alist (verilog-ext-unserialize verilog-ext-hierarchy-vhier-cache-file)) + (setq verilog-ext-hierarchy-module-alist (verilog-ext-unserialize verilog-ext-hierarchy-module-cache-file))) + (defun verilog-ext-hierarchy-setup () "Setup hierarchy backend/frontend depending on available binaries/packages. If these have been set before, keep their values." @@ -531,7 +636,30 @@ If these have been set before, keep their values." (frontend (or verilog-ext-hierarchy-frontend 'hierarchy))) (setq verilog-ext-hierarchy-backend backend) - (setq verilog-ext-hierarchy-frontend frontend))) + (setq verilog-ext-hierarchy-frontend frontend) + ;; Cache + (when verilog-ext-cache-enable + (verilog-ext-hierarchy-unserialize)))) + +(defun verilog-ext-hierarchy-clear-cache (&optional all) + "Clear hierarchy cache files for current project. + +With prefix arg, clear cache for ALL projects." + (interactive "P") + (if (not all) + (let ((proj (verilog-ext-buffer-proj))) + (unless proj + (user-error "Not in a Verilog project buffer")) + (verilog-ext-proj-setcdr proj verilog-ext-hierarchy-internal-alist nil) + (verilog-ext-proj-setcdr proj verilog-ext-hierarchy-vhier-alist nil) + (verilog-ext-proj-setcdr proj verilog-ext-hierarchy-module-alist nil) + (verilog-ext-hierarchy-serialize) + (message "[%s] Cleared hierarchy cache!" proj)) + (setq verilog-ext-hierarchy-internal-alist nil) + (setq verilog-ext-hierarchy-vhier-alist nil) + (setq verilog-ext-hierarchy-module-alist nil) + (verilog-ext-hierarchy-serialize) + (message "Cleared hierarchy cache!"))) (defun verilog-ext-hierarchy-extract (module) "Construct hierarchy for MODULE depending on selected backend." @@ -547,40 +675,98 @@ If these have been set before, keep their values." ;; Fallback (t (error "Must set a proper extraction backend in `verilog-ext-hierarchy-backend'")))) -(defun verilog-ext-hierarchy-display (hierarchy &optional module) +(defun verilog-ext-hierarchy-display (hierarchy) "Display HIERARCHY depending on selected frontend. Handle conversion (if needed) of input extracted data depending on output frontend. E.g.: If extracted with vhier and displayed with hierarchy it is needed to -convert between an indented string and a populated hierarchy struct. - -Optional arg MODULE will set the name of the display buffer, if provided." +convert between an indented string and a populated hierarchy struct." (let ((display-hierarchy hierarchy)) (cond (;; Outshine (eq verilog-ext-hierarchy-frontend 'outshine) (when (hierarchy-p hierarchy) (setq display-hierarchy (verilog-ext-hierarchy--convert-struct-to-string hierarchy))) - (verilog-ext-hierarchy-outshine-display display-hierarchy module)) + (verilog-ext-hierarchy-outshine-display display-hierarchy)) ;; Hierarchy ((eq verilog-ext-hierarchy-frontend 'hierarchy) - (when (stringp hierarchy) - (let ((top-module (string-trim-left (car (split-string (car (split-string hierarchy "\n")) ":")))) ; First line of the string, as parsed by vhier - (hierarchy-alist (verilog-ext-hierarchy--convert-string-to-alist hierarchy))) - (setq verilog-ext-hierarchy-current-flat-hierarchy hierarchy-alist) - (setq display-hierarchy (verilog-ext-hierarchy-extract--internal top-module)))) - (verilog-ext-hierarchy-twidget-display display-hierarchy module)) + (setq display-hierarchy hierarchy) + (verilog-ext-hierarchy-twidget-display display-hierarchy)) ;; Fallback (t (error "Must set a proper display frontend in `verilog-ext-hierarchy-frontend'"))))) +(defun verilog-ext-hierarchy-parse (&optional verbose) + "Return flat hierarchy of modules and instances of project. + +Populates `verilog-ext-hierarchy-internal-alist' for subsequent hierarchy +extraction and display. + +With current-prefix or VERBOSE, dump output log." + (interactive "P") + (let* ((proj (verilog-ext-buffer-proj)) + (files (verilog-ext-proj-files proj)) + (num-files (length files)) + (num-files-processed 0) + (log-file (concat verilog-ext-hierarchy-internal-cache-file ".log")) + (hier-progress-reporter (make-progress-reporter "[Hierarchy parsing]: " 0 num-files)) + flat-hierarchy data) + (unless files + (error "No files found for current buffer project. Set `verilog-ext-project-alist' accordingly?")) + (when verbose + (delete-file log-file)) + (dolist (file files) + (when verbose + (append-to-file (format "(%0d%%) [Hierarchy parsing] Processing %s\n" (/ (* num-files-processed 100) num-files) file) nil log-file)) + (progress-reporter-update hier-progress-reporter num-files-processed (format "[%s]" file)) + (setq data (cond ((eq verilog-ext-hierarchy-backend 'tree-sitter) + (verilog-ext-hierarchy-tree-sitter-parse-file file)) + ((eq verilog-ext-hierarchy-backend 'builtin) + (verilog-ext-hierarchy-builtin-parse-file file)) + (t + (error "Wrong backend selected!")))) + (when data + (dolist (entry data) + (push entry flat-hierarchy))) + (setq num-files-processed (1+ num-files-processed))) + ;; Update hierarchy and module alists and cache + (verilog-ext-proj-setcdr proj verilog-ext-hierarchy-internal-alist flat-hierarchy) + (verilog-ext-hierarchy-build-module-alist files proj) + (when verilog-ext-cache-enable + (verilog-ext-serialize verilog-ext-hierarchy-internal-alist verilog-ext-hierarchy-internal-cache-file) + (verilog-ext-serialize verilog-ext-hierarchy-module-alist verilog-ext-hierarchy-module-cache-file)) ; Updated after initial call to `verilog-ext-proj-files' + ;; Return value for async related function + (message "Finished analyzing hierarchy!") + (list verilog-ext-hierarchy-internal-alist verilog-ext-hierarchy-module-alist))) + +(defun verilog-ext-hierarchy-parse-async (&optional verbose) + "Return flat hierarchy of modules and instances of project asynchronously. + +Populates `verilog-ext-hierarchy-internal-alist' for subsequent hierarchy +extraction and display. + +With current-prefix or VERBOSE, dump output log." + (interactive "P") + (message "Starting hierarchy parsing for %s" (verilog-ext-buffer-proj)) + (async-start + `(lambda () + ,(async-inject-variables verilog-ext-hierarchy-async-inject-variables-re) + (require 'verilog-ext) + ;; Preserve cache on child Emacs process + (setq verilog-ext-hierarchy-internal-alist (verilog-ext-unserialize verilog-ext-hierarchy-internal-cache-file)) + (setq verilog-ext-hierarchy-module-alist (verilog-ext-unserialize verilog-ext-hierarchy-module-cache-file)) + (verilog-ext-hierarchy-parse ,@verbose)) + (lambda (result) + (message "Finished analyzing hierarchy!") + (setq verilog-ext-hierarchy-internal-alist (car result)) + (setq verilog-ext-hierarchy-module-alist (cadr result))))) + (defun verilog-ext-hierarchy-current-buffer () "Extract and display hierarchy for module of `current-buffer'." (interactive) (let* ((module (verilog-ext-select-file-module)) (hierarchy (verilog-ext-hierarchy-extract module))) - (verilog-ext-hierarchy-display hierarchy module))) - + (verilog-ext-hierarchy-display hierarchy))) (provide 'verilog-ext-hierarchy) diff --git a/verilog-ext-nav.el b/verilog-ext-nav.el index bc48b4e..706c175 100644 --- a/verilog-ext-nav.el +++ b/verilog-ext-nav.el @@ -707,18 +707,13 @@ Runs only when the ag/rg search was triggered by (defvar verilog-ext-jump-to-parent-module-starting-windows nil "Variable to register how many windows are open when trying to jump-to-parent.") -(defun verilog-ext-jump-to-parent-module (&optional dir) +(defun verilog-ext-jump-to-parent-module () "Find current module/interface instantiations via `ag'/`rg'. -Perform search in DIR. If not specified use current directory. -See also `verilog-ext-workspace-jump-to-parent-module'. - Configuration should be done so that `verilog-ext-navigation-ag-rg-hook' is run after the search has been done." (interactive) - (unless dir - (setq dir default-directory)) - (let* ((proj-dir dir) + (let* ((proj-dir (verilog-ext-buffer-proj-root)) (module-name (or (verilog-ext-select-file-module buffer-file-name) (error "No module/interface found @ %s" buffer-file-name))) (module-instance-pcre ; Many thanks to Kaushal Modi for this PCRE @@ -733,6 +728,9 @@ after the search has been done." "\\b(" verilog-identifier-re ")\\b" ; Instance name "(?=[^a-zA-Z0-9_]*\\()" ; Nested lookahead (space/newline after instance name and before opening parenthesis) ")"))) ; Closing lookahead + ;; Check we are in a project + (unless proj-dir + (user-error "Not in a Verilog project buffer")) ;; Update variables used by the ag/rg search finished hooks (setq verilog-ext-jump-to-parent-module-name module-name) (setq verilog-ext-jump-to-parent-module-dir proj-dir) @@ -938,8 +936,6 @@ Otherwise move to previous paragraph." (backward-paragraph))) - - (provide 'verilog-ext-nav) ;;; verilog-ext-nav.el ends here diff --git a/verilog-ext-tags.el b/verilog-ext-tags.el index 7f235df..5926ff7 100644 --- a/verilog-ext-tags.el +++ b/verilog-ext-tags.el @@ -24,6 +24,8 @@ ;;; Code: +(require 'async) +(require 'map) (require 'verilog-ext-nav) (require 'verilog-ts-mode) @@ -38,13 +40,80 @@ (const :tag "Built-in" builtin)) :group 'verilog-ext-tags) +(defcustom verilog-ext-tags-fontify-matches t + "Set to non-nil to fontify matches for xref. + +This setting slightly increases processing time of `verilog-ext-tags-get'." + :type 'boolean + :group 'verilog-ext) + + +(defvar verilog-ext-tags-file-hashes nil) + +(defvar verilog-ext-tags-defs-table nil) +(defvar verilog-ext-tags-refs-table nil) +(defvar verilog-ext-tags-inst-table nil) + +(defvar verilog-ext-tags-defs-file-tables nil) +(defvar verilog-ext-tags-inst-file-tables nil) +(defvar verilog-ext-tags-refs-file-tables nil) + +(defvar verilog-ext-tags-defs-current-file nil) +(defvar verilog-ext-tags-inst-current-file nil) +(defvar verilog-ext-tags-refs-current-file nil) + +(defconst verilog-ext-tags-defs-file-tables-cache-file (file-name-concat verilog-ext-cache-dir "defs-file-tables") + "The file where `verilog-ext' defs-file-tables will be written to.") +(defconst verilog-ext-tags-refs-file-tables-cache-file (file-name-concat verilog-ext-cache-dir "refs-file-tables") + "The file where `verilog-ext' refs-file-tables will be written to.") +(defconst verilog-ext-tags-inst-file-tables-cache-file (file-name-concat verilog-ext-cache-dir "inst-file-tables") + "The file where `verilog-ext' inst-file-tables will be written to.") + +(defconst verilog-ext-tags-defs-table-cache-file (file-name-concat verilog-ext-cache-dir "defs-table") + "The file where `verilog-ext' defs-table will be written to.") +(defconst verilog-ext-tags-refs-table-cache-file (file-name-concat verilog-ext-cache-dir "refs-table") + "The file where `verilog-ext' refs-table will be written to.") +(defconst verilog-ext-tags-inst-table-cache-file (file-name-concat verilog-ext-cache-dir "inst-table") + "The file where `verilog-ext' inst-table will be written to.") + +(defconst verilog-ext-tags-file-hashes-cache-file (file-name-concat verilog-ext-cache-dir "file-hashes") + "The file where `verilog-ext' file-hashes will be written to.") + +(defconst verilog-ext-tags-cache-log-file (file-name-concat verilog-ext-cache-dir "tags.log")) + +(defconst verilog-ext-tags-async-inject-variables-re + (eval-when-compile + (regexp-opt '("load-path" + "buffer-file-name" + "default-directory" + "verilog-ext-feature-list" + "verilog-ext-project-alist" + "verilog-ext-tags-backend") + 'symbols))) + ;;;; Common -(cl-defun verilog-ext-tags-table-push (&key table tag type desc file line col parent) +(defsubst verilog-ext-tags-locs-props (type desc file line col) + "Return :locs properties for current tag. + +These include tag TYPE, description DESC, the FILE, current LINE and COL." + `(:type ,type + :desc ,desc + :file ,file + :line ,line + :col ,col)) + +(defsubst verilog-ext-tags-desc () + "Return string description for tag at point. + +The description determines what `xref' will show when a match is found." + (buffer-substring (line-beginning-position) (line-end-position))) + +(cl-defsubst verilog-ext-tags-table-push (&key table tag type desc file line col 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. +It is needed to provide TYPE, description DESC, FILE, LINE and COL properties to +add the entry in the table. Optional arg PARENT is the module where TAG is defined/instantiated for dot completion. @@ -63,51 +132,58 @@ existing one with current location properties." (puthash parent parent-value table))) ;; Next add the tag if it was not present in the table or update existing tag properties if it was present. (if (not tag-value) - (puthash tag `(:items nil :locs (,(verilog-ext-tags-locs-props type desc file (line-number-at-pos) (current-column)))) table) + (puthash tag `(:items nil :locs (,(verilog-ext-tags-locs-props type desc file line col))) table) (setq locs-plist (plist-get tag-value :locs)) - (setq loc-new (verilog-ext-tags-locs-props type desc file (line-number-at-pos) (current-column))) + (setq loc-new (verilog-ext-tags-locs-props type desc file line col)) (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))))) -(defun verilog-ext-tags-locs-props (type desc file line col) - "Return :locs properties for current tag. - -These include tag TYPE, description DESC, the FILE, current LINE and COL." - `(:type ,type - :desc ,desc - :file ,file - :line ,line - :col ,col)) - -(defun verilog-ext-tags-desc () - "Return string description for tag at point. - -The descriptin determines what `xref' will show when a match is found." - (string-trim (buffer-substring-no-properties (line-beginning-position) (line-end-position)))) - -(defun verilog-ext-tags-is-def-p (tag defs-table file pos) - "Return non-nil if TAG is a definition in DEFS-TABLE. - -TAG is a definition if: - 1) It is present in DEFS-TABLE - 2) Its entry property list in DEFS-TABLE has the same :file and :line values - -Use FILE and POS arguments for comparison." - (let (existing-def existing-def-props) - (and defs-table - (setq existing-def (gethash tag defs-table)) - (setq existing-def-props (plist-get existing-def :locs)) - (progn (catch 'exit - (dolist (prop-list existing-def-props) - (when (and (eq (plist-get prop-list :file) file) - (eq (plist-get prop-list :line) (line-number-at-pos pos))) - (throw 'exit t)))))))) +(defun verilog-ext-tags-table-remove-file-locs (file file-tables table) + "Remove FILE tag locations in TABLE. + +FILE-TABLES is the intermediate variable with a per-file hash table for current +project." + (when (and file-tables + (gethash file file-tables) + table) + (let ((file-tag-locs-table (gethash file file-tables)) + items-and-locs locs tag-loc) + (maphash (lambda (key value) + (setq items-and-locs (gethash (car key) table)) + (setq locs (plist-get items-and-locs :locs)) + (setq tag-loc (verilog-ext-tags-locs-props (plist-get value :type) + (plist-get value :desc) + (plist-get (cdr key) :file) + (plist-get (cdr key) :line) + (plist-get value :col))) + (when (member tag-loc locs) + (setf (cl-getf items-and-locs :locs) (remove tag-loc locs))) + (when (not (plist-get items-and-locs :locs)) + (remhash (car key) table))) + file-tag-locs-table)))) + +(defun verilog-ext-tags-add-file-locs (file file-tables table) + "Add FILE tag locations in TABLE. + +FILE-TABLES is the intermediate variable with a per-file hash table for current +project." + (let ((file-table (gethash file file-tables))) + (maphash (lambda (key value) + (verilog-ext-tags-table-push :table table + :tag (car key) + :type (plist-get value :type) + :desc (plist-get value :desc) + :file (plist-get (cdr key) :file) + :line (plist-get (cdr key) :line) + :col (plist-get value :col) + :parent (plist-get value :parent))) + file-table))) ;;;; Builtin -(cl-defun verilog-ext-tags-table-push-defs (&key tag-type table file start limit parent inst-table) +(cl-defun verilog-ext-tags-table-push-defs (&key tag-type file start limit parent) "Push definitions of TAG-TYPE inside hash table TABLE. FILE might be specified for the cases when a temp-buffer without an associated @@ -116,12 +192,8 @@ file is being parsed. Limit search between START and LIMIT if provided, otherwise search the whole buffer. -PARENT is the module where TAG is defined/instantiated for dot completion. - -INST-TABLE is the instances table, needed to separate between tags for -completion and navigation." +PARENT is the module where TAG is defined/instantiated for dot completion." (let ((ignore-paren-decl (eq tag-type 'declarations-no-parens)) - (inst-table (or inst-table (make-hash-table :test #'equal))) tag type data inner-start inner-limit) (unless start (setq start (point-min))) (unless limit (setq limit (point-max))) @@ -138,62 +210,70 @@ completion and navigation." (when ignore-paren-decl (verilog-in-parenthesis-p)) (not tag)) - (verilog-ext-tags-table-push :table table - :tag tag - :type type - :desc (verilog-ext-tags-desc) - :file file - :parent parent)))) + (puthash `(,tag + :file ,file + :line ,(line-number-at-pos)) + `(:type ,type + :desc ,(verilog-ext-tags-desc) + :col ,(current-column) + :parent ,parent) + verilog-ext-tags-defs-current-file)))) ('tf (while (setq data (verilog-ext-find-function-task-fwd limit)) (setq tag (match-string-no-properties 1)) - (verilog-ext-tags-table-push :table table - :tag tag - :type (alist-get 'type data) - :desc (verilog-ext-tags-desc) - :file file - :parent parent) + (puthash `(,tag + :file ,file + :line ,(line-number-at-pos)) + `(:type ,(alist-get 'type data) + :desc ,(verilog-ext-tags-desc) + :col ,(current-column) + :parent ,parent) + verilog-ext-tags-defs-current-file) (save-excursion ; Get tasks and function declarations (when (< (setq inner-start (alist-get 'pos data)) (setq inner-limit (save-excursion (verilog-re-search-backward "\\<\\(function\\|task\\)\\>" (line-beginning-position) :no-error) (verilog-ext-pos-at-forward-sexp)))) (verilog-ext-tags-table-push-defs :tag-type 'declarations-no-parens - :table table :file file :start inner-start :limit inner-limit :parent tag))))) ('instances (while (verilog-ext-find-module-instance-fwd limit) - (verilog-ext-tags-table-push :table inst-table - :tag (match-string-no-properties 2) - :type (match-string-no-properties 1) - :desc (verilog-ext-tags-desc) - :file file - :parent parent))) + (puthash `(,(match-string-no-properties 2) + :file ,file + :line ,(line-number-at-pos)) + `(:type ,(match-string-no-properties 1) + :desc ,(verilog-ext-tags-desc) + :col ,(current-column) + :parent ,parent) + verilog-ext-tags-inst-current-file))) ('structs (while (setq data (verilog-ext-find-struct)) (setq tag (alist-get 'name data)) - (verilog-ext-tags-table-push :table table - :tag tag - :type "struct" - :desc (verilog-ext-tags-desc) - :file file - :parent parent) + (puthash `(,tag + :file ,file + :line ,(line-number-at-pos)) + `(:type "struct" + :desc ,(verilog-ext-tags-desc) + :col ,(current-column) + :parent ,parent) + verilog-ext-tags-defs-current-file) (save-excursion ; Get struct items (verilog-ext-backward-syntactic-ws) (verilog-ext-tags-table-push-defs :tag-type 'declarations - :table table :file file :start (verilog-ext-pos-at-backward-sexp) :limit (point) :parent tag)))) ('classes (while (setq data (verilog-ext-find-class-fwd limit)) (setq tag (alist-get 'name data)) - (verilog-ext-tags-table-push :table table - :tag tag - :type "class" - :desc (verilog-ext-tags-desc) - :file file - :parent parent) + (puthash `(,tag + :file ,file + :line ,(line-number-at-pos)) + `(:type "class" + :desc ,(verilog-ext-tags-desc) + :col ,(current-column) + :parent ,parent) + verilog-ext-tags-defs-current-file) ;; Get class items (save-excursion (verilog-re-search-backward "\\" nil :no-error) @@ -201,7 +281,6 @@ completion and navigation." (setq inner-limit (verilog-ext-pos-at-forward-sexp))) (dolist (defs '(declarations-no-parens tf structs)) (verilog-ext-tags-table-push-defs :tag-type defs - :table table :file file :start inner-start :limit inner-limit @@ -209,11 +288,13 @@ completion and navigation." ('top-items (while (verilog-re-search-forward verilog-ext-top-re nil :no-error) (setq tag (match-string-no-properties 3)) (setq type (match-string-no-properties 1)) - (verilog-ext-tags-table-push :table table - :tag tag - :type type - :desc (verilog-ext-tags-desc) - :file file) + (puthash `(,tag + :file ,file + :line ,(line-number-at-pos)) + `(:type ,type + :desc ,(verilog-ext-tags-desc) + :col ,(current-column)) + verilog-ext-tags-defs-current-file) ;; Get top-block items (setq inner-start (match-beginning 1)) (save-excursion @@ -224,44 +305,37 @@ completion and navigation." (setq top-items-defs `(,@top-items-defs instances))) (dolist (defs top-items-defs) (verilog-ext-tags-table-push-defs :tag-type defs - :table table :file file :start inner-start :limit inner-limit - :parent tag - :inst-table inst-table))))) + :parent tag))))) (_ (error "Unsupported tag type"))))))) -(cl-defun verilog-ext-tags-table-push-refs (&key table defs-table file start limit) +(defun verilog-ext-tags-table-push-refs (file) "Push references inside hash table TABLE. -Table DEFS-TABLE is used to filter out references that have already been parsed -as definitions. - FILE can be provided for the case when references are fetched from a -temp-buffer. - -Limit search between START and LIMIT if provided, otherwise search the whole -buffer." - (let (tag begin) - (unless start (setq start (point-min))) - (unless limit (setq limit (point-max))) +temp-buffer." + (let (tag begin line) (save-match-data (save-excursion - (goto-char start) - (while (verilog-re-search-forward verilog-identifier-sym-re limit :no-error) + (goto-char (point-min)) + (while (verilog-re-search-forward verilog-identifier-sym-re nil :no-error) (setq begin (match-beginning 0)) (setq tag (match-string-no-properties 0)) + (setq line (line-number-at-pos begin)) (unless (or (member tag verilog-keywords) ; Filter verilog keywords - (and defs-table ; Filter existing definitions - (verilog-ext-tags-is-def-p tag defs-table file (point))) + (member tag verilog-ext-compiler-directives) + (gethash `(,tag :file ,file :line ,line) verilog-ext-tags-defs-current-file) ; Unless it's already a definition (save-excursion ; Filter bit-width expressions (goto-char begin) (eq (preceding-char) ?'))) - (verilog-ext-tags-table-push :table table - :tag tag - :desc (verilog-ext-tags-desc) - :file file))))))) + (puthash `(,tag ; Key plist + :file ,file + :line ,(line-number-at-pos)) + `(:desc ,(verilog-ext-tags-desc) ; Value plist + :col ,(current-column)) + verilog-ext-tags-refs-current-file))))))) ;;;; Tree-sitter @@ -324,23 +398,18 @@ completion.") (eval-when-compile (regexp-opt '("task_declaration" "function_declaration" "class_constructor_declaration") 'symbols))) -(cl-defun verilog-ext-tags-table-push-defs-ts (&key table inst-table file) - "Push definitions inside hash table TABLE using tree-sitter. - -FILE might be specified for the cases when a temp-buffer without an associated -file is being parsed. +(defun verilog-ext-tags-table-push-defs-ts (file) + "Push current FILE definitions using tree-sitter. -INST-TABLE is the instances table, needed to separate between tags for -completion and navigation." +Update hash tables `verilog-ext-tags-defs-current-file' and +`verilog-ext-tags-inst-current-file'." (let* ((node (treesit-buffer-root-node 'verilog)) (tree (treesit-induce-sparse-tree node verilog-ext-tags-definitions-ts-re - nil 1000)) - (inst-table (or inst-table (make-hash-table :test #'equal)))) - (verilog-ext-tags-table-push-defs-ts--recurse :table table - :inst-table inst-table - :node tree + nil 1000))) + (verilog-ext-tags-table-push-defs-ts--recurse :node tree + :parent nil :file file))) (defun verilog-ext-tags-table-push-defs-ts--parent (ts-node ts-type parent-node) @@ -358,30 +427,21 @@ this function is synctactic sugar for (t ;; Default (verilog-ts--node-identifier-name parent-node)))) -(cl-defun verilog-ext-tags-table-push-defs-ts--recurse (&key table inst-table node parent file) - "Push definitions recursively inside hash table TABLE using tree-sitter. +(cl-defun verilog-ext-tags-table-push-defs-ts--recurse (&key node parent file) + "Push current FILE definitions recursively using tree-sitter. Traverse the tree starting at NODE. -PARENT is passed as an argument to build the :items prop list of TABLE. - -FILE might be specified for the cases when a temp-buffer without an associated -file is being parsed. - -INST-TABLE is the instances table, needed to separate between tags for -completion and navigation." +PARENT is passed as an argument to build the :items prop list of +`verilog-ext-tags-defs-current-file'." (let* ((ts-node (car node)) (children (cdr node)) - (ts-type (treesit-node-type ts-node)) - (is-instance (and ts-type (string-match "\\(module\\|interface\\)_instantiation" ts-type))) - (is-typedef-class (and ts-type - (string-match "\\" ts-type) - (string-match (concat "typedef\\s-+class\\s-+" verilog-identifier-re "\\s-*;") (treesit-node-text ts-node :no-prop))))) + (type (treesit-node-type ts-node)) + (is-instance (and type (string-match verilog-ts-instance-re type))) + (is-typedef-class (verilog-ts--node-is-typedef-class-p ts-node))) ;; Iterate over all the nodes of the tree (mapc (lambda (child-node) - (verilog-ext-tags-table-push-defs-ts--recurse :table table - :inst-table inst-table - :node child-node + (verilog-ext-tags-table-push-defs-ts--recurse :node child-node :parent ts-node :file file)) children) @@ -389,48 +449,252 @@ completion and navigation." (when (and ts-node (not is-typedef-class)) ; root ts-node will be nil (goto-char (treesit-node-start ts-node)) (if is-instance - (verilog-ext-tags-table-push :table inst-table - :tag (verilog-ts--node-instance-name ts-node) - :type (verilog-ts--node-identifier-name ts-node) - :file file - :parent (verilog-ts--node-identifier-name parent)) - (verilog-ext-tags-table-push :table table - :tag (verilog-ts--node-identifier-name ts-node) - :type (verilog-ts--node-identifier-type ts-node) - :desc (verilog-ext-tags-desc) - :file file - :parent (verilog-ext-tags-table-push-defs-ts--parent ts-node ts-type parent)))))) - -(cl-defun verilog-ext-tags-table-push-refs-ts (&key table defs-table file) - "Push references inside hash table TABLE using tree-sitter. - -Optional definitions table DEFS-TABLE is used to filter out references that have -already been parsed as definitions. - -FILE can be provided for the case when references are fetched from a -temp-buffer." - (let (tag pos) + (puthash `(,(verilog-ts--node-instance-name ts-node) ; Key plist + :file ,file + :line ,(line-number-at-pos)) + `(:type ,(verilog-ts--node-identifier-name ts-node) ; Value plist + :col ,(current-column) + :parent ,(verilog-ts--node-identifier-name parent)) + verilog-ext-tags-inst-current-file) + (puthash `(,(verilog-ts--node-identifier-name ts-node) ; Key plist + :file ,file + :line ,(line-number-at-pos)) + `(:type ,type ; Value plist + :desc ,(verilog-ext-tags-desc) + :col ,(current-column) + :parent ,(verilog-ext-tags-table-push-defs-ts--parent ts-node type parent)) + verilog-ext-tags-defs-current-file))))) + +(defun verilog-ext-tags-table-push-refs-ts (file) + "Push current FILE references using tree-sitter. + +Update hash table `verilog-ext-tags-refs-current-file'." + (let (tag pos line) (dolist (node (verilog-ts-nodes "simple_identifier")) (setq tag (treesit-node-text node :no-prop)) (setq pos (treesit-node-start node)) - (unless (and defs-table - (verilog-ext-tags-is-def-p tag defs-table file pos)) + (setq line (line-number-at-pos pos)) + (unless (gethash `(,tag :file ,file :line ,line) verilog-ext-tags-defs-current-file) ; Unless it's already a definition (goto-char pos) - (verilog-ext-tags-table-push :table table - :tag tag - :desc (verilog-ext-tags-desc) - :file file))))) + (puthash `(,tag ; Key plist + :file ,file + :line ,(line-number-at-pos)) + `(:desc ,(verilog-ext-tags-desc) ; Value plist + :col ,(current-column)) + verilog-ext-tags-refs-current-file))))) + + +;;;; Tags collection and cache +(defun verilog-ext-tags-proj-init (proj) + "Initialize value of PROJ variables and hash-tables needed for tags collection." + (dolist (var '(verilog-ext-tags-file-hashes + verilog-ext-tags-defs-file-tables + verilog-ext-tags-inst-file-tables + verilog-ext-tags-refs-file-tables + verilog-ext-tags-defs-table + verilog-ext-tags-inst-table + verilog-ext-tags-refs-table)) + (unless (verilog-ext-aget (eval var) proj) + (set var (cons (cons proj (make-hash-table :test 'equal)) (symbol-value var)))))) + +(defun verilog-ext-tags-get--process-file (file proj &optional file-was-removed verbose) + "Auxiliary function to process FILE tags of project PROJ. + +Steps: + - Initialize tags variables + - For removed files, remove corresponding file locs from tags tables + (FILE-WAS-REMOVED should be non-nil) + - Check current file hash and compare to previous stored ones to check if it + has changed + - If it did, consider 3 different scenarios: + - File did not change: skip that file and check next one + - File changed: remove previous file locs, collect new file tags and update + tables and file hashes + - File is new: collect new file tags and update tables and file hashes (no + need to remove any file locs). + +Optional arg VERBOSE to display extra messages for debugging." + (let ((proj-file-hashes (verilog-ext-aget verilog-ext-tags-file-hashes proj)) + (proj-defs-file-tables (verilog-ext-aget verilog-ext-tags-defs-file-tables proj)) + (proj-defs-table (verilog-ext-aget verilog-ext-tags-defs-table proj)) + (proj-inst-file-tables (verilog-ext-aget verilog-ext-tags-inst-file-tables proj)) + (proj-inst-table (verilog-ext-aget verilog-ext-tags-inst-table proj)) + (proj-refs-file-tables (verilog-ext-aget verilog-ext-tags-refs-file-tables proj)) + (proj-refs-table (verilog-ext-aget verilog-ext-tags-refs-table proj)) + file-hash-new file-hash-old) + ;; Reset current file tags + (setq verilog-ext-tags-defs-current-file (make-hash-table :test 'equal)) + (setq verilog-ext-tags-inst-current-file (make-hash-table :test 'equal)) + (setq verilog-ext-tags-refs-current-file (make-hash-table :test 'equal)) + ;; Process tags + (if file-was-removed + (progn ; Remove tags in reverse order: first locs from the table, then from intermediate tables, and finally from file-hashes + (verilog-ext-tags-table-remove-file-locs file proj-defs-file-tables proj-defs-table) + (verilog-ext-tags-table-remove-file-locs file proj-inst-file-tables proj-inst-table) + (verilog-ext-tags-table-remove-file-locs file proj-refs-file-tables proj-refs-table) + (remhash file proj-defs-file-tables) + (remhash file proj-inst-file-tables) + (remhash file proj-refs-file-tables) + (remhash file proj-file-hashes)) + ;; File not removed: Could be not modified, modified or added + (with-temp-buffer + (insert-file-contents file) + (setq file-hash-new (secure-hash 'md5 (buffer-substring-no-properties (point-min) (point-max)))) + (setq file-hash-old (gethash file proj-file-hashes)) + (if (string= file-hash-new file-hash-old) + (when verbose (message "Skipping file: %s" file)) ; Not modified + ;; Modified/added + (puthash file file-hash-new proj-file-hashes) + ;; If file has changed remove old tags + (when file-hash-old + (verilog-ext-tags-table-remove-file-locs file proj-defs-file-tables proj-defs-table) + (verilog-ext-tags-table-remove-file-locs file proj-inst-file-tables proj-inst-table) + (verilog-ext-tags-table-remove-file-locs file proj-refs-file-tables proj-refs-table)) + ;; If file is new or has changed, collect tags + (cond (;; Tree-sitter + (eq verilog-ext-tags-backend 'tree-sitter) + (if verilog-ext-tags-fontify-matches + (verilog-ext-with-no-hooks ; Avoid spending time on any possible hooks, just on fontifying to get text properties + (verilog-ts-mode) + (font-lock-ensure)) + (treesit-parser-create 'verilog)) ; Not running `verilog-ts-mode' avoids unnecessary hooks for this task + (verilog-ext-tags-table-push-defs-ts file) ; Populates `verilog-ext-tags-defs-current-file' and `verilog-ext-tags-inst-current-file' + (verilog-ext-tags-table-push-refs-ts file)) ; Populates `verilog-ext-tags-refs-current-file' + (;; Builtin + (eq verilog-ext-tags-backend 'builtin) + (verilog-ext-with-no-hooks + (verilog-mode)) + (when verilog-ext-tags-fontify-matches + (font-lock-ensure)) + (cond (;; Top-block based-file (module/interface/package/program) + (save-excursion (verilog-re-search-forward verilog-ext-top-re nil :no-error)) + (verilog-ext-tags-table-push-defs :tag-type 'top-items :file file)) + ;; No top-blocks class-based file + ((save-excursion (verilog-ext-find-class-fwd)) + (verilog-ext-tags-table-push-defs :tag-type 'classes :file file)) + ;; Default, + (t (dolist (defs '(declarations tf structs)) + (verilog-ext-tags-table-push-defs :tag-type defs :file file)))) + (verilog-ext-tags-table-push-refs file)) + (t ; Fallback error + (error "Wrong backend for `verilog-ext-tags-backend'"))) + ;; Update file tables + (puthash file verilog-ext-tags-defs-current-file proj-defs-file-tables) + (puthash file verilog-ext-tags-inst-current-file proj-inst-file-tables) + (puthash file verilog-ext-tags-refs-current-file proj-refs-file-tables) + ;; Update tables + (verilog-ext-tags-add-file-locs file proj-defs-file-tables proj-defs-table) + (verilog-ext-tags-add-file-locs file proj-inst-file-tables proj-inst-table) + (verilog-ext-tags-add-file-locs file proj-refs-file-tables proj-refs-table)))))) + +(defun verilog-ext-tags-get (&optional verbose) + "Get tags of current project. +With current-prefix or VERBOSE, dump output log." + (interactive "P") + (let* ((proj (verilog-ext-buffer-proj)) + (files (verilog-ext-proj-files proj)) + (files-removed (seq-difference (map-keys (verilog-ext-aget verilog-ext-tags-file-hashes proj)) files)) + (num-files (+ (length files-removed) (length files))) + (num-files-processed 0) + (log-file verilog-ext-tags-cache-log-file) + (tags-progress-reporter (make-progress-reporter "[Tags collection]: " 0 num-files))) + (verilog-ext-tags-proj-init proj) + (when verbose + (delete-file log-file)) + (dolist (file files-removed) + (progress-reporter-update tags-progress-reporter num-files-processed (format "[%s]" file)) + (verilog-ext-tags-get--process-file file proj :file-was-removed verbose) + (setq num-files-processed (1+ num-files-processed))) + (dolist (file files) + (when verbose + (append-to-file (format "(%0d%%) [Tags collection] Processing %s\n" (/ (* num-files-processed 100) num-files) file) nil log-file)) + (progress-reporter-update tags-progress-reporter num-files-processed (format "[%s]" file)) + (verilog-ext-tags-get--process-file file proj nil verbose) + (setq num-files-processed (1+ num-files-processed))) + (message "Finished collection of tags!"))) + +(defun verilog-ext-tags-get-async (&optional verbose) + "Create tags table asynchronously. +With current-prefix or VERBOSE, dump output log." + (interactive "P") + (let ((proj-root (verilog-ext-buffer-proj-root))) + (unless proj-root + (user-error "Not in a Verilog project buffer")) + (message "Starting tag collection for %s" proj-root) + (async-start + `(lambda () + ,(async-inject-variables verilog-ext-tags-async-inject-variables-re) + (require 'verilog-ext) + (verilog-ext-tags-unserialize) ; Read environment in child process + (verilog-ext-tags-get ,@verbose) ; Update variables in child process + (verilog-ext-tags-serialize)) ; Update cache file in childe process + (lambda (_result) + (verilog-ext-tags-unserialize) + (message "Finished collection of tags!"))))) ; Update parent process from cache file + +(defun verilog-ext-tags-serialize () + "Write variables to their cache files." + (message "Serializing `verilog-ext' tags cache...") + (dolist (var `((,verilog-ext-tags-defs-file-tables . ,verilog-ext-tags-defs-file-tables-cache-file) + (,verilog-ext-tags-refs-file-tables . ,verilog-ext-tags-refs-file-tables-cache-file) + (,verilog-ext-tags-inst-file-tables . ,verilog-ext-tags-inst-file-tables-cache-file) + (,verilog-ext-tags-defs-table . ,verilog-ext-tags-defs-table-cache-file) + (,verilog-ext-tags-inst-table . ,verilog-ext-tags-inst-table-cache-file) + (,verilog-ext-tags-refs-table . ,verilog-ext-tags-refs-table-cache-file) + (,verilog-ext-tags-file-hashes . ,verilog-ext-tags-file-hashes-cache-file))) + (verilog-ext-serialize (car var) (cdr var))) + (message "Serialized `verilog-ext' tags cache!")) + +(defun verilog-ext-tags-unserialize () + "Read cache files into their corresponding variables." + (dolist (var `((verilog-ext-tags-defs-file-tables . ,verilog-ext-tags-defs-file-tables-cache-file) + (verilog-ext-tags-refs-file-tables . ,verilog-ext-tags-refs-file-tables-cache-file) + (verilog-ext-tags-inst-file-tables . ,verilog-ext-tags-inst-file-tables-cache-file) + (verilog-ext-tags-defs-table . ,verilog-ext-tags-defs-table-cache-file) + (verilog-ext-tags-inst-table . ,verilog-ext-tags-inst-table-cache-file) + (verilog-ext-tags-refs-table . ,verilog-ext-tags-refs-table-cache-file) + (verilog-ext-tags-file-hashes . ,verilog-ext-tags-file-hashes-cache-file))) + (set (car var) (verilog-ext-unserialize (cdr var))))) -;;;; Setup (defun verilog-ext-tags-setup () - "Setup tags backend depending on tree-sitter availability. -If it has been set before, keep its value." + "Setup tags feature: backend, cache read at startup and write before exit." (let ((backend (or verilog-ext-tags-backend (if (and (treesit-available-p) (treesit-language-available-p 'verilog)) 'tree-sitter 'builtin)))) - (setq verilog-ext-tags-backend backend))) + (setq verilog-ext-tags-backend backend) + (when verilog-ext-cache-enable + (verilog-ext-tags-unserialize) + (add-hook 'kill-emacs-hook #'verilog-ext-tags-serialize)))) + +(defun verilog-ext-tags-clear-cache (&optional all) + "Clear tags cache files for current project. + +With prefix arg, clear cache for ALL projects." + (interactive "P") + (if (not all) + (let ((proj (verilog-ext-buffer-proj))) + (unless proj + (user-error "Not in a Verilog project buffer")) + (verilog-ext-proj-setcdr proj verilog-ext-tags-defs-table nil) + (verilog-ext-proj-setcdr proj verilog-ext-tags-refs-table nil) + (verilog-ext-proj-setcdr proj verilog-ext-tags-inst-table nil) + (verilog-ext-proj-setcdr proj verilog-ext-tags-defs-file-tables nil) + (verilog-ext-proj-setcdr proj verilog-ext-tags-refs-file-tables nil) + (verilog-ext-proj-setcdr proj verilog-ext-tags-inst-file-tables nil) + (verilog-ext-proj-setcdr proj verilog-ext-tags-file-hashes nil) + (verilog-ext-tags-serialize) + (message "[%s] Cleared tags cache!" proj)) + (setq verilog-ext-tags-defs-table nil) + (setq verilog-ext-tags-refs-table nil) + (setq verilog-ext-tags-inst-table nil) + (setq verilog-ext-tags-defs-file-tables nil) + (setq verilog-ext-tags-refs-file-tables nil) + (setq verilog-ext-tags-inst-file-tables nil) + (setq verilog-ext-tags-file-hashes nil) + (verilog-ext-tags-serialize) + (message "Cleared all projects tags cache!"))) (provide 'verilog-ext-tags) diff --git a/verilog-ext-typedef.el b/verilog-ext-typedef.el index 200fb97..4c44e5d 100644 --- a/verilog-ext-typedef.el +++ b/verilog-ext-typedef.el @@ -25,10 +25,22 @@ ;;; Code: +(require 'async) (require 'verilog-ext-nav) -(defvar verilog-ext-typedef-align-words nil) -(defvar verilog-ext-typedef-align-words-re nil) +(defvar verilog-ext-typedef-align-words-current-proj nil) +(defvar verilog-ext-typedef-align-words-re-alist nil) + +(defconst verilog-ext-typedef-cache-file (file-name-concat verilog-ext-cache-dir "typedefs")) +(defconst verilog-ext-typedef-cache-log-file (file-name-concat verilog-ext-cache-dir "typedefs.log")) + +(defconst verilog-ext-typedef-async-inject-variables-re + (eval-when-compile + (regexp-opt '("load-path" + "buffer-file-name" + "default-directory" + "verilog-ext-project-alist") + 'symbols))) (defun verilog-ext-typedef--var-find (regex &optional limit) "Search for REGEX and bound to LIMIT. @@ -52,18 +64,18 @@ Match data is expected to fits that one of (defun verilog-ext-typedef--var-buffer-update (regex) "Scan REGEX on current buffer and update list of user typedefs. -See `verilog-ext-typedef-align-words'. +See `verilog-ext-typedef-align-words-current-proj'. Used for fontification and alignment." (let (type) (save-excursion (goto-char (point-min)) (while (setq type (verilog-ext-typedef--var-find regex)) - (unless (member type verilog-ext-typedef-align-words) - (push type verilog-ext-typedef-align-words)))))) + (unless (member type verilog-ext-typedef-align-words-current-proj) + (push type verilog-ext-typedef-align-words-current-proj)))))) (defun verilog-ext-typedef--tf-args-buffer-update () "Scan functions/tasks arguments on current buffer to update user typedefs list. -See `verilog-ext-typedef-align-words'. +See `verilog-ext-typedef-align-words-current-proj'. Used for fontification and alignment." (let (tf-args arg-proc) ; tf-args is expected to be a list of strings (save-excursion @@ -80,8 +92,8 @@ Used for fontification and alignment." ;; Typedef word will be the first one of the argument (when (equal 0 (string-match (string-remove-suffix ";" verilog-ext-typedef-var-decl-single-re) arg-proc)) (setq arg-proc (car (split-string arg-proc " "))) - (unless (member arg-proc verilog-ext-typedef-align-words) - (push arg-proc verilog-ext-typedef-align-words))))))))) + (unless (member arg-proc verilog-ext-typedef-align-words-current-proj) + (push arg-proc verilog-ext-typedef-align-words-current-proj))))))))) (defun verilog-ext-typedef--class-buffer-update () "Scan class declarations on current buffer to update user typedef list." @@ -89,8 +101,8 @@ Used for fontification and alignment." (save-excursion (goto-char (point-min)) (while (setq word (alist-get 'name (verilog-ext-find-class-fwd))) - (unless (member word verilog-ext-typedef-align-words) - (push word verilog-ext-typedef-align-words)))))) + (unless (member word verilog-ext-typedef-align-words-current-proj) + (push word verilog-ext-typedef-align-words-current-proj)))))) (defun verilog-ext-typedef--typedef-buffer-update (regex) "Scan REGEX typedef declarations on current buffer to update user typedef list." @@ -99,11 +111,13 @@ Used for fontification and alignment." (goto-char (point-min)) (while (verilog-re-search-forward regex nil t) (setq word (match-string-no-properties 2)) - (unless (member word verilog-ext-typedef-align-words) - (push word verilog-ext-typedef-align-words)))))) + (unless (member word verilog-ext-typedef-align-words-current-proj) + (push word verilog-ext-typedef-align-words-current-proj)))))) (defun verilog-ext-typedef--var-decl-update () - "Scan and update Verilog buffers and `verilog-ext-typedef-align-words'." + "Scan Verilog buffers and update typedef vars. + +I.e: populate `verilog-ext-typedef-align-words-current-proj'." (verilog-ext-typedef--var-buffer-update verilog-ext-typedef-var-decl-single-re) (verilog-ext-typedef--var-buffer-update verilog-ext-typedef-var-decl-multiple-re) (verilog-ext-typedef--tf-args-buffer-update) @@ -111,6 +125,105 @@ Used for fontification and alignment." (verilog-ext-typedef--typedef-buffer-update verilog-ext-typedef-class-re) (verilog-ext-typedef--typedef-buffer-update verilog-ext-typedef-generic-re)) +(defun verilog-ext-typedef-get (&optional verbose) + "Scan all (System)Verilog FILES and udpate typedef list. + +It will return the updated value of +`verilog-ext-typedef-align-words-current-proj', which can be used later along +with `verilog-regexp-words' to update the variable +`verilog-align-typedef-regexp'. This enables the fontification and alignment of +user typedefs. + +If optional arg VERBOSE is enabled, dump output to a logfile for potential debug +in corresponding async function." + (interactive "P") + (let* ((proj (verilog-ext-buffer-proj)) + (files (verilog-ext-proj-files proj)) + (num-files (length files)) + (num-files-processed 0) + (log-file verilog-ext-typedef-cache-log-file) + (tags-progress-reporter (make-progress-reporter "[Typedefs collection]: " 0 num-files))) + (setq verilog-ext-typedef-align-words-current-proj nil) ; Reset value + (when verbose + (delete-file log-file)) + (dolist (file files) + (progress-reporter-update tags-progress-reporter num-files-processed (format "[%s]" file)) + (when verbose + (append-to-file (format "(%0d%%) [Typedef collection] Processing %s\n" (/ (* num-files-processed 100) num-files) file) nil log-file)) + (with-temp-buffer + (insert-file-contents file) + (verilog-ext-with-no-hooks + (verilog-mode)) + (verilog-ext-typedef--var-decl-update)) + (setq num-files-processed (1+ num-files-processed))) + ;; Postprocess obtained results (remove keywords and generic types that were uppercase) + (mapc (lambda (elm) + (when (member elm verilog-keywords) + (delete elm verilog-ext-typedef-align-words-current-proj))) + verilog-ext-typedef-align-words-current-proj) + (let ((case-fold-search nil)) + (setq verilog-ext-typedef-align-words-current-proj (seq-remove (lambda (s) + (not (string-match-p "[[:lower:]]" s))) + verilog-ext-typedef-align-words-current-proj))) + ;; Store results + (when verilog-ext-typedef-align-words-current-proj + (verilog-ext-proj-setcdr proj verilog-ext-typedef-align-words-re-alist (verilog-regexp-words verilog-ext-typedef-align-words-current-proj))) + (when verilog-ext-cache-enable + (verilog-ext-serialize verilog-ext-typedef-align-words-re-alist verilog-ext-typedef-cache-file)) + ;; Report end and return value for async processing + (message "Finished collection of typedefs!") + verilog-ext-typedef-align-words-re-alist)) + +(defun verilog-ext-typedef-get-async (&optional verbose) + "Update typedef list of current asynchronously. +With current-prefix or VERBOSE, dump output log." + (interactive "P") + (let ((proj-root (verilog-ext-buffer-proj-root))) + (unless proj-root + (user-error "Not in a Verilog project buffer")) + (message "Starting typedef collection for %s" proj-root) + (async-start + `(lambda () + ,(async-inject-variables verilog-ext-typedef-async-inject-variables-re) + (require 'verilog-ext) + (verilog-ext-typedef-get ,@verbose)) + (lambda (result) + (message "Finished collection of typedefs!") + (setq verilog-ext-typedef-align-words-re-alist result))))) + +(defun verilog-ext-typedef-proj-hook () + "Hook to run when typedef feature is enabled." + (let ((proj (verilog-ext-buffer-proj))) + (when (and (eq major-mode 'verilog-mode) proj) + (setq-local verilog-align-typedef-regexp (verilog-ext-aget verilog-ext-typedef-align-words-re-alist proj))))) + +(defun verilog-ext-typedef-set (&optional disable) + "Enable or DISABLE typedef feature. + +INFO: Enabling this feature will override the value of +`verilog-align-typedef-regexp'." + (if disable + (remove-hook 'verilog-mode-hook #'verilog-ext-typedef-proj-hook) + (add-hook 'verilog-mode-hook #'verilog-ext-typedef-proj-hook) + (when verilog-ext-cache-enable + (setq verilog-ext-typedef-align-words-re-alist (verilog-ext-unserialize verilog-ext-typedef-cache-file))))) + +(defun verilog-ext-typedef-clear-cache (&optional all) + "Clear typedef cache files for current project. + +With prefix arg, clear cache for ALL projects." + (interactive "P") + (if (not all) + (let ((proj (verilog-ext-buffer-proj))) + (unless proj + (user-error "Not in a Verilog project buffer")) + (verilog-ext-proj-setcdr proj verilog-ext-typedef-align-words-re-alist nil) + (verilog-ext-serialize verilog-ext-typedef-align-words-re-alist verilog-ext-typedef-cache-file) + (message "[%s] Cleared typedef cache!" proj)) + (setq verilog-ext-typedef-align-words-re-alist nil) + (verilog-ext-serialize verilog-ext-typedef-align-words-re-alist verilog-ext-typedef-cache-file) + (message "Cleared all projects tags cache!"))) + (provide 'verilog-ext-typedef) diff --git a/verilog-ext-utils.el b/verilog-ext-utils.el index a85020b..18d2fbc 100644 --- a/verilog-ext-utils.el +++ b/verilog-ext-utils.el @@ -36,12 +36,64 @@ Defaults to .v, .vh, .sv and .svh." :type 'string :group 'verilog-ext) +(defcustom verilog-ext-cache-enable t + "Enable use of cache files if set to non-nil." + :type 'boolean + :group 'verilog-ext) + +(defcustom verilog-ext-cache-do-compression t + "If set to non-nil compress cache files. +Requires having \"gzip\" and \"gunzip\" in the PATH." + :type 'boolean + :group 'verilog-ext) + +(defcustom verilog-ext-project-alist nil + "`verilog-ext' project alist. + +Used for per-project functionality in `verilog-ext'. + +Its elements have the following structure: their car is a string with the name +of the project and their cdr a property list with the following properties: + - :root - base directory of the project (mandatory) + - :dirs - directories to search for project files (list of strings) + - :ignore-dirs - directories to ignore (list of strings) + - :files - files to be used for the project, keep in order for vhier + hierarchy extraction (list of strings) + - :ignore-files - files to be ignored for project (list of stings) + +Compilation: + - :compile-cmd - command used to compile current project (string) + +Vhier: + - :command-file - vhier command file + - :lib-search-path - list of dirs to look for include directories or libraries." + :type '(repeat + (list (string :tag "Project") + (plist :tag "Properties" + :options ((:root string) + (:dirs (repeat directory)) + (:ignore-dirs (repeat directory)) + (:files (repeat file)) + (:ignore-files (repeat file)) + (:compile-cmd string) + (:command-file file) + (:lib-search-path (repeat directory)))))) + :group 'verilog-ext) + + ;;;; Consts/Vars (defconst verilog-ext-keywords-re (eval-when-compile (regexp-opt verilog-keywords 'symbols))) +(defconst verilog-ext-compiler-directives + (eval-when-compile + (mapcar (lambda (directive) + (substring directive 1 nil)) + verilog-compiler-directives)) + "List of Verilog compiler directives, without the tick.") + (defconst verilog-ext-top-instantiable-re (concat "\\<\\(?1:module\\|interface\\)\\>\\(\\s-+\\\\)?\\s-+\\(?3:" verilog-identifier-sym-re "\\)")) (defconst verilog-ext-task-re @@ -87,6 +139,8 @@ type_t foo1, foo2 , foo4, foo6[], foo7 [25], foo8 ;") "\\(" verilog-ext-typedef-class-params-optional-re "\\|" verilog-ext-range-optional-re "\\)" "\\s-*\\(?2:\\<" verilog-identifier-re "\\>\\)")) +(defconst verilog-ext-cache-dir (file-name-concat user-emacs-directory "verilog-ext") + "The directory where verilog-ext cache files will be placed at.") (defconst verilog-ext-server-lsp-list '((ve-hdl-checker . ("hdl_checker" "--lsp")) @@ -99,7 +153,25 @@ type_t foo1, foo2 , foo4, foo6[], foo7 [25], foo8 ;") (defconst verilog-ext-server-lsp-ids (mapcar #'car verilog-ext-server-lsp-list)) -(defconst verilog-ext-async-inject-variables-re "\\`\\(load-path\\|buffer-file-name\\|verilog-ext-workspace-\\|verilog-ext-tags-\\|verilog-ext-hierarchy-\\|verilog-ext-typedef-\\|verilog-align-typedef-\\)") + +;;;; Macros +(defmacro verilog-ext-with-no-hooks (&rest body) + "Execute BODY without running any Verilog related hooks." + (declare (indent 0) (debug t)) + `(let ((prog-mode-hook nil) + (verilog-mode-hook '(verilog-ext-mode)) + (verilog-ts-mode-hook nil)) + ,@body)) + +(defmacro verilog-ext-proj-setcdr (proj alist value) + "Set cdr of ALIST for current PROJ to VALUE. + +ALIST is an alist and its keys are projects in `verilog-ext-project-alist' as +strings. + +If current VALUE is nil remove its key from the alist ALIST." + (declare (indent 0) (debug t)) + `(setf (alist-get ,proj ,alist nil 'remove 'string=) ,value)) ;;;; Wrappers @@ -208,9 +280,12 @@ the replacement text (see `replace-match' for more info)." (while (search-forward string endpos t) (replace-match to-string fixedcase))))) + ;;;; Dirs/files -(defun verilog-ext-dir-files (dir &optional follow-symlinks ignore-dirs) - "Find SystemVerilog files recursively on DIR. +(defun verilog-ext-dir-files (dir &optional recursive follow-symlinks ignore-dirs) + "Find SystemVerilog files on DIR. + +If RECURSIVE is non-nil find files recursively. Follow symlinks if optional argument FOLLOW-SYMLINKS is non-nil. @@ -219,9 +294,9 @@ symlink #.test.sv). Optional arg IGNORE-DIRS specifies which directories should be excluded from search." - (let* ((files (directory-files-recursively dir - verilog-ext-file-extension-re - nil nil follow-symlinks)) + (let* ((files (if recursive + (directory-files-recursively dir verilog-ext-file-extension-re nil nil follow-symlinks) + (directory-files dir t verilog-ext-file-extension-re))) (files-after-ignored (seq-filter (lambda (file) ;; Each file checks if it has its prefix in the list of ignored directories (let (ignore-file) @@ -254,10 +329,18 @@ search." (delete "" (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")))) (defun verilog-ext-file-from-filefile (filelist out-file) - "Write FILELIST to FILE as one line per file." + "Write FILELIST to OUT-FILE as one line per file." (with-temp-file out-file (insert (mapconcat #'identity filelist "\n")))) +(defun verilog-ext-expand-file-list (file-list &optional rel-dir) + "Expand files in FILE-LIST. + +Expand with respect to REL-DIR if non-nil." + (mapcar (lambda (file) + (expand-file-name file rel-dir)) + file-list)) + ;;;; File modules (defun verilog-ext-scan-buffer-modules () @@ -667,6 +750,138 @@ If on a `verilog-ts-mode' buffer, run `indent-for-tab-command' with ARG." (error "Wrong major-mode to run `verilog-ext-tab'")))) +;;;; Project +(defun verilog-ext-aget (alist key) + "Return the value in ALIST that is associated with KEY. +If KEY is not found, then nil is returned." + (cdr (assoc key alist))) + +(defun verilog-ext-buffer-proj () + "Return current buffer project if it belongs to `verilog-ext-project-alist'." + (catch 'project + (when (and buffer-file-name verilog-ext-project-alist) + (dolist (proj verilog-ext-project-alist) + (when (string-prefix-p (expand-file-name (plist-get (cdr proj) :root)) + (expand-file-name buffer-file-name)) + (throw 'project (car proj))))))) + +(defun verilog-ext-buffer-proj-root (&optional project) + "Return current buffer PROJECT root if it belongs to `verilog-ext-project-alist'." + (let ((proj (or project (verilog-ext-buffer-proj)))) + (when proj + (expand-file-name (plist-get (verilog-ext-aget verilog-ext-project-alist proj) :root))))) + +(defun verilog-ext-proj-compile-cmd (&optional project) + "Return current PROJECT compile-cmd from `verilog-ext-project-alist'." + (let ((proj (or project (verilog-ext-buffer-proj)))) + (when proj + (plist-get (verilog-ext-aget verilog-ext-project-alist proj) :compile-cmd)))) + +(defun verilog-ext-proj-command-file (&optional project) + "Return current PROJECT vhier command-file from `verilog-ext-project-alist'." + (let* ((proj (or project (verilog-ext-buffer-proj))) + (root (verilog-ext-buffer-proj-root proj)) + (cmd-file (plist-get (verilog-ext-aget verilog-ext-project-alist proj) :command-file))) + (when (and root cmd-file) + (expand-file-name cmd-file root)))) + +(defun verilog-ext-proj-lib-search-path (&optional project) + "Return current PROJECT lib search path for vhier." + (let ((proj (or project (verilog-ext-buffer-proj)))) + (when proj + (plist-get (verilog-ext-aget verilog-ext-project-alist proj) :lib-search-path)))) + +(defun verilog-ext-proj-files (&optional project) + "Return list of files for PROJECT. + +These depend on the value of property list of `verilog-ext-project-alist'. + :dirs - list of strings with optional \"-r\" to find files recursively + :ignore-dirs - list of strings of dirs to be ignored + :files - list of strings with the files + :ignore-files - list of strings of ignored files" + (let* ((proj (or project (verilog-ext-buffer-proj))) + (proj-plist (verilog-ext-aget verilog-ext-project-alist proj)) + (proj-root (when proj-plist (expand-file-name (plist-get proj-plist :root)))) + (proj-dirs (plist-get proj-plist :dirs)) + (proj-ignore-dirs (plist-get proj-plist :ignore-dirs)) + (proj-files (plist-get proj-plist :files)) + (proj-ignore-files (plist-get proj-plist :ignore-files)) + files-dirs files-all) + ;; Basic checks + (unless proj + (user-error "Not in a Verilog project buffer, check `verilog-ext-project-alist'")) + (unless proj-root + (user-error "Project root not set for project %s" proj)) + ;; Expand filenames (except for proj-dirs since they need to parse the -r recursive flag) + (when proj-ignore-dirs + (setq proj-ignore-dirs (verilog-ext-expand-file-list proj-ignore-dirs proj-root))) + (when proj-files + (setq proj-files (verilog-ext-expand-file-list proj-files proj-root))) + (when proj-ignore-files + (setq proj-ignore-files (verilog-ext-expand-file-list proj-ignore-files proj-root))) + ;; Analyze directories + (when proj-dirs + (mapc (lambda (dir) + (if (string= "-r" (car (split-string dir))) + (setq files-dirs (append files-dirs (verilog-ext-dir-files (expand-file-name (cadr (split-string dir)) proj-root) :recursive :follow-symlinks proj-ignore-dirs))) + (setq files-dirs (append files-dirs (verilog-ext-dir-files (expand-file-name dir proj-root) nil :follow-symlinks proj-ignore-dirs))))) + proj-dirs)) + ;; Merge and filter + (setq files-dirs (delete-dups files-dirs)) + (setq files-all (append files-dirs proj-files)) + (seq-filter (lambda (file) + (not (member file proj-ignore-files))) + files-all))) + +;;;; Cache +(defun verilog-ext-serialize (data filename) + "Serialize DATA to FILENAME. + +Compress cache files if gzip is available." + (let ((dir (file-name-directory filename)) + (gzip-proc-name "verilog-ext-serialize-compress") + (gzip-buf "*verilog-ext-serialize-compress*")) + (unless (file-exists-p dir) + (make-directory dir :parents)) + (if (not (file-writable-p filename)) + (message "Verilog-ext cache '%s' not writeable" filename) + (with-temp-file filename + (insert (let (print-length) (prin1-to-string data)))) + (when (and verilog-ext-cache-do-compression + (executable-find "gzip")) + ;; Async compressing + (start-process-shell-command gzip-proc-name gzip-buf (format "gzip -9f %s" filename)))))) + +(defun verilog-ext-unserialize (filename) + "Read data serialized by `verilog-ext-serialize' from FILENAME." + (let* ((compressed-filename (concat filename ".gz")) + (temp-filename (make-temp-file (concat (file-name-nondirectory filename) "-"))) + (gzip-buf "*verilog-ext-serialize-decompress*") + (decompress-cmd (format "gunzip -c %s > %s" compressed-filename temp-filename))) ; Keep original compressed file + (with-demoted-errors + "Error during file deserialization: %S" + ;; INFO: Tried using zlib with `zlib-available-p' and + ;; `zlib-decompress-region', which are faster. However, these require the + ;; buffer to be unibyte (i.e. have only ASCII characters). That could not + ;; be the case for paths with non-latin characters + ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Disabling-Multibyte.html + (if (and verilog-ext-cache-do-compression + (executable-find "gunzip") + (file-exists-p compressed-filename)) + ;; External gunzip program: This doesn't need to be asynchronous as it will only be done during initial setup + (unless (eq 0 (call-process-shell-command decompress-cmd nil gzip-buf t)) + (error "Error uncompressing %s" compressed-filename)) + (setq temp-filename filename)) + (when (file-exists-p temp-filename) + (with-temp-buffer + (insert-file-contents temp-filename) + (delete-file temp-filename) + ;; this will blow up if the contents of the file aren't lisp data structures + (read (buffer-string))))))) + + + + (provide 'verilog-ext-utils) ;;; verilog-ext-utils.el ends here diff --git a/verilog-ext-workspace.el b/verilog-ext-workspace.el deleted file mode 100644 index 6dedae8..0000000 --- a/verilog-ext-workspace.el +++ /dev/null @@ -1,493 +0,0 @@ -;;; verilog-ext-workspace.el --- Verilog-ext Workspace -*- lexical-binding: t -*- - -;; Copyright (C) 2022-2023 Gonzalo Larumbe - -;; Author: Gonzalo Larumbe -;; URL: https://github.com/gmlarumbe/verilog-ext - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Workspace utils - -;;; Code: - -(require 'make-mode) -(require 'async) -(require 'verilog-ext-tags) -(require 'verilog-ext-capf) -(require 'verilog-ext-typedef) -(require 'verilog-ext-hierarchy) -(require 'verilog-ext-template) -(require 'verilog-ext-compile) - -(defgroup verilog-ext-workspace nil - "Verilog-ext workspace." - :group 'verilog-ext) - -(defcustom verilog-ext-workspace-root-dir nil - "Workspace root directory for file indexing. -Detected automatically if set to nil." - :type 'directory - :group 'verilog-ext-workspace) - -(defcustom verilog-ext-workspace-dirs nil - "List of current workspace directories for indexing. -If set to nil default to search for current project files." - :type '(repeat directory) - :group 'verilog-ext-workspace) - -(defcustom verilog-ext-workspace-extra-files nil - "List of files besides the ones searched for in `verilog-ext-workspace-dirs'." - :type '(repeat file) - :group 'verilog-ext-workspace) - -(defcustom verilog-ext-workspace-ignore-dirs nil - "List of current workspace directories to be ignored." - :type '(repeat directory) - :group 'verilog-ext-workspace) - -(defcustom verilog-ext-workspace-ignore-files nil - "List of current workspace files to be ignored." - :type '(repeat file) - :group 'verilog-ext-workspace) - -(defcustom verilog-ext-workspace-compile-cmd nil - "The command used to perform compilation on the workspace." - :group 'verilog-ext-workspace - :type 'string) - - -(defvar verilog-ext-workspace-tags-defs-table (make-hash-table :test #'equal)) -(defvar verilog-ext-workspace-tags-refs-table (make-hash-table :test #'equal)) -(defvar verilog-ext-workspace-tags-inst-table (make-hash-table :test #'equal)) - -(defconst verilog-ext-workspace-cache-dir (file-name-concat user-emacs-directory "verilog-ext") - "The directory where Verilog-ext cache files will be placed at.") - -(defconst verilog-ext-workspace-typedefs-cache-file (file-name-concat verilog-ext-workspace-cache-dir "typedefs")) -(defconst verilog-ext-workspace-tags-defs-cache-file (file-name-concat verilog-ext-workspace-cache-dir "tags-defs")) -(defconst verilog-ext-workspace-tags-refs-cache-file (file-name-concat verilog-ext-workspace-cache-dir "tags-refs")) -(defconst verilog-ext-workspace-tags-inst-cache-file (file-name-concat verilog-ext-workspace-cache-dir "tags-inst")) -(defconst verilog-ext-workspace-hierarchy-cache-file (file-name-concat verilog-ext-workspace-cache-dir "hierarchy")) - - -(defun verilog-ext-workspace-root () - "Return directory of current workspace root." - (or verilog-ext-workspace-root-dir - (and (project-current) - (project-root (project-current))) - default-directory)) - -(defun verilog-ext-workspace-files (&optional follow-symlinks) - "Return list of current workspace files. - -Follow symlinks if optional argument FOLLOW-SYMLINKS is non-nil." - (let* ((files (if verilog-ext-workspace-dirs - (verilog-ext-dirs-files verilog-ext-workspace-dirs - follow-symlinks - verilog-ext-workspace-ignore-dirs) - (verilog-ext-dir-files (verilog-ext-workspace-root) - follow-symlinks - verilog-ext-workspace-ignore-dirs))) - (files-all (append files verilog-ext-workspace-extra-files)) - (files-after-ignored (seq-filter (lambda (file) - (not (member file verilog-ext-workspace-ignore-files))) - files-all))) - files-after-ignored)) - - -;;;; Cache -(defun verilog-ext-workspace-serialize (data filename) - "Serialize DATA to FILENAME. - -The saved data can be restored with `verilog-ext-workspace-unserialize'." - (let ((dir (file-name-directory filename))) - (unless (file-exists-p dir) - (make-directory dir :parents)) - (if (file-writable-p filename) - (with-temp-file filename - (insert (let (print-length) (prin1-to-string data)))) - (message "Verilog-ext cache '%s' not writeable" filename)))) - -(defun verilog-ext-workspace-unserialize (filename) - "Read data serialized by `verilog-ext-workspace-serialize' from FILENAME." - (with-demoted-errors - "Error during file deserialization: %S" - (when (file-exists-p filename) - (with-temp-buffer - (insert-file-contents filename) - ;; this will blow up if the contents of the file aren't - ;; lisp data structures - (read (buffer-string)))))) - -(defun verilog-ext-workspace-clear-cache () - "Clears the hard drive cache." - (interactive) - (setq verilog-ext-workspace-tags-defs-table nil) - (setq verilog-ext-workspace-tags-inst-table nil) - (setq verilog-ext-workspace-tags-refs-table nil) - (setq verilog-ext-hierarchy-current-flat-hierarchy nil) - (setq verilog-ext-typedef-align-words-re nil) - (setq verilog-align-typedef-regexp nil) - (verilog-ext-workspace-serialize verilog-ext-workspace-tags-defs-table verilog-ext-workspace-tags-defs-cache-file) - (verilog-ext-workspace-serialize verilog-ext-workspace-tags-inst-table verilog-ext-workspace-tags-inst-cache-file) - (verilog-ext-workspace-serialize verilog-ext-workspace-tags-refs-table verilog-ext-workspace-tags-refs-cache-file) - (verilog-ext-workspace-serialize verilog-ext-hierarchy-current-flat-hierarchy verilog-ext-workspace-hierarchy-cache-file) - (verilog-ext-workspace-serialize verilog-ext-typedef-align-words-re verilog-ext-workspace-typedefs-cache-file) - (message "Cleared cache!")) - - -;;;; Hierarchy -(defun verilog-ext-workspace-hierarchy-parse (&optional verbose) - "Return flat hierarchy of modules and instances of current workspace files. - -Populates `verilog-ext-hierarchy-current-flat-hierarchy' for subsequent -hierarchy extraction and display. - -With current-prefix or VERBOSE, dump output log." - (interactive "P") - (let* ((files (if verilog-ext-hierarchy-builtin-dirs - (verilog-ext-dirs-files verilog-ext-hierarchy-builtin-dirs :follow-symlinks) - (verilog-ext-workspace-files :follow-symlinks))) - (num-files (length files)) - (num-files-processed 0) - (log-file (file-name-concat verilog-ext-workspace-cache-dir "hierarchy.log")) - msg progress flat-hierarchy data) - (when verbose - (delete-file log-file)) - (dolist (file files) - (setq progress (/ (* num-files-processed 100) num-files)) - (setq msg (format "(%0d%%) [Hierarchy parsing] Processing %s" progress file)) - (when verbose - (append-to-file (concat msg "\n") nil log-file)) - (message "%s" msg) - (setq data (cond ((eq verilog-ext-hierarchy-backend 'tree-sitter) - (verilog-ext-hierarchy-tree-sitter-parse-file file)) - ((eq verilog-ext-hierarchy-backend 'builtin) - (verilog-ext-hierarchy-builtin-parse-file file)) - (t - (error "Wrong backend selected!")))) - (when data - (dolist (entry data) - (push entry flat-hierarchy))) - (setq num-files-processed (1+ num-files-processed))) - (setq verilog-ext-hierarchy-current-flat-hierarchy flat-hierarchy) - (verilog-ext-workspace-serialize verilog-ext-hierarchy-current-flat-hierarchy verilog-ext-workspace-hierarchy-cache-file) - ;; Return value for async related function - verilog-ext-hierarchy-current-flat-hierarchy)) - -(defun verilog-ext-workspace-hierarchy-parse-async (&optional verbose) - "Return flat hierarchy of modules and instances of workspace asynchronously. - -Populates `verilog-ext-hierarchy-current-flat-hierarchy' for subsequent -hierarchy extraction and display. - -With current-prefix or VERBOSE, dump output log." - (interactive "P") - (message "Starting hierarchy parsing for %s" (verilog-ext-workspace-root)) - (async-start - `(lambda () - ,(async-inject-variables verilog-ext-async-inject-variables-re) - (require 'verilog-ext) - (verilog-ext-workspace-hierarchy-parse ,@verbose)) - (lambda (result) - (message "Finished analyzing hierarchy!") - (setq verilog-ext-hierarchy-current-flat-hierarchy result)))) - - -;;;; Tags -(defun verilog-ext-workspace-get-tags (&optional verbose) - "Get tags of current workspace. -With current-prefix or VERBOSE, dump output log." - (interactive "P") - (let* ((files (verilog-ext-workspace-files :follow-symlinks)) - (num-files (length files)) - (num-files-processed 0) - (table (make-hash-table :test #'equal)) - (inst-table (make-hash-table :test #'equal)) - (log-file (file-name-concat verilog-ext-workspace-cache-dir "tags.log")) - msg progress) - (when verbose - (delete-file log-file)) - ;; Definitions - (dolist (file files) - (with-temp-buffer - (setq progress (/ (* num-files-processed 100) num-files)) - (setq msg (format "(%0d%%) [Tags collection] Processing %s" progress file)) - (when verbose - (append-to-file (concat msg "\n") nil log-file)) - (message "%s" msg) - (insert-file-contents file) - (cond (;; Tree-sitter - (eq verilog-ext-tags-backend 'tree-sitter) - (verilog-ts-mode) - (verilog-ext-tags-table-push-defs-ts :table table :inst-table inst-table :file file)) - (;; Builtin - (eq verilog-ext-tags-backend 'builtin) - (verilog-mode) - (cond (;; Top-block based-file (module/interface/package/program) - (save-excursion (verilog-re-search-forward verilog-ext-top-re nil :no-error)) - (verilog-ext-tags-table-push-defs :tag-type 'top-items :table table :file file :inst-table inst-table)) - ;; No top-blocks class-based file - ((save-excursion (verilog-ext-find-class-fwd)) - (verilog-ext-tags-table-push-defs :tag-type 'classes :table table :file file)) - ;; Default, - (t (dolist (defs '(declarations tf structs)) - (verilog-ext-tags-table-push-defs :tag-type defs :table table :file file))))) - (t ; Fallback error - (error "Wrong backend for `verilog-ext-tags-backend'"))) - (setq num-files-processed (1+ num-files-processed)))) - (setq verilog-ext-workspace-tags-defs-table table) - (setq verilog-ext-workspace-tags-inst-table inst-table) - (verilog-ext-workspace-serialize verilog-ext-workspace-tags-defs-table verilog-ext-workspace-tags-defs-cache-file) - (verilog-ext-workspace-serialize verilog-ext-workspace-tags-inst-table verilog-ext-workspace-tags-inst-cache-file) - ;; References - (setq table (make-hash-table :test #'equal)) ; Clean table - (setq num-files-processed 0) - (dolist (file files) - (with-temp-buffer - (setq progress (/ (* num-files-processed 100) num-files)) - (setq msg (format "(%0d%%) [References collection] Processing %s" progress file)) - (when verbose - (append-to-file (concat msg "\n") nil log-file)) - (message "%s" msg) - (insert-file-contents file) - (cond (;; Tree-sitter - (eq verilog-ext-tags-backend 'tree-sitter) - (verilog-ts-mode) - (verilog-ext-tags-table-push-refs-ts :table table :defs-table verilog-ext-workspace-tags-defs-table :file file)) - (;; Builtin - (eq verilog-ext-tags-backend 'builtin) - (verilog-mode) - (verilog-ext-tags-table-push-refs :table table :defs-table verilog-ext-workspace-tags-defs-table :file file)) - (t - (error "Wrong backend for `verilog-ext-tags-backend'")))) - (setq num-files-processed (1+ num-files-processed))) - (setq verilog-ext-workspace-tags-refs-table table) - (verilog-ext-workspace-serialize verilog-ext-workspace-tags-refs-table verilog-ext-workspace-tags-refs-cache-file) - (message "Finished collection tags!") - ;; Return value for async processing - `(,verilog-ext-workspace-tags-defs-table ,verilog-ext-workspace-tags-inst-table ,verilog-ext-workspace-tags-refs-table))) - -(defun verilog-ext-workspace-get-tags-async (&optional verbose) - "Create tags table asynchronously. -With current-prefix or VERBOSE, dump output log." - (interactive "P") - (message "Starting tag collection for %s" (verilog-ext-workspace-root)) - (async-start - `(lambda () - ,(async-inject-variables verilog-ext-async-inject-variables-re) - (require 'verilog-ext) - (verilog-ext-workspace-get-tags ,@verbose)) - (lambda (result) - (message "Finished collection tags!") - (setq verilog-ext-workspace-tags-defs-table (car result)) - (setq verilog-ext-workspace-tags-inst-table (cadr result)) - (setq verilog-ext-workspace-tags-refs-table (cddr result))))) - -;;;; Typedefs -(defun verilog-ext-workspace-typedef-batch-update (files &optional verbose) - "Scan all (System)Verilog FILES and udpate typedef list. - -It will return the updated value of `verilog-ext-typedef-align-words', -which can be used later along with `verilog-regexp-words' to update the variable -`verilog-align-typedef-regexp'. This enables the fontification and alignment of -user typedefs. - -If optional arg VERBOSE is enabled, dump output to a logfile for potential debug -in corresponding async function." - (let ((num-files (length files)) - (num-files-processed 0) - (log-file (file-name-concat verilog-ext-workspace-cache-dir "typedefs.log")) - msg progress) - (setq verilog-ext-typedef-align-words nil) ; Reset value - (when verbose - (delete-file log-file)) - (dolist (file files) - (setq progress (/ (* num-files-processed 100) num-files)) - (setq msg (format "(%0d%%) [Typedef collection] Processing %s" progress file)) - (when verbose - (append-to-file (concat msg "\n") nil log-file)) - (message "%s" msg) - (with-temp-buffer - (insert-file-contents file) - (verilog-mode) - (verilog-ext-typedef--var-decl-update)) - (setq num-files-processed (1+ num-files-processed))) - ;; Postprocess obtained results (remove keywords and generic types that were uppercase) - (mapc (lambda (elm) - (when (member elm verilog-keywords) - (delete elm verilog-ext-typedef-align-words))) - verilog-ext-typedef-align-words) - (let ((case-fold-search nil)) - (setq verilog-ext-typedef-align-words (seq-remove (lambda (s) - (not (string-match-p "[[:lower:]]" s))) - verilog-ext-typedef-align-words))) - ;; Store results - (when verilog-ext-typedef-align-words - (setq verilog-ext-typedef-align-words-re (verilog-regexp-words verilog-ext-typedef-align-words)) - (setq verilog-align-typedef-regexp verilog-ext-typedef-align-words-re)) - (verilog-ext-workspace-serialize verilog-ext-typedef-align-words-re verilog-ext-workspace-typedefs-cache-file) - ;; Return value for async processing - verilog-ext-typedef-align-words-re)) - -(defun verilog-ext-workspace-typedef-update (&optional verbose) - "Update typedef list of current workspace. -With current-prefix or VERBOSE, dump output log." - (interactive "P") - (verilog-ext-workspace-typedef-batch-update (verilog-ext-workspace-files :follow-symlinks) verbose)) - -(defun verilog-ext-workspace-typedef-update-async (&optional verbose) - "Update typedef list of current workspace asynchronously. -With current-prefix or VERBOSE, dump output log." - (interactive "P") - (message "Starting typedef collection for %s" (verilog-ext-workspace-root)) - (async-start - `(lambda () - ,(async-inject-variables verilog-ext-async-inject-variables-re) - (require 'verilog-ext) - (verilog-ext-workspace-typedef-batch-update (verilog-ext-workspace-files :follow-symlinks) ,@verbose)) - (lambda (result) - (message "Finished collection of typedefs!") - (setq verilog-ext-typedef-align-words-re result) - (setq verilog-align-typedef-regexp verilog-ext-typedef-align-words-re)))) - - -;;;; Completion-at-point -(defun verilog-ext-workspace-capf-annotation-function (cand) - "Workspace completion annotation function for candidate CAND." - (verilog-ext-capf-annotation-function cand - verilog-ext-workspace-tags-defs-table - verilog-ext-workspace-tags-inst-table)) - -(defun verilog-ext-workspace-capf () - "Verilog-ext `completion-at-point' function. -Complete with identifiers of current workspace." - (interactive) - (verilog-ext-capf :defs-table verilog-ext-workspace-tags-defs-table - :inst-table verilog-ext-workspace-tags-inst-table - :refs-table verilog-ext-workspace-tags-refs-table - :annotation-fn #'verilog-ext-workspace-capf-annotation-function)) - -(defun verilog-ext-workspace-capf-set (&optional disable) - "Enable or DISABLE builtin capf function. -Replace already existing `verilog-mode' `verilog-completion-at-point'." - (if disable - (progn - (remove-hook 'completion-at-point-functions #'verilog-ext-workspace-capf :local) - (add-hook 'completion-at-point-functions #'verilog-completion-at-point :local)) - ;; Else - (add-hook 'completion-at-point-functions #'verilog-ext-workspace-capf nil :local) - (remove-hook 'completion-at-point-functions #'verilog-completion-at-point :local))) - - -;;;; Makefile -(defun verilog-ext-workspace-makefile-create () - "Create Iverilog/verilator Yasnippet based Makefile. -Create it only if in a project and the Makefile does not already exist." - (interactive) - (let ((project-root (verilog-ext-workspace-root)) - file) - (if project-root - (if (file-exists-p (setq file (file-name-concat project-root "Makefile"))) - (error "File %s already exists!" file) - (find-file file) - (verilog-ext-template-insert-yasnippet "verilog")) - (error "Not in a project!")))) - -(defun verilog-ext-workspace-makefile-compile () - "Prompt to available Makefile targets and compile. -Compiles them with various verilog regexps." - (interactive) - (let ((makefile (file-name-concat (verilog-ext-workspace-root) "Makefile")) - (makefile-need-target-pickup t) ; Force refresh of makefile targets - target cmd) - (unless (file-exists-p makefile) - (error "%s does not exist!" makefile)) - (with-temp-buffer - (insert-file-contents makefile) - (makefile-pickup-targets) - (setq target (completing-read "Target: " makefile-target-table))) - (setq cmd (concat "cd " (verilog-ext-workspace-root) " && make " target)) - (compile cmd))) - - -;;;; Compilation -(defun verilog-ext-workspace-compile () - "Compile using command of `verilog-ext-workspace-compile-cmd'. -Depending on the command, different syntax highlight will be applied. -The function will detect any of the supported compilation error parsers -and will set the appropriate mode." - (interactive) - (unless verilog-ext-workspace-compile-cmd - (error "You first need to set `verilog-ext-workspace-compile-cmd'")) - (let* ((cmd-list (split-string verilog-ext-workspace-compile-cmd)) - (cmd-args (cdr cmd-list)) - (cmd-bin (car cmd-list)) - (fn (pcase cmd-bin - ("verilator" #'verilog-ext-compile-verilator) - ("iverilog" #'verilog-ext-compile-iverilog) - ("slang" #'verilog-ext-compile-slang) - ("svlint" #'verilog-ext-compile-svlint) - ("surelog" #'verilog-ext-compile-surelog) - ("verible-verilog-lint" #'verilog-ext-compile-verible) - (_ #'compile))) - (cmd-processed (cond (;; For svlint, make sure the -1 arg is present - (string= cmd-bin "svlint") - (if (member "-1" cmd-args) - verilog-ext-workspace-compile-cmd - (mapconcat #'identity `(,cmd-bin "-1" ,@cmd-args) " "))) - ;; For slang make sure that there is no colored output - ((string= cmd-bin "slang") - (if (member "--color-diagnostics=false" cmd-args) - verilog-ext-workspace-compile-cmd - (mapconcat #'identity `(,cmd-bin "--color-diagnostics=false" ,@cmd-args) " "))) - ;; For the rest use the provided command - (t - verilog-ext-workspace-compile-cmd))) - (cmd (concat "cd " (verilog-ext-workspace-root) " && " cmd-processed))) - (funcall fn cmd))) - -;;;; Jump-to-parent module -(defun verilog-ext-workspace-jump-to-parent-module () - "Find current module/interface instantiations via `ag'/`rg' in current workspace." - (interactive) - (verilog-ext-jump-to-parent-module (verilog-ext-workspace-root))) - - -;;;; Setup -(defun verilog-ext-workspace-hierarchy-setup () - "Setup workspace builtin hierarchy analysis." - (setq verilog-ext-hierarchy-current-flat-hierarchy (verilog-ext-workspace-unserialize verilog-ext-workspace-hierarchy-cache-file))) - -(defun verilog-ext-workspace-tags-table-setup () - "Setup workspace tags table feature for `xref' and `capf'." - (setq verilog-ext-workspace-tags-defs-table (verilog-ext-workspace-unserialize verilog-ext-workspace-tags-defs-cache-file)) - (setq verilog-ext-workspace-tags-inst-table (verilog-ext-workspace-unserialize verilog-ext-workspace-tags-inst-cache-file)) - (setq verilog-ext-workspace-tags-refs-table (verilog-ext-workspace-unserialize verilog-ext-workspace-tags-refs-cache-file))) - -(defun verilog-ext-workspace-typedefs-setup () - "Setup workspace typedef feature. -INFO: Enabling this feature will override the value of -`verilog-align-typedef-regexp'." - (setq verilog-ext-typedef-align-words-re (verilog-ext-workspace-unserialize verilog-ext-workspace-typedefs-cache-file)) - (setq verilog-align-typedef-regexp verilog-ext-typedef-align-words-re)) - - - -(provide 'verilog-ext-workspace) - -;;; verilog-ext-workspace.el ends here diff --git a/verilog-ext-xref.el b/verilog-ext-xref.el index 2788861..202410d 100644 --- a/verilog-ext-xref.el +++ b/verilog-ext-xref.el @@ -24,29 +24,29 @@ ;;; Code: -(require 'verilog-ext-workspace) +(require 'verilog-ext-tags) (defgroup verilog-ext-xref nil "Verilog-ext xref customization." :group 'verilog-ext) -(defcustom verilog-ext-xref-match-face 'font-lock-warning-face +(defface verilog-ext-xref-match-face '((t :inherit match)) "Verilog-ext face used to highlight matches in xref." - :type '(repeat string) :group 'verilog-ext-xref) (defun verilog-ext-xref--find-symbol (symbol type) "Return list of TYPE xref objects for SYMBOL." - (let* ((table (cond ((eq type 'def) - verilog-ext-workspace-tags-defs-table) + (let* ((proj (verilog-ext-buffer-proj)) + (table (cond ((eq type 'def) + (verilog-ext-aget verilog-ext-tags-defs-table proj)) ((eq type 'ref) - verilog-ext-workspace-tags-refs-table) + (verilog-ext-aget verilog-ext-tags-refs-table proj)) (t (error "Wrong table")))) (table-entry (if table (gethash symbol table) - (error "Tags table empty. Run first `verilog-ext-workspace-get-tags' or `verilog-ext-workspace-get-tags-async'"))) + (error "Tags table empty. Run first `verilog-ext-tags-get' or `verilog-ext-tags-get-async'"))) (entry-locs (plist-get table-entry :locs)) (entry-locs-grouped (seq-group-by (lambda (loc) (equal buffer-file-name (plist-get loc :file))) @@ -59,9 +59,9 @@ (dolist (loc entry-locs-sorted) (setq file (plist-get loc :file)) (setq line (plist-get loc :line)) - (setq column nil) + (setq column (plist-get loc :col)) (setq desc (replace-regexp-in-string (concat "\\_<" symbol "\\_>") - (propertize symbol 'face verilog-ext-xref-match-face) + (propertize symbol 'face 'verilog-ext-xref-match-face) (plist-get loc :desc) :fixedcase)) (push (xref-make desc (xref-make-file-location file line column)) xref-entries))) @@ -69,7 +69,7 @@ (defun verilog-ext-xref-backend () "Verilog-ext backend for Xref." - (when (verilog-ext-workspace-root) + (when (verilog-ext-buffer-proj) 'verilog-ext)) (cl-defmethod xref-backend-identifier-at-point ((_backend (eql verilog-ext))) diff --git a/verilog-ext.el b/verilog-ext.el index 49b8c81..0317b7d 100644 --- a/verilog-ext.el +++ b/verilog-ext.el @@ -40,7 +40,7 @@ ;; - Improve `imenu' entries: detect instances, classes and methods ;; - Enhanced support for `which-func' ;; - Code folding via `hideshow' -;; - Workspace tags, typedef analysis and caching +;; - Project tags, typedef analysis and caching ;; - Time-stamp auto-configuration ;; - Convert block end comments to names ;; - Port connections utilities @@ -75,7 +75,7 @@ "Which Verilog-ext features to enable." :type '(set (const :tag "Improved syntax highlighting via `font-lock'." font-lock) - (const :tag "Xref backend to navigate definitions/references in current workspace." + (const :tag "Xref backend to navigate definitions/references in current project." xref) (const :tag "Completion at point builtin function with dot and scope completion." capf) @@ -103,7 +103,7 @@ which-func) (const :tag "`hideshow' configuration." hideshow) - (const :tag "Scan typedefs and classes of current workspace for syntax highlighting and alignment." + (const :tag "Scan typedefs and classes of current project for syntax highlighting and alignment." typedefs) (const :tag "`time-stamp' configuration." time-stamp) @@ -146,7 +146,6 @@ FEATURES can be a single feature or a list of features." (require 'verilog-ext-hierarchy) (require 'verilog-ext-beautify) (require 'verilog-ext-template) -(require 'verilog-ext-workspace) (require 'verilog-ext-xref) (require 'verilog-ext-formatter) (require 'verilog-ext-flycheck) @@ -162,7 +161,7 @@ FEATURES can be a single feature or a list of features." (verilog-ext-when-feature 'formatter (define-key map (kbd "C-c C-l") 'verilog-ext-formatter-run)) (verilog-ext-when-feature 'compilation - (define-key map (kbd "C-c ") 'verilog-ext-workspace-compile) + (define-key map (kbd "C-c ") 'verilog-ext-compile) (define-key map (kbd "C-c C-p") 'verilog-ext-preprocess)) (verilog-ext-when-feature 'flycheck (define-key map (kbd "C-c C-f") 'verilog-ext-flycheck-mode)) @@ -173,27 +172,31 @@ FEATURES can be a single feature or a list of features." (verilog-ext-when-feature 'beautify (define-key map (kbd "C-M-i") 'verilog-ext-beautify-block-at-point-indent) (define-key map (kbd "C-c C-b") 'verilog-ext-beautify-module-at-point)) + (verilog-ext-when-feature 'xref + (define-key map (kbd "C-c C-u") 'verilog-ext-tags-get)) (verilog-ext-when-feature 'navigation - (define-key map (kbd "C-M-a") 'verilog-ext-nav-beg-of-defun-dwim) - (define-key map (kbd "C-M-e") 'verilog-ext-nav-end-of-defun-dwim) - (define-key map (kbd "C-M-d") 'verilog-ext-nav-down-dwim) - (define-key map (kbd "C-M-u") 'verilog-ext-nav-up-dwim) - (define-key map (kbd "C-M-p") 'verilog-ext-nav-prev-dwim) - (define-key map (kbd "C-M-n") 'verilog-ext-nav-next-dwim) + (when (eq major-mode 'verilog-mode) + (define-key map (kbd "C-M-a") 'verilog-ext-nav-beg-of-defun-dwim) + (define-key map (kbd "C-M-e") 'verilog-ext-nav-end-of-defun-dwim) + (define-key map (kbd "C-M-d") 'verilog-ext-nav-down-dwim) + (define-key map (kbd "C-M-u") 'verilog-ext-nav-up-dwim) + (define-key map (kbd "C-M-p") 'verilog-ext-nav-prev-dwim) + (define-key map (kbd "C-M-n") 'verilog-ext-nav-next-dwim)) (define-key map (kbd "C-c M-.") 'verilog-ext-jump-to-module-at-point-def) (define-key map (kbd "C-c M-?") 'verilog-ext-jump-to-module-at-point-ref) - (define-key map (kbd "C-M-.") 'verilog-ext-workspace-jump-to-parent-module)) + (define-key map (kbd "C-M-.") 'verilog-ext-jump-to-parent-module)) (verilog-ext-when-feature 'ports (define-key map (kbd "C-c C-c c") 'verilog-ext-ports-clean-blanks) (define-key map (kbd "C-c C-c t") 'verilog-ext-ports-toggle-connect) - (define-key map (kbd "C-c C-c r") 'verilog-ext-ports-connect-recursively) + (define-key map (kbd "C-c C-c r") 'verilog-ext-ports-connect-recursively)) ;; Misc - (define-key map (kbd "TAB") 'verilog-ext-tab) - (define-key map (kbd "M-d") 'verilog-ext-kill-word) - (define-key map (kbd "M-f") 'verilog-ext-forward-word) - (define-key map (kbd "M-b") 'verilog-ext-backward-word) - (define-key map (kbd "C-") 'verilog-ext-backward-kill-word) - (define-key map (kbd "M-DEL") 'verilog-ext-backward-kill-word)) + (when (eq major-mode 'verilog-mode) + (define-key map (kbd "TAB") 'verilog-ext-tab) + (define-key map (kbd "M-d") 'verilog-ext-kill-word) + (define-key map (kbd "M-f") 'verilog-ext-forward-word) + (define-key map (kbd "M-b") 'verilog-ext-backward-word) + (define-key map (kbd "C-") 'verilog-ext-backward-kill-word) + (define-key map (kbd "M-DEL") 'verilog-ext-backward-kill-word)) map) "Key map for the `verilog-ext'.") @@ -209,10 +212,9 @@ FEATURES can be a single feature or a list of features." (verilog-ext-when-feature 'template (verilog-ext-template-add-snippets)) (verilog-ext-when-feature 'typedefs - (verilog-ext-workspace-typedefs-setup)) + (verilog-ext-typedef-set)) (verilog-ext-when-feature 'hierarchy - (verilog-ext-hierarchy-setup) - (verilog-ext-workspace-hierarchy-setup)) + (verilog-ext-hierarchy-setup)) (verilog-ext-when-feature 'formatter (verilog-ext-formatter-setup)) (verilog-ext-when-feature 'flycheck @@ -223,8 +225,7 @@ FEATURES can be a single feature or a list of features." (verilog-ext-lsp-setup) (verilog-ext-lsp-set-server verilog-ext-lsp-mode-default-server)) (verilog-ext-when-feature '(capf xref) - (verilog-ext-tags-setup) - (verilog-ext-workspace-tags-table-setup)) + (verilog-ext-tags-setup)) ;; Jump to parent module ag/ripgrep hooks (add-hook 'ag-search-finished-hook #'verilog-ext-navigation-ag-rg-hook) (add-hook 'ripgrep-search-finished-hook #'verilog-ext-navigation-ag-rg-hook)) @@ -265,7 +266,7 @@ FEATURES can be a single feature or a list of features." (verilog-ext-when-feature 'time-stamp (verilog-ext-time-stamp-mode)) (verilog-ext-when-feature 'capf - (verilog-ext-workspace-capf-set)) + (verilog-ext-capf-set)) (verilog-ext-when-feature 'xref (verilog-ext-xref-set)) ;; `verilog-mode'-only customization (exclude `verilog-ts-mode') @@ -293,10 +294,12 @@ FEATURES can be a single feature or a list of features." (verilog-ext-block-end-comments-to-names-mode -1)) (verilog-ext-when-feature 'time-stamp (verilog-ext-time-stamp-mode -1)) + (verilog-ext-when-feature 'typedef + (verilog-ext-typedef-set :disable)) (verilog-ext-when-feature 'xref (verilog-ext-xref-set :disable)) (verilog-ext-when-feature 'capf - (verilog-ext-workspace-capf-set :disable)))) + (verilog-ext-capf-set :disable)))) ;;; Provide