Skip to content

Commit 037e023

Browse files
committed
feat: add navigation
1 parent 3e159b4 commit 037e023

File tree

3 files changed

+171
-2
lines changed

3 files changed

+171
-2
lines changed

netlinx-mode-navigation.el

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
;;; netlinx-mode-navigation.el --- Navigation support for netlinx-mode -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2024 Norgate AV
4+
5+
;;; Commentary:
6+
7+
;; This file provides navigation and imenu support for netlinx-mode using
8+
;; tree-sitter. It defines which syntax nodes are considered "defuns" for
9+
;; navigation purposes and how to extract names for imenu.
10+
11+
;;; Code:
12+
13+
(require 'treesit)
14+
15+
(declare-function treesit-node-type "treesit.c")
16+
(declare-function treesit-node-child-by-field-name "treesit.c")
17+
(declare-function treesit-node-text "treesit.c")
18+
(declare-function treesit-node-child "treesit.c")
19+
(declare-function treesit-filter-child "treesit.c")
20+
21+
(defun netlinx-mode--defun-name (node)
22+
"Return the defun name of NODE.
23+
Return nil if there is no name or if NODE is not a defun node."
24+
(pcase (treesit-node-type node)
25+
;; Function definitions: define_function
26+
("define_function"
27+
(when-let* ((func-def (netlinx-mode--child-of-type node "function_definition"))
28+
(name-node (treesit-node-child-by-field-name func-def "name")))
29+
(treesit-node-text name-node t)))
30+
31+
;; Module definitions: define_module
32+
("define_module"
33+
(when-let* ((module-def (netlinx-mode--child-of-type node "module_definition"))
34+
(name-node (treesit-node-child-by-field-name module-def "module_name")))
35+
(treesit-node-text name-node t)))
36+
37+
;; Event declarators
38+
((or "button_event_declarator"
39+
"channel_event_declarator"
40+
"data_event_declarator"
41+
"level_event_declarator"
42+
"custom_event_declarator"
43+
"timeline_event_declarator")
44+
;; For events, use the first child as the name (the event target/device)
45+
(when-let ((first-child (treesit-node-child node 0)))
46+
(treesit-node-text first-child t)))
47+
48+
;; Sections (DEFINE_DEVICE, DEFINE_VARIABLE, etc.)
49+
("section"
50+
(treesit-node-text node t))
51+
52+
;; Struct definitions
53+
("struct_specifier"
54+
(when-let ((name-node (treesit-node-child-by-field-name node "name")))
55+
(treesit-node-text name-node t)))))
56+
57+
(defun netlinx-mode--child-of-type (node type)
58+
"Return first child of NODE that has TYPE."
59+
(car (treesit-filter-child
60+
node
61+
(lambda (child)
62+
(equal (treesit-node-type child) type)))))
63+
64+
;; Define which node types are considered "defuns" for navigation
65+
(defvar netlinx-mode--defun-type-regexp
66+
(rx (or "define_function"
67+
"define_module"
68+
"button_event_declarator"
69+
"channel_event_declarator"
70+
"data_event_declarator"
71+
"level_event_declarator"
72+
"custom_event_declarator"
73+
"timeline_event_declarator"
74+
"section"
75+
"struct_specifier"))
76+
"Regexp matching node types that are considered defuns in NetLinx.")
77+
78+
;; Define imenu categories and their corresponding node types
79+
(defvar netlinx-mode--imenu-settings
80+
'(("Function" "\\`define_function\\'" nil nil)
81+
("Module" "\\`define_module\\'" nil nil)
82+
("Button Event" "\\`button_event_declarator\\'" nil nil)
83+
("Channel Event" "\\`channel_event_declarator\\'" nil nil)
84+
("Data Event" "\\`data_event_declarator\\'" nil nil)
85+
("Level Event" "\\`level_event_declarator\\'" nil nil)
86+
("Custom Event" "\\`custom_event_declarator\\'" nil nil)
87+
("Timeline Event" "\\`timeline_event_declarator\\'" nil nil)
88+
("Section" "\\`section\\'" nil nil)
89+
("Struct" "\\`struct_specifier\\'" nil nil))
90+
"Imenu settings for netlinx-mode.
91+
Each entry is (CATEGORY REGEXP PRED NAME-FN) as expected by
92+
`treesit-simple-imenu-settings'.")
93+
94+
(provide 'netlinx-mode-navigation)
95+
96+
;;; netlinx-mode-navigation.el ends here

netlinx-mode.el

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
;; Load indentation settings
5454
(require 'netlinx-mode-indent)
5555

56+
;; Load navigation and imenu settings
57+
(require 'netlinx-mode-navigation)
58+
5659
;; Configuration for tree-sitter grammar
5760
(defcustom netlinx-mode-grammar-location
5861
"https://github.com/Norgate-AV/tree-sitter-netlinx"
@@ -75,11 +78,23 @@
7578
:group 'netlinx)
7679

7780
;; Path to NetLinx Keyword Help file
81+
;; "C:\Program Files (x86)\AMX Control Disc\NetLinx Studio 4\NetLinx-Keywords.chm"
7882
(defcustom netlinx-mode-help-file
7983
(when (eq system-type 'windows-nt)
80-
"C:/Program Files (x86)/AMX Control Disk/NetLinx Studio/NetLinxKeywords.chm")
84+
"C:/Program Files (x86)/AMX Control Disc/NetLinx Studio 4/NetLinx-Keywords.chm")
8185
"Path to the NetLinx Keyword Help CHM file.
82-
If set, enables quick access to NetLinx documentation via \\[netlinx-open-help]."
86+
If set, enables quick access to NetLinx keyword documentation via \\[netlinx-open-keyword-help]."
87+
:type '(choice (const :tag "Not configured" nil)
88+
(file :tag "Path to CHM file"))
89+
:group 'netlinx)
90+
91+
;; Path to Standard NetLinx API (SNAPI) Help file
92+
;; "C:\Program Files (x86)\AMX Control Disc\NetLinx Studio 4\Standard_NetLinx_API.chm"
93+
(defcustom netlinx-mode-help-file
94+
(when (eq system-type 'windows-nt)
95+
"C:/Program Files (x86)/AMX Control Disc/NetLinx Studio 4/Standard_NetLinx_API.chm")
96+
"Path to the NetLinx SNAPI Help CHM file.
97+
If set, enables quick access to NetLinx SNAPI documentation via \\[netlinx-open-snapi-help]."
8398
:type '(choice (const :tag "Not configured" nil)
8499
(file :tag "Path to CHM file"))
85100
:group 'netlinx)
@@ -194,6 +209,13 @@ The file path is configured via `netlinx-mode-help-file'."
194209
(setq-local electric-indent-chars
195210
(append "{}():;," electric-indent-chars))
196211

212+
;; Navigation
213+
(setq-local treesit-defun-type-regexp netlinx-mode--defun-type-regexp)
214+
(setq-local treesit-defun-name-function #'netlinx-mode--defun-name)
215+
216+
;; Imenu
217+
(setq-local treesit-simple-imenu-settings netlinx-mode--imenu-settings)
218+
197219
;; Setup font-lock
198220
(setq-local treesit-font-lock-settings netlinx-mode--font-lock-settings)
199221
(setq-local treesit-font-lock-level 4)

test/netlinx-mode-test.el

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,58 @@
309309
(should (local-variable-p 'electric-pair-inhibit-predicate))
310310
(should (functionp electric-pair-inhibit-predicate)))))
311311

312+
;;; Navigation and Imenu Tests
313+
314+
(ert-deftest netlinx-mode-test-navigation-defun-type-configured ()
315+
"Test that treesit-defun-type-regexp is configured."
316+
(with-temp-buffer
317+
(cl-letf (((symbol-function 'netlinx-mode--ensure-grammar) #'ignore)
318+
((symbol-function 'netlinx-mode--setup-snippets) #'ignore)
319+
((symbol-function 'treesit-ready-p) (lambda (&rest _) t))
320+
((symbol-function 'treesit-parser-create) #'ignore)
321+
((symbol-function 'treesit-major-mode-setup) #'ignore))
322+
(netlinx-mode)
323+
(should (local-variable-p 'treesit-defun-type-regexp))
324+
(should (stringp treesit-defun-type-regexp))
325+
;; Check that the regexp is set to the expected variable
326+
(should (equal treesit-defun-type-regexp netlinx-mode--defun-type-regexp)))))
327+
328+
(ert-deftest netlinx-mode-test-navigation-defun-name-function-configured ()
329+
"Test that treesit-defun-name-function is configured."
330+
(with-temp-buffer
331+
(cl-letf (((symbol-function 'netlinx-mode--ensure-grammar) #'ignore)
332+
((symbol-function 'netlinx-mode--setup-snippets) #'ignore)
333+
((symbol-function 'treesit-ready-p) (lambda (&rest _) t))
334+
((symbol-function 'treesit-parser-create) #'ignore)
335+
((symbol-function 'treesit-major-mode-setup) #'ignore))
336+
(netlinx-mode)
337+
(should (local-variable-p 'treesit-defun-name-function))
338+
(should (functionp treesit-defun-name-function))
339+
(should (eq treesit-defun-name-function #'netlinx-mode--defun-name)))))
340+
341+
(ert-deftest netlinx-mode-test-imenu-configured ()
342+
"Test that imenu settings are configured."
343+
(with-temp-buffer
344+
(cl-letf (((symbol-function 'netlinx-mode--ensure-grammar) #'ignore)
345+
((symbol-function 'netlinx-mode--setup-snippets) #'ignore)
346+
((symbol-function 'treesit-ready-p) (lambda (&rest _) t))
347+
((symbol-function 'treesit-parser-create) #'ignore)
348+
((symbol-function 'treesit-major-mode-setup) #'ignore))
349+
(netlinx-mode)
350+
(should (local-variable-p 'treesit-simple-imenu-settings))
351+
(should (listp treesit-simple-imenu-settings))
352+
;; Should have at least several categories
353+
(should (>= (length treesit-simple-imenu-settings) 5)))))
354+
355+
(ert-deftest netlinx-mode-test-navigation-variables-defined ()
356+
"Test that navigation-related variables are defined."
357+
(should (boundp 'netlinx-mode--defun-type-regexp))
358+
(should (boundp 'netlinx-mode--imenu-settings))
359+
(should (fboundp 'netlinx-mode--defun-name))
360+
(should (fboundp 'netlinx-mode--child-of-type)))
361+
312362
(provide 'netlinx-mode-test)
313363

314364
;;; netlinx-mode-test.el ends here
315365

366+

0 commit comments

Comments
 (0)