|
| 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 |
0 commit comments