forked from gmlarumbe/verilog-ext
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverilog-ext-imenu.el
169 lines (147 loc) · 7.6 KB
/
verilog-ext-imenu.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
;;; verilog-ext-imenu.el --- Verilog-ext Imenu -*- lexical-binding: t -*-
;; Copyright (C) 2022-2023 Gonzalo Larumbe
;; Author: Gonzalo Larumbe <[email protected]>
;; 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Improved `imenu' functionality
;; - Show instances of current module
;; - Show classes and their methods
;;; Code:
(require 'imenu)
(require 'verilog-ext-nav)
(defconst verilog-ext-imenu-top-re "^\\s-*\\(?1:connectmodule\\|m\\(?:odule\\|acromodule\\)\\|p\\(?:rimitive\\|rogram\\|ackage\\)\\)\\(\\s-+automatic\\)?\\s-+\\(?2:[a-zA-Z0-9_.:]+\\)")
(defconst verilog-ext-imenu-localparam-re "^\\s-*localparam\\(?1:\\s-+\\(logic\\|bit\\|int\\|integer\\)\\s-*\\(\\[.*\\]\\)?\\)?\\s-+\\(?2:[a-zA-Z0-9_.:]+\\)")
(defconst verilog-ext-imenu-define-re "^\\s-*`define\\s-+\\([a-zA-Z0-9_.:]+\\)")
(defconst verilog-ext-imenu-assign-re "^\\s-*assign\\s-+\\([a-zA-Z0-9_.:]+\\)")
(defconst verilog-ext-imenu-generate-re "^\\s-*generate[ \t\n]*\\(?1:.*\\)")
(defconst verilog-ext-imenu-always-re "^\\s-*always\\(_ff\\|_comb\\|_latch\\)?\\s-*\\(.*\\)\\(begin\\)?[ |\n]*\\(.*\\)")
(defconst verilog-ext-imenu-initial-re "^\\s-*initial\\s-+\\(.*\\)\\(begin\\)?[ |\n]*\\(.*\\)")
(defvar verilog-ext-imenu-generic-expression
`(;; Search by regexp
("*Top*" ,verilog-ext-imenu-top-re 2)
("*Localparams*" ,verilog-ext-imenu-localparam-re 2)
("*Defines*" ,verilog-ext-imenu-define-re 1)
("*Assigns*" ,verilog-ext-imenu-assign-re 1)
("*Generates*" ,verilog-ext-imenu-generate-re 1)
("*Always blocks*" ,verilog-ext-imenu-always-re 4)
("*Initial blocks*" ,verilog-ext-imenu-initial-re 3)))
(defun verilog-ext-imenu-find-module-instance-index ()
"Create imenu entries of modules and instances.
Placing this outside of `imenu--generic-function' avoids running it if
`which-func' is enabled. It also allows to conditionally disable the index
building if file cannot contain instances."
(save-excursion
(goto-char (point-max))
(let ((group-name "*Instances*")
index)
(when verilog-ext-file-allows-instances
(while (verilog-ext-find-module-instance-bwd)
;; Use capture group index 3 to get instance name
(push (cons (match-string-no-properties 1) (line-beginning-position)) index))
(when index
(list (cons group-name index)))))))
(defun verilog-ext-imenu-find-tf-outside-class-index ()
"Create entries of tasks and functions outside classes.
Group the ones that belong to same external method definitions."
(save-excursion
(goto-char (point-max))
(let ((tf-group-name "*Task/Func*")
index node data pos name class-name)
(while (setq data (verilog-ext-find-function-task-bwd))
(unless (verilog-ext-point-inside-block 'class)
;; Get information from the subroutine
(setq pos (alist-get 'pos data))
(setq name (alist-get 'name data))
(setq class-name (alist-get 'class-name data))
;; Add element to the tree
(setq node (cons name pos))
(if class-name
;; Externally declared methods
(if (not (assoc class-name index))
(setq index `((,class-name ,node)))
;; Add to existing class
(setf (cdr (assoc class-name index)) (cons node (cdr (assoc class-name index)))))
;; Non-methods
(if (not (assoc tf-group-name index))
(setq index `((,tf-group-name ,node)))
(setf (cdr (assoc tf-group-name index)) (cons node (cdr (assoc tf-group-name index))))))))
index)))
(defun verilog-ext-imenu--format-class-item-label (type name modifiers)
"Return Imenu label for single node using TYPE, NAME and MODIFIERS."
(let* ((prop-name (propertize name 'face '(:foreground "goldenrod" :weight bold)))
(short-type (pcase type
("task" " [T]")
("function" " [F]")
("class" "")
(_ type)))
(modifiers-string (mapconcat (lambda (x) (substring-no-properties x 0 1)) modifiers ""))
(prop-modifiers (if (string= modifiers-string "")
""
(propertize (concat " (" modifiers-string ")") 'face 'italic))))
(format "%s%s%s" prop-name short-type prop-modifiers)))
(defun verilog-ext-imenu--class-put-parent (type name pos tree modifiers)
"Create parent node (classes).
Use TYPE, NAME and MODIFIERS to format the node name.
Create cons cell with the label and the POS if it is a leaf node.
Otherwsise create the cons cell with the label and the TREE."
(let* ((label (verilog-ext-imenu--format-class-item-label type name modifiers)))
(if (not tree)
(cons label pos)
(cons label tree))))
(defun verilog-ext-imenu--build-class-tree (&optional tree)
"Build the imenu class alist TREE recursively.
Find recursively tasks and functions inside classes."
(save-restriction
(narrow-to-region (point-min) (point))
(let* ((data (and (verilog-ext-find-class-bwd)
(verilog-ext-forward-sexp)
(verilog-ext-find-function-task-class-bwd)))
(pos (when (alist-get 'pos data)
(save-excursion
(goto-char (alist-get 'pos data))
(line-beginning-position))))
(type (alist-get 'type data))
(name (alist-get 'name data))
(modifiers (alist-get 'modifiers data))
(label (when name
(verilog-ext-imenu--format-class-item-label type name modifiers))))
(cond ((not pos)
nil)
((verilog-ext-looking-at-class-declaration)
(verilog-ext-imenu--class-put-parent type name pos tree modifiers))
((or (string= type "function")
(string= type "task"))
(verilog-ext-imenu--build-class-tree (cons (cons label pos) tree)))
(t ; Build subtrees recursively
(verilog-ext-imenu--build-class-tree
(cons (verilog-ext-imenu--build-class-tree
(list (cons label pos))) tree)))))))
(defun verilog-ext-imenu-classes-index ()
"Create entries of tasks and functions within classes."
(save-excursion
(goto-char (point-max))
(let (index tree)
(while (setq tree (verilog-ext-imenu--build-class-tree))
(setq index (cons tree index)))
(when index
(list (cons "*Classes*" index))))))
(defun verilog-ext-imenu-index ()
"Index builder function for Verilog Imenu.
Makes use of `verilog-ext-imenu-generic-expression' for everything but classes
and methods. These are collected with `verilog-ext-imenu-classes-index'."
(append (verilog-ext-imenu-find-module-instance-index)
(verilog-ext-imenu-classes-index)
(verilog-ext-imenu-find-tf-outside-class-index)
(imenu--generic-function verilog-ext-imenu-generic-expression)))
(provide 'verilog-ext-imenu)
;;; verilog-ext-imenu.el ends here