branch: externals/indent-bars
commit a4bc36df85dc8123c9e6c9679d74f44f0d757d72
Author: JD Smith <[email protected]>
Commit: JD Smith <[email protected]>

    Implement bar skipping with no-descend-lists
    
    If the beginning of the line is within nested lists, bars between
    subsequent nested list openers are skipped, or drawn as invisible
    blanks.
---
 indent-bars.el | 320 +++++++++++++++++++++++++++++++++------------------------
 1 file changed, 187 insertions(+), 133 deletions(-)

diff --git a/indent-bars.el b/indent-bars.el
index 53bc2225e5..4b8888f2a0 100644
--- a/indent-bars.el
+++ b/indent-bars.el
@@ -392,12 +392,12 @@ non-nil, any stipple appearance parameters will be 
ignored."
 (defcustom indent-bars-highlight-selection-method 'context
   "Method for selecting bar depth for current indentation highlight.
 If nil, the last showing bar on the current line is selected for
-highlight.  If the symbol `on-bar', and the start of the text on
-the line would have fallen directly on a bar, highlight that bar
-depth instead.  If `context', use `on-bar' logic, but only if a
-directly adjacent (non-blank) context line is indented deeper, by
-more than one indent spacing.  Otherwise select the last bar
-showing for highlight (i.e. the same as CONTEXT nil)."
+highlight.  If the symbol `on-bar', and the start of the text on the
+line would have fallen directly on a bar, highlight that bar depth
+instead.  If `context', use `on-bar' logic, but only if a directly
+adjacent (non-blank) context line is indented deeper by more than one
+indent spacing.  Otherwise select the last bar actually shown for
+highlight (i.e. the same as CONTEXT nil)."
   :type '(choice (const :tag "Containing" nil)
                 (const :tag "On Bar" on-bar)
                 (const :tag "Context" context))
@@ -1020,34 +1020,38 @@ Skips any fully blank lines."
 
 (defvar-local indent-bars--update-depth-function nil)
 (defvar-local indent-bars--ppss nil)
-(defun indent-bars--current-indentation-depth (&optional on-bar)
+(defun indent-bars--current-indentation-depth
+    (&optional on-bar include-skip)
   "Calculate current indentation depth.
 Depth is 1-based (independent of the value of
-`indent-bars-starting-column'), with a depth of 1 corresponding
-to the outermost bar, and a depth of 0 indicating there is no
-valid current depth.
+`indent-bars-starting-column'), with a depth of 1 corresponding to the
+outermost bar, and a depth of 0 indicating there is no valid current
+depth.
 
 If ON-BAR is nil, return the depth of the last visible bar on the
-current line.  If ON-BAR is non-nil and content begins at a
-column where a bar would otherwise have fallen, report the depth
-of that (undrawn) bar.  If ON-BAR is the symbol `context', and
-the first non-blank line immediately above or below the current
-line is not at a deeper indentation level (by at least one bar
-spacing), disable on-bar and use the last-visible-bar depth for
-that line instead.
+current line.  If ON-BAR is non-nil and content on the line begins at a
+column where a bar would otherwise have fallen, report the depth of
+that (undrawn) bar.  If ON-BAR is the symbol `context', and the first
+non-blank line immediately above or below the current line is not at a
+deeper indentation level (by at least one bar spacing), disable on-bar
+and use the last-visible-bar depth for that line instead.
 
 If `indent-bars-no-descend-string' is non-nil and point at line
-beginning is inside a string, do not add bars deeper than one
-more than the string's start.  If it is `all', do not add any
-bars at all.  If `indent-bars-no-descend-lists' is non-nil,
-perform the same check for lists.
-
-If `indent-bars--update-depth-function' is non-nil, it will be
-called with the indentation depth (prior to the ON-BAR check),
-and can return an updated depth."
+beginning is inside a string, do not add bars deeper than one more than
+the string's start.  If it is `all', do not add any bars at all.  If
+`indent-bars-no-descend-lists' is non-nil, perform the same check for
+lists.
+
+If `indent-bars--update-depth-function' is non-nil, it will be called
+with the indentation depth (prior to the ON-BAR check), and can return
+an updated depth to use.
+
+If INCLUDE-SKIP is non-nil and some bars should be skipped,
+return (DEPTH . SKIP), with SKIP a list of bar numbers to skip display
+of."
   (let* ((c (current-indentation))
         (d (indent-bars--depth c))     ;last visible bar
-        ppss-ind)
+        ppss-ind skip dppss)
     (when indent-bars--ppss
       (save-excursion
        (forward-line 0)
@@ -1061,28 +1065,44 @@ and can return an updated depth."
                                     (max string-start list-start)
                                   (or string-start list-start)))
              (goto-char ppss-ind)
-             (let* ((cnew (current-indentation))
-                    (dnew (1+ (indent-bars--depth cnew))))
-               (when (< dnew d) (setq d dnew c cnew))))))))
+             (let* ((cppss (current-indentation)))
+               (setq dppss (1+ (indent-bars--depth cppss)))
+               (when (< dppss d) (setq d dppss c cppss)))))
+
+         ;; Nested lists?  Generate bar skip list (if requested)
+         (when (and include-skip list-start (> (car ppss) 1) (not 
string-start))
+           (setq skip
+                 (cl-loop
+                  for pos in (cdr (nreverse (nth 9 ppss)))
+                  with depth
+                  for last-depth = dppss then depth
+                  do (progn (goto-char pos)
+                            (setq depth (1+ (indent-bars--depth
+                                             (current-indentation)))))
+                  if (> last-depth (1+ depth))
+                  append (number-sequence (1+ depth) (1- last-depth))))))))
     (when (and indent-bars--update-depth-function (not ppss-ind))
       (setq d (funcall indent-bars--update-depth-function d)))
     (when (and (eq on-bar 'context)
               (< (indent-bars--context-indentation) (+ c indent-bars-spacing)))
       (setq on-bar nil))
-    (if (and on-bar (= c (+ indent-bars--offset (* d indent-bars-spacing))))
-       (1+ d) d)))
+    (when (and on-bar (= c (+ indent-bars--offset (* d indent-bars-spacing))))
+      (cl-incf d))
+    (if skip (cons d skip) d)))
 
-(defun indent-bars--blank-string (style off nbars bar-from
-                                       &optional width
-                                       switch-after style2)
+(defun indent-bars--blank-string ( style off nbars bar-from skip
+                                  &optional width switch-after style2)
   "Return a blank string with bars displayed, using style STYLE.
-OFF is the character offset within the string to draw the first
-bar, NBARS is the desired number of bars to add, and BAR-FROM is
-the starting index of the first bar (>=1).  WIDTH is the total
-string width to return, right-padding with space if needed.
+OFF is the character offset within the string to draw the first bar,
+NBARS is the desired number of bars to add, and BAR-FROM is the starting
+index of the first bar (>=1).  If non-nil, WIDTH is the total string
+width to return, right-padding with space if needed.
 
-If SWITCH-AFTER is supplied and is an integer, switch from STYLE
-to STYLE2 after drawing that many bars.  If it is t, use
+If SKIP is provided, it should be a list of bar numbers to skip
+drawing (with first bar as number 1).
+
+If SWITCH-AFTER is supplied and is an integer, switch from STYLE to
+STYLE2 after drawing (or skipping) that many bars.  If it is t, use
 STYLE2 for all bars.
 
 Bars are displayed using stipple properties or characters; see
@@ -1092,56 +1112,68 @@ Bars are displayed using stipple properties or 
characters; see
           (cl-loop
            for i from 0 to (1- nbars)
            for depth = (+ bar-from i)
+           for skip-bar = (memql depth skip)
            for sty = (if switch-after
                          (if (or (eq switch-after t)
                                  (>= i switch-after))
                              style2
                            style)
                        style)
-           collect (if indent-bars--no-stipple
-                       (indent-bars--no-stipple-char sty depth)
-                     (propertize " " 'face (indent-bars--face sty depth))))
+           collect (cond
+                    (skip-bar
+                     (propertize " " 'face 'indent-bars-invisible-face))
+                    (indent-bars--no-stipple
+                     (indent-bars--no-stipple-char sty depth))
+                    (t (propertize " " 'face (indent-bars--face sty depth)))))
           (propertize (make-string (1- indent-bars-spacing) ?\s)
                       'face 'indent-bars-invisible-face))
-         (when width
-           (propertize
-            (make-string (- width
-                            (+ off nbars (* (1- nbars) (1- 
indent-bars-spacing))))
-                         ?\s)
-            'face 'indent-bars-invisible-face))))
-
-(defun indent-bars--tab-display (style p off bar-from max &rest r)
-  "Display up to MAX bars on the tab at P, offsetting them by OFF.
-Bars are spaced by `indent-bars-spacing' and displayed with style
-STYLE.  BAR-FROM is the bar number for the first bar.  Other
-arguments R are passed to `indent-bars--blank-string'.  Returns
-the number of bars actually displayed."
-  (let* ((nb (min max (1+ (/ (- tab-width off 1) indent-bars-spacing))))
-        (str (apply #'indent-bars--blank-string style off nb
-                    bar-from tab-width r)))
-    (put-text-property p (+ p 1) 'indent-bars-display str)
-    nb))
-
-(defun indent-bars--draw-line (style nbars start end &optional
-                                    invent switch-after style2)
+         (and width
+              (propertize
+               (make-string (- width
+                               (+ off nbars (* (1- nbars)
+                                               (1- indent-bars-spacing))))
+                            ?\s)
+               'face 'indent-bars-invisible-face))))
+
+(defun indent-bars--tab-display (style p off bar-from skip max &rest r)
+  "Display up to MAX bars on the tab at position P.
+Within the space occupied by the tab character, bars are offset by OFF
+columns.  Bars are spaced by `indent-bars-spacing' and displayed with
+style STYLE.  BAR-FROM is the bar number for the first bar.  SKIP is a
+list of bar numbers to skip.  Other arguments R are passed to
+`indent-bars--blank-string'.  Returns the number of bars actually
+displayed or skipped."
+  (let ((nbars (min max (1+ (/ (- tab-width off 1) indent-bars-spacing)))))
+    (unless (and skip (cl-loop for b from bar-from upto (+ bar-from nbars)
+                              unless (memql b skip) return nil
+                              finally return t)) ; all skipped
+     (put-text-property p (+ p 1) 'indent-bars-display
+                       (apply #'indent-bars--blank-string style off nbars
+                              bar-from skip tab-width r)))
+    nbars))
+
+(defun indent-bars--draw-line ( style nbars start end &optional
+                               skip invent switch-after style2)
   "Draw NBARS bars on the line between positions START and END.
-Bars are drawn in style STYLE, `indent-bars-style' by default.
-START is assumed to be on a line beginning position.  Drawing
-starts at a column determined by `indent-bars-starting-column'.
-Tabs at the line beginning have appropriate display properties
-applied if `indent-tabs-mode' is enabled.
-
-If SWITCH-AFTER is an integer, switch from STYLE to STYLE2
-after drawing that many bars.  If it is t, use STYLE2
+Bars are drawn in style STYLE, `indent-bars-style' by default.  START is
+assumed to be on a line beginning position.  Drawing starts at a column
+determined by `indent-bars-starting-column'.  Tabs at the line beginning
+have appropriate display properties applied if `indent-tabs-mode' is
+enabled.
+
+If SWITCH-AFTER is an integer, switch from STYLE to STYLE2 after
+drawing (or skipping) that many bars.  If it is t, use STYLE2
 exclusively.
 
-If INVENT is non-nil and the line's length is insufficient to
-display all NBARS bars (whether by replacing tabs or adding
-properties to existing non-tab whitespace), bars will be
-\"invented\".  That is, the line's final newline, which is (only
-in this case) expected to be located at END, will have its
-display properties set to fill out the remaining bars, if any are
-needed."
+If SKIP is non-nil, it should be a list of bar numbers to skip while
+drawing (where the first and leftmost possible bar is number 1).
+
+If INVENT is non-nil and the line's length is insufficient to display
+all NBARS bars (whether by replacing tabs or adding properties to
+existing non-tab whitespace), bars will be \"invented\".  That is, the
+line's final newline, which is (only in this case) expected to be
+located at END, will have its display properties set to fill out the
+remaining bars, if any are needed."
   (let* ((tabs (and indent-tabs-mode
                    (save-excursion
                      (goto-char start) (looking-at "^\t+"))
@@ -1153,32 +1185,34 @@ needed."
       (while (and (<= bar nbars) (< (setq tnum (/ vp tab-width)) tabs))
        (setq bars-drawn
              (indent-bars--tab-display style (+ start tnum) (mod vp tab-width)
-                                       bar (- nbars bar -1)
+                                       bar skip (- nbars bar -1)
                                        switch-after style2))
        (when (integerp switch-after)
          (cl-decf switch-after bars-drawn)
-         (if (<= switch-after 0) (setq switch-after t))) ; switch the rest
+         (when (<= switch-after 0)
+           (setq switch-after t)))     ; switch style of the rest
        (cl-incf bar bars-drawn)
        (cl-incf vp (* bars-drawn indent-bars-spacing)))
       (cl-incf start (+ (/ vp tab-width) (mod vp tab-width))))
-    (when (<= bar nbars)               ; still bars to show
+    (when (<= bar nbars)               ; still bars to show?
       (if indent-bars--no-stipple
          (setq prop 'indent-bars-display fun #'indent-bars--no-stipple-char)
        (setq prop 'face fun #'indent-bars--face))
       (let ((pos (if tabs start (+ start indent-bars--offset))))
        (while (and (<= bar nbars) (< pos end))
-         (put-text-property
-          pos (1+ pos)
-          prop (funcall fun
-                        (cond ((integerp switch-after)
-                               (prog1
-                                   (if (> switch-after 0) style style2)
-                                 (cl-decf switch-after)
-                                 (when (<= switch-after 0)
-                                   (setq switch-after t))))
-                              ((eq switch-after t) style2)
-                              (t style))
-                        bar))
+         (unless (memql bar skip)
+           (put-text-property
+            pos (1+ pos)
+            prop (funcall fun
+                          (cond ((integerp switch-after)
+                                 (prog1
+                                     (if (> switch-after 0) style style2)
+                                   (cl-decf switch-after)
+                                   (when (<= switch-after 0)
+                                     (setq switch-after t))))
+                                ((eq switch-after t) style2)
+                                (t style))
+                          bar)))
          (cl-incf bar)
          (cl-incf pos indent-bars-spacing))
        ;; STILL bars to show: invent them (if requested)
@@ -1187,64 +1221,84 @@ needed."
           end (1+ end) ; atop the final newline
           `(indent-bars-display
             ,(concat (indent-bars--blank-string
-                      style (- pos end) (- nbars bar -1) bar nil
+                      style (- pos end) (- nbars bar -1) bar skip nil 
                       switch-after style2)
                      "\n")
            rear-nonsticky t)))))))
 
-(defsubst indent-bars--context-bars (end &optional min)
-  "Maximum number of bars at point and END.
-If MIN is non-nil, return the minimum number of bars instead.
-Moves point."
-  (funcall (if min #'min #'max)
-          (indent-bars--current-indentation-depth)
-          (progn
-            (goto-char (1+ end))       ; end is always eol
-            (indent-bars--current-indentation-depth))))
+(defun indent-bars--context-bars (end &optional min)
+  "Maximum(/minimum) number of bars between point and END.
+If MIN is non-nil, return the minimum number of bars instead.  If bars
+are skipped on either line, the return value is (DEPTH . SKIP), where
+SKIP is a list of bar numbers to skip.  If the bar depths at point and
+END are the same, and both include skipped bars, the longer of thw two
+SKIP lists is returned.  Moves point."
+  (let ((pdepth (indent-bars--current-indentation-depth nil 'skip))
+       (edepth (progn
+                 (goto-char (1+ end))  ; end is always eol
+                 (indent-bars--current-indentation-depth nil 'skip)))
+       skip eskip)
+    (when (consp pdepth)
+      (setq skip (cdr pdepth) pdepth (car pdepth)))
+    (when (consp edepth)
+      (setq eskip (cdr edepth) edepth (car edepth)))
+    (when (and skip eskip)
+      (cond
+       ((< edepth pdepth) (when min (setq skip eskip)))
+       ((> edepth pdepth) (unless min (setq skip eskip)))
+       (t (if (> (length eskip) (length skip)) (setq skip eskip)))))
+    (let ((depth (funcall (if min #'min #'max) pdepth edepth)))
+      (if skip (cons depth skip) depth))))
 
 (defun indent-bars--display (beg end &optional style switch-after style2)
   "Draw indentation bars from BEG..END, based on line contents.
 BEG and END should be on the same line.  STYLE, SWITCH-AFTER and
 STYLE2 are as in `indent-bars--draw-line'.  If STYLE is not
 passed, uses `indent-bars-style' for drawing."
-  (let ((n (save-excursion
-            (goto-char beg)
-            (indent-bars--current-indentation-depth))))
-    (and (> n 0) (indent-bars--draw-line style n beg end nil
-                                        switch-after style2))))
+  (let* ((n (save-excursion
+             (goto-char beg)
+             (indent-bars--current-indentation-depth nil 'skip)))
+        skip)
+    (when (consp n)
+       (setq skip (cdr n) n (car n))
+       (message "Skip: %s" skip))
+    (when (> n 0)
+      (indent-bars--draw-line style n beg end skip
+                             nil switch-after style2))))
 
 (defun indent-bars--display-blank-lines (beg end &optional style switch-after 
style2)
   "Display appropriate bars over the blank-only lines from BEG..END.
-Only called if `indent-bars-display-on-blank-lines' is non-nil.
-To be called on complete multi-line blank line regions.
-
-It is ambigious how many bars to draw on blank lines, so this
-uses the maximum depth of the surrounding line indentation, above
-and below, unless `indent-bars-display-on-blank-lines' is set to
-`least', in which case the minimum bar depth of such lines is
-used instead.
-
-Drawing uses `indent-bars--draw-line'.  STYLE, SWITCH-AFTER and
-STYLE2 are as in `indent-bars--draw-line'.
-
-Note: blank lines at the very beginning or end of the buffer are
-not indicated, even if they otherwise would be."
-  (let ((pm (point-max))
-       (lst (eq indent-bars-display-on-blank-lines 'least))
-       ctxbars)
-    (save-excursion
-      (goto-char (1- beg))
-      (forward-line 0)
-      (when (> (setq ctxbars (indent-bars--context-bars end lst)) 0)
+Only called if `indent-bars-display-on-blank-lines' is non-nil.  To be
+called on complete multi-line blank line regions.
+
+It is ambigious how many bars to draw on blank lines, so this uses the
+maximum depth of the surrounding line indentation, above and below,
+unless `indent-bars-display-on-blank-lines' is set to `least', in which
+case the minimum bar depth of such lines is used instead.
+
+Drawing uses `indent-bars--draw-line'.  STYLE, SWITCH-AFTER and STYLE2
+are as in `indent-bars--draw-line'.
+
+Note: blank lines at the very beginning or end of the buffer are not
+indicated, even if they otherwise would be."
+  (save-excursion
+    (goto-char (1- beg))
+    (forward-line 0)
+    (let* ((pm (point-max))
+          (lst (eq indent-bars-display-on-blank-lines 'least))
+          (ctxbars (indent-bars--context-bars end lst))
+          (ctxskip (when (consp ctxbars)
+                     (prog1 (cdr ctxbars) (setq ctxbars (car ctxbars))))))
+      (when (> ctxbars 0)
        (goto-char beg)
        (while (< (point) end) ;note: end extends 1 char beyond blank line range
          (let* ((bp (line-beginning-position))
                 (ep (line-end-position)))
            (unless (= ep pm)
-             (indent-bars--draw-line style ctxbars bp ep 'invent
+             (indent-bars--draw-line style ctxbars bp ep ctxskip 'invent
                                      switch-after style2))
-           (beginning-of-line 2)))))
-    nil))
+           (beginning-of-line 2))))
+    nil)))
 
 ;;;; jit-lock support
 (defvar-local indent-bars--orig-fontify-region nil)

Reply via email to