emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/indent-bars 77aec85ad0 124/431: Merge branch 'treesit'


From: ELPA Syncer
Subject: [elpa] externals/indent-bars 77aec85ad0 124/431: Merge branch 'treesit' into dev
Date: Mon, 16 Sep 2024 12:59:20 -0400 (EDT)

branch: externals/indent-bars
commit 77aec85ad0f056a65fa940dd3c7625417e62d803
Merge: 568ec43b0e 44d7d633fc
Author: JD Smith <93749+jdtsmith@users.noreply.github.com>
Commit: JD Smith <93749+jdtsmith@users.noreply.github.com>

    Merge branch 'treesit' into dev
---
 README.md      |  26 ++++++++++
 indent-bars.el | 156 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 160 insertions(+), 22 deletions(-)

diff --git a/README.md b/README.md
index 66a864b489..cac39b74a1 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ This package provides vertical indentation _guide bars_, with 
the following feat
 - Properly handles font size changes.
 - Optional zero-cost current-depth bar highlighting, permitting bar color 
and/or appearance changes.
 - Optional support for drawing bars on blank lines.
+- Optional tree-sitter support, for context-aware bar depth.
 
 # FAQ's
 
@@ -46,6 +47,21 @@ To clone with `use-package` and `straight`:
   :hook ((python-mode yaml-mode) . indent-bars-mode)) ; or whichever modes you 
prefer
 ```
 
+## With tree-sitter support
+
+```elisp
+(use-package indent-bars
+  :load-path "~/code/emacs/indent-bars"
+  :custom
+  
+  (indent-bars-treesit-support t)
+  (indent-bars-treesit-wrap '((python argument_list parameters
+                                     list list_comprehension
+                                     dictionary dictionary_comprehension
+                                     parenthesized_expression subscript)))
+  :hook ((python-base-mode yaml-mode) . indent-bars-mode))
+```
+
 ## Compatibility 
 
 For `indent-bars` to display fancy guide bars, your port and version of emacs 
must correctly display the `:stipple` face attribute.  **Most do.**  It can 
also be used *without stipples*, drawing a simple vertical character (like `│`) 
instead.  It automatically does this in non-graphical displays (terminals), but 
can optionally be configured to always do so; see [Non-stipple 
Display](#non-stipple-display).
@@ -83,6 +99,10 @@ The main customization variables:
 - `indent-bars-prefer-character`: Use *characters* to display the vertical bar 
instead of stipples.  This occurs automatically on non-graphical displays 
(terminals), but this variable can be used to always prefer character-based 
display.
 - `indent-bars-no-stipple-char`: The character to use when stipples are 
unavailable or disabled. Defaults to the vertical box character `│`.  Other 
good options include `┃`, `┋`, and `║`.
 - `indent-bars-unspecified-bg|fg-color`: Colors to use for the frame 
background and default foreground when unspecified (e.g. in terminals).  If you 
intend to use `indent-bars` in the terminal, set to the terminal 
background/foreground colors you use. 
+- `indent-bars-treesit-support`: Whether to use tree-sitter (if available) to 
help determine appropriate bar depth.
+- `indent-bars-treesit-wrap`: A mapping of language and wrap types, to avoid 
adding extra bars (e.g. in wrapped function arguments).
+- `indent-bars-treesit-ignore-blank-lines-types`: A list of tree-sitter node 
types to inhibit styling blank lines, like "module". 
+- `indent-bars-no-descend-string`: Whether to inhibit increasing depth inside 
of (tree-sitter determined) strings. 
 
 See the documentation of each variable for more details.
 
@@ -98,6 +118,12 @@ The heaviest operation (though still fairly efficient) is 
**blank-line highlight
 
 `indent-bars` only works with space-based indentation, i.e. 
`indent-tabs-mode=nil`.  Note that many modes enable this by default.
 
+## Tree-sitter
+
+`indent-bars` can optionally use tree-sitter when configured in supported 
files to improve the calculation of bar depth.  For example, many modes wrap 
function definitions to align parameters with the opening `(`.  With the help 
of tree-sitter, `indent-bars` can avoid adding unwanted additional bars there.  
It also can be used to identify strings, and to tweak the behavior of blank 
line display.  See options above.
+
+**Note**: This requires Emacs 29 built with tree-sitter support, and the 
appropriate tree-sitter grammars installed for languages of interest.
+
 ## Display
 
 ### Stipples
diff --git a/indent-bars.el b/indent-bars.el
index 7e150e638c..907d5c3940 100644
--- a/indent-bars.el
+++ b/indent-bars.el
@@ -42,6 +42,7 @@
 (require 'outline)
 (require 'font-lock)
 (require 'compat)
+(require 'treesit nil t)
 
 ;;;; Customization
 (defgroup indent-bars nil
@@ -343,6 +344,45 @@ buffer-local automatically."
   :type '(choice integer (const :tag "Discover automatically" :value nil))
   :group 'indent-bars)
 
+;;;;; Treesitter
+(defcustom indent-bars-treesit-support nil
+  "Whether to enable tree-sitter support (if available)."
+  :type 'boolean
+  :group 'indent-bars)
+
+(defcustom indent-bars-treesit-wrap nil
+    "An alist of language and treesitter node type symbols to wrap.
+Inside such wrapping types, indentation bar depth will not be
+increased more than one beyond that of the containing node's
+depth.  This is typically done for lists, parameters, function
+arguments, etc., to avoid unwanted \"extra bars\".  Types must be
+valid node types for the grammar of the language indicated."
+    :type '(choice (const :tag "No wrap types" nil)
+                  (alist :tag "Alist of node types"
+                         :key-type (symbol :tag "Language")
+                         :value-type (repeat :tag "Types" (symbol :tag 
"Type"))))
+    :group 'indent-bars)
+
+(defcustom indent-bars-treesit-ignore-blank-lines-types nil
+  "Do not style blank lines when the type of node at start is in this list.
+Either nil, or a list of node type strings to avoid adding blank
+line styling to.  Typically \"top-level\" node types like
+\"module\", \"program\", and \"translation_unit\" would be used
+here, and they need not be valid types for any particular
+grammar.  Only applicable if `indent-bars-display-on-blank-lines'
+is set."
+  :type '(choice (const :tag "None" nil)
+                (repeat :tag "Node types" string))
+  :group 'indent-bars)
+
+(defcustom indent-bars-no-descend-string t
+  "Configure bar behavior inside strings.
+If non-nil, bars will go no deeper than their starting line
+inside multi-line strings.  Strings are identified with
+tree-sitter; see `indent-bars-ts-string-type'."
+  :type 'boolean
+  :group 'indent-bars)
+
 ;;;; Colors
 (defvar indent-bars--main-color nil)
 (defvar indent-bars--depth-palette nil)
@@ -685,11 +725,58 @@ returned."
 (defun indent-bars--display ()
   "Display indentation bars based on line contents."
   (save-excursion
-    (goto-char (match-beginning 1))
-    (indent-bars--draw (+ (line-beginning-position) indent-bars-spacing) 
(match-end 1) nil nil
-                      (or (not (display-graphic-p)) 
indent-bars-prefer-character)))
+    (goto-char (match-end 1))
+    (let ((d (indent-bars--current-indentation-depth))
+         (b (match-beginning 1)))
+      (if (> d 0)
+         (indent-bars--draw (+ b indent-bars-spacing)
+                            (+ b (* d indent-bars-spacing)) nil nil
+                            (or (not (display-graphic-p)) 
indent-bars-prefer-character)))))
   nil)
 
+;;;; Tree-sitter
+(defvar-local indent-bars-ts-string-type 'string)
+
+(defvar-local indent-bars--ts-parser nil)
+(defvar-local indent-bars--ts-query nil)
+(defvar-local indent-bars--ts-string-query nil)
+
+(defsubst indent-bars--ts-node-query (node query)
+  "Capture node(s) spanning NODE matching QUERY.
+QUERY is a compiled treesit query."
+  (treesit-query-capture
+   indent-bars--ts-parser query
+   (treesit-node-start node) (treesit-node-end node) t))
+
+(defsubst indent-bars--indent-at-node (node)
+  "Return the current indentation at the start of NODE.
+Moves point."
+  (goto-char (treesit-node-start node))
+  (current-indentation))
+
+(defun indent-bars--current-indentation-depth ()
+  "Calculate current indentation depth.
+If treesit support is enabled, searches for parent nodes with
+types specified in `indent-bars-treesit-wrap' for the current
+buffer's language, and, if found, limits the indentation depth to
+one more than the topmost matching parent node's initial line's
+indentation depth.  If `indent-bars-no-descend-string' is
+non-nil, also look for enclosing string and mark indent depth no
+deeper than one more than the starting line's depth.  May move
+point."
+  (let* ((d (/ (current-indentation) indent-bars-spacing))
+        (p (point)))
+    (or
+     (when-let ((indent-bars--ts-query)
+               ((/= p (point-min)))
+               (node (treesit-node-on (1- p) p indent-bars--ts-parser)))
+       (if (and indent-bars-no-descend-string
+               (indent-bars--ts-node-query node indent-bars--ts-string-query))
+          (min d (1+ (/ (indent-bars--indent-at-node node) 
indent-bars-spacing)))
+        (when-let ((ctx (indent-bars--ts-node-query node 
indent-bars--ts-query)))
+          (min d (1+ (/ (indent-bars--indent-at-node (car ctx)) 
indent-bars-spacing))))))
+     d)))
+
 ;;;; No stipple (e.g. terminal)
 (defvar indent-bars--no-stipple-chars nil)
 ;; (defvar indent-bars--no-stipple-current-depth-char nil)
@@ -758,30 +845,40 @@ display on each line, and applies a string display 
property on
 the final newline if necessary to display the needed bars.
 
 Note: blank lines at the beginning or end of the buffer are not
-indicated, even if otherwise they would be."
+indicated, even if otherwise they would be.  If
+`indent-bars-treesit-ignore-blank-lines-types' is configured,
+ignore blank lines whose starting positions are spanned by nodes
+of those types (e.g. module)."
   (let* ((beg (match-beginning 0))
         (end (match-end 0))
         (no-stipple (or indent-bars-prefer-character (not 
(display-graphic-p))))
         ctxbars)
     (when (and (/= end (point-max)) (/= beg (point-min)))
       (save-excursion
-       (goto-char (1- beg))            ;beg is always bol
-       (when (> (setq ctxbars
-                      (1- (max (/ (current-indentation) indent-bars-spacing)
-                               (progn
-                                 (goto-char (1+ end)) ; end is always eol
-                                 (/ (current-indentation) 
indent-bars-spacing)))))
-                0)
+       (goto-char (1- beg))
+       (beginning-of-line 1)
+       (when (and
+              (not
+               (and indent-bars--ts-parser 
indent-bars-treesit-ignore-blank-lines-types
+                    (when-let ((n (treesit-node-on beg beg)))
+                      (seq-contains-p 
indent-bars-treesit-ignore-blank-lines-types
+                                      (treesit-node-type n)))))
+              (> (setq ctxbars
+                       (1- (max (indent-bars--current-indentation-depth)
+                                (progn
+                                  (goto-char (1+ end)) ; end is always eol
+                                  (indent-bars--current-indentation-depth)))))
+                 0))
          (goto-char beg)
-         (while (<= (point) (1- end))  ;note: end extends 1 char beyond blank 
line range
+         (while (<= (point) (1- end)) ;note: end extends 1 char beyond blank 
line range
            (let* ((bp (line-beginning-position))
                   (ep (line-end-position))
                   (len (- ep bp))
                   (nbars (/ (max 0 (1- len)) indent-bars-spacing)))
-             ;; Draw "real" bars in existing blank
+             ;; Draw "real" bars in existing blank text
              (if (> nbars 0) (indent-bars--draw (+ bp indent-bars-spacing)
                                                 ep nil nil no-stipple))
-             ;; Add fake bars via display
+             ;; Add fake bars, via display
              (when (> ctxbars nbars)
                (let* ((off (- (* (1+ nbars) indent-bars-spacing) len))
                       (s (if no-stipple
@@ -822,8 +919,8 @@ ROT are as in `indent-bars--stipple', and have similar 
default values."
 (defun indent-bars--highlight-current-depth ()
   "Refresh current indentation depth highlight.
 Works by remapping the appropriate indent-bars-N face."
-  (let ((depth (/ (current-indentation) indent-bars-spacing)))
-    (when (not (= depth indent-bars--current-depth))
+  (let ((depth (save-excursion (indent-bars--current-indentation-depth))))
+    (when (and depth (not (= depth indent-bars--current-depth)))
       (if indent-bars--remap-face      ; out with the old
          (face-remap-remove-relative indent-bars--remap-face))
       (setq indent-bars--current-depth depth)
@@ -960,17 +1057,31 @@ Adapted from `highlight-indentation-mode'."
   ;; Window state: selection/size
   (add-hook 'window-state-change-functions #'indent-bars--window-change nil t)
 
-  ;; Font-lock
-  (indent-bars--setup-font-lock)
-  (font-lock-flush)
-  
+  ;; Treesitter
+  (when-let (((and indent-bars-treesit-support
+                  (fboundp #'treesit-available-p)
+                  (treesit-available-p)))
+            (lang (treesit-language-at (point-min)))
+            (types (alist-get lang indent-bars-treesit-wrap)))
+    (setq indent-bars--ts-parser
+         (cl-find lang (treesit-parser-list) :key #'treesit-parser-language)
+         indent-bars--ts-query
+         (treesit-query-compile lang `([,@(mapcar #'list types)] @ctx)))
+    (when indent-bars-no-descend-string
+      (setq indent-bars--ts-string-query
+           (treesit-query-compile lang `([(,indent-bars-ts-string-type)] 
@s)))))
+
   ;; Current depth highlight
   (when indent-bars-highlight-current-depth
     (indent-bars--set-current-bg-color)
     (indent-bars--set-current-depth-stipple)
     (add-hook 'post-command-hook #'indent-bars--highlight-current-depth nil t)
     (setq indent-bars--current-depth 0)
-    (indent-bars--highlight-current-depth)))
+    (indent-bars--highlight-current-depth))
+
+  ;; Font-lock
+  (indent-bars--setup-font-lock)
+  (font-lock-flush))
 
 (defun indent-bars-teardown ()
   "Tears down indent-bars."
@@ -991,7 +1102,8 @@ Adapted from `highlight-indentation-mode'."
        indent-bars--current-depth-stipple nil
        indent-bars--no-stipple-chars nil
        indent-bars--current-bg-color nil
-       indent-bars--current-depth 0)
+       indent-bars--current-depth 0
+       indent-bars--ts-query nil)
   (remove-hook 'text-scale-mode-hook #'indent-bars--resize-stipple t)
   (remove-hook 'post-command-hook #'indent-bars--highlight-current-depth t)
   (remove-hook 'font-lock-extend-region-functions



reply via email to

[Prev in Thread] Current Thread [Next in Thread]