From 6fcff0d748eef1998cec47470f10f64c66d5aa99 Mon Sep 17 00:00:00 2001 From: Oscar Najera Date: Mon, 19 Aug 2024 00:35:16 +0200 Subject: YAML mode tree sitter imenu --- config/doom/config.org | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'config') diff --git a/config/doom/config.org b/config/doom/config.org index bec9fc0..6a2df58 100644 --- a/config/doom/config.org +++ b/config/doom/config.org @@ -604,6 +604,78 @@ Emacs mode for managing ledger text files :load-path "~/dev/emacs-lisp/emacs-libyaml/") (use-package! yaml) + +(after! yaml-mode + ;; Inspired by this blog post to use tree sitter to create a imenu list. + ;; https://blog.meain.io/2022/navigating-config-files-using-tree-sitter/ + (defun on/ts-elements () + "Tree sitter matches for all entries in data buffer." + (let ((query (thread-last + (pcase major-mode + ('json-mode "(object (pair (string (string_content) @key) (_)) @item)") + ('yaml-mode "(block_mapping_pair (flow_node) @key (_)) @item")) + (tsc-make-query tree-sitter-language))) + (root-node (tsc-root-node tree-sitter-tree))) + (tsc-query-matches query root-node #'tsc--buffer-substring-no-properties))) + + (defun on/pos-items (ts-match) + "From a TS-MATCH get element key name and region in buffer." + (pcase-let ((`[(_ . ,item) (_ . ,key)] (cdr ts-match))) + (list + (tsc-node-text key) + (tsc-node-byte-range item) + ;; (destructuring-bind (s . e) (tsc-node-byte-range key) + ;; (cons (byte-to-position s) (byte-to-position e))) + ))) + + (defun on/nested-item-names (items) + "From named ITEMS figure out the nested hierarchy for names." + (let ((node-stack '(("" 0))) + result) + (dolist (item items (nreverse result)) + (pcase-let ((`(,item-name (,item-start . ,item-end)) item) + (`((_ ,parent-end)) node-stack)) + (when (< parent-end item-end) + (setq node-stack (seq-filter (lambda (y) + (<= item-end (cadr y))) + node-stack))) + (push (list item-name item-end) node-stack) + (push (list (nreverse (mapcar #'car node-stack)) + item-start) + result))))) + + (defun on/imenu-nested-path-elements () + "On a yaml or json buffer get the element positions for imenu." + (thread-last + (seq-map #'on/pos-items (on/ts-elements)) + (on/nested-item-names) + (seq-map (lambda (item) + (seq-let (name-chain start) item + (cons (string-join name-chain ".") (byte-to-position start))))))) + + (remove-hook 'yaml-mode-hook 'yaml-set-imenu-generic-expression) ;; don't use default one + (add-hook 'yaml-mode-hook + (lambda () + (setq imenu-create-index-function #'on/imenu-nested-path-elements))) + + (ert-deftest on/tree-sitter-imenu () + "Test the imenu generation." + (with-temp-buffer + (yaml-mode) + (tree-sitter-mode) + (insert "one: Ñaño +skills: + run: 3 + jump: + high: 7 + low: 2 + +") + (should + (equal '(("one" . 1) ("skills" . 13) ("skills.run" . 23) ("skills.jump" . 32) ("skills.jump.high" . 43) ("skills.jump.low" . 55)) + (on/imenu-nested-path-elements))) + )) + ) #+end_src ** Databases -- cgit v1.2.3