Re: c*-mode-agnostic basic-offset value

JD Smith <[email protected]> Thu, 4 Jul 2024 13:14:58 -0400
Newsgroups gmane.emacs.cc-mode.general
Message-ID <[email protected]>
--===============6027475216738180127==
Content-Type: multipart/alternative;
	boundary="Apple-Mail=_8D1B272E-2958-49A5-AF96-FD9831F9042C"


--Apple-Mail=_8D1B272E-2958-49A5-AF96-FD9831F9042C
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=utf-8


> On Jul 4, 2024, at 7:17=E2=80=AFAM, Alan Mackenzie <[email protected]> wrote:
>=20
> Hello, JD.
>=20
> Thanks for your post!
>=20
> On Tue, Jul 02, 2024 at 11:32:45 -0400, JD Smith wrote:
>>   I am the author of indent-bars[1], a mode providing indentation =
bars
>>   across modes.  For its operation, indent-bars requires knowledge of =
the
>>   indentation offset, and uses a heuristic to discover that =
information.
>=20
>=20
>>   For c-modes it currently does a simple (in a large cond):
>=20
>>   ((and (local-variable-p 'c-basic-offset) (numberp c-basic-offset))
>>       c-basic-offset)
>=20
>>   But that fails to gather the needed information for various
>>   auto-configuration examples like 'set-from-style.   Can anyone
>>   recommend a simple configuration that will work reliably across all =
the
>>   various c*-modes and their various style options?  Indent-bars just
>>   needs to know the indent multiple (2 chars, 4 chars, 8 chars???).  =
This
>>   code runs in a hook like prog-mode-hook (or equivalent =
mode-specific
>>   hooks) so initialization should already be done.
>=20
> I'm a little puzzled as to what's going on.  In a fully initialised CC
> Mode buffer, c-basic-offset should be a number, such as 2, 4, or 8.  =
Are
> you evaluating that large cond before C Mode is fully initialised,
> somehow?  I think (though I'm not quite sure) that c-basic-offset =
should
> be set up before the mode hooks (including prog-mode-hook) get run.

Thanks for getting back to me.  Since writing, the user who opened the =
issue has let me know they are using c-ts-mode.  I'm not sure of the =
relationship among all the various c*-modes, and whether that factors =
in.  Maybe it's as simple as those modes being distinct and requiring =
distinct offset variables?  It would of course be nice if all emacs =
modes used a unified local variable name (indent-offset?) to indicate =
the offset they have configured.=20

> Is there some way I can access indent-bars without having to allow
> github's Javascript access to my machine?

Sure, I've attached the two relevant files here (main branch, with the =
addition of the user's proposed fix of checking =
`c-ts-common-indent-offset').  I intend to submit this to ELPA soon so =
it should then be accessible there.=20

> It would be useful for me to be able to try to reproduce the bug.  Is
> there anything else I need to know for this beyond what you've already
> said (i.e. call indent-bars from a major mode hook)?

If you eval indent-bars.el and M-x indent-bars it should work (assuming =
your Emacs supports stipples, sadly not all do, though the situation is =
improving).   Note that TS support is not on by default; if you want =
that you must also configure indent-bars-treesit-scope (though I've not =
tested that with C modes). =20

Thanks again,
JD

=EF=BF=BC=EF=BF=BC=

--Apple-Mail=_8D1B272E-2958-49A5-AF96-FD9831F9042C
Content-Type: multipart/mixed;
	boundary="Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2"


--Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
	charset=utf-8

<html><head><meta http-equiv=3D"content-type" content=3D"text/html; =
charset=3Dutf-8"></head><body style=3D"overflow-wrap: break-word; =
-webkit-nbsp-mode: space; line-break: =
after-white-space;"><div><br><blockquote type=3D"cite"><div>On Jul 4, =
2024, at 7:17=E2=80=AFAM, Alan Mackenzie &lt;[email protected]&gt; =
wrote:</div><br class=3D"Apple-interchange-newline"><div><meta =
charset=3D"UTF-8"><span style=3D"caret-color: rgb(0, 0, 0); font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none; float: none; display: inline !important;">Hello, JD.</span><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none;"><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none;"><span =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none; float: none; =
display: inline !important;">Thanks for your post!</span><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none;"><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none;"><span =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none; float: none; =
display: inline !important;">On Tue, Jul 02, 2024 at 11:32:45 -0400, JD =
Smith wrote:</span><br style=3D"caret-color: rgb(0, 0, 0); font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none;"><blockquote type=3D"cite" style=3D"font-family: Helvetica; =
font-size: 12px; font-style: normal; font-variant-caps: normal; =
font-weight: 400; letter-spacing: normal; orphans: auto; text-align: =
start; text-indent: 0px; text-transform: none; white-space: normal; =
widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;">&nbsp;&nbsp;I am the author of indent-bars[1], a =
mode providing indentation bars<br>&nbsp;&nbsp;across modes. &nbsp;For =
its operation, indent-bars requires knowledge of =
the<br>&nbsp;&nbsp;indentation offset, and uses a heuristic to discover =
that information.<br></blockquote><br style=3D"caret-color: rgb(0, 0, =
0); font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><br style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><blockquote type=3D"cite" style=3D"font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; orphans: auto; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;">&nbsp;&nbsp;For c-modes it currently does a =
simple (in a large cond):<br></blockquote><br style=3D"caret-color: =
rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: =
normal; font-variant-caps: normal; font-weight: 400; letter-spacing: =
normal; text-align: start; text-indent: 0px; text-transform: none; =
white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><blockquote type=3D"cite" style=3D"font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; orphans: auto; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;">&nbsp;&nbsp;((and (local-variable-p =
'c-basic-offset) (numberp =
c-basic-offset))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c-basic-offset)<br=
></blockquote><br style=3D"caret-color: rgb(0, 0, 0); font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none;"><blockquote type=3D"cite" style=3D"font-family: Helvetica; =
font-size: 12px; font-style: normal; font-variant-caps: normal; =
font-weight: 400; letter-spacing: normal; orphans: auto; text-align: =
start; text-indent: 0px; text-transform: none; white-space: normal; =
widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;">&nbsp;&nbsp;But that fails to gather the needed =
information for various<br>&nbsp;&nbsp;auto-configuration examples like =
'set-from-style. &nbsp;&nbsp;Can anyone<br>&nbsp;&nbsp;recommend a =
simple configuration that will work reliably across all =
the<br>&nbsp;&nbsp;various c*-modes and their various style options? =
&nbsp;Indent-bars just<br>&nbsp;&nbsp;needs to know the indent multiple =
(2 chars, 4 chars, 8 chars???). &nbsp;This<br>&nbsp;&nbsp;code runs in a =
hook like prog-mode-hook (or equivalent =
mode-specific<br>&nbsp;&nbsp;hooks) so initialization should already be =
done.<br></blockquote><br style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><span style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none; float: none; display: inline !important;">I'm a =
little puzzled as to what's going on. &nbsp;In a fully initialised =
CC</span><br style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; =
font-size: 12px; font-style: normal; font-variant-caps: normal; =
font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none;"><span style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; =
font-size: 12px; font-style: normal; font-variant-caps: normal; =
font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none; float: none; display: inline !important;">Mode buffer, =
c-basic-offset should be a number, such as 2, 4, or 8. =
&nbsp;Are</span><br style=3D"caret-color: rgb(0, 0, 0); font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none;"><span style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; =
font-size: 12px; font-style: normal; font-variant-caps: normal; =
font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none; float: none; display: inline !important;">you evaluating that =
large cond before C Mode is fully initialised,</span><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none;"><span =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none; float: none; =
display: inline !important;">somehow? &nbsp;I think (though I'm not =
quite sure) that c-basic-offset should</span><br style=3D"caret-color: =
rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: =
normal; font-variant-caps: normal; font-weight: 400; letter-spacing: =
normal; text-align: start; text-indent: 0px; text-transform: none; =
white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><span style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none; float: none; display: inline !important;">be set =
up before the mode hooks (including prog-mode-hook) get run.</span><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: =
none;"></div></blockquote><div><br></div><div><font =
color=3D"#000000">Thanks for getting back to me. &nbsp;Since writing, =
the user who opened the issue has let me know they are using c-ts-mode. =
&nbsp;I'm not sure of the relationship among all the various c*-modes, =
and whether that factors in. &nbsp;Maybe it's as simple as those modes =
being distinct and requiring distinct offset variables? &nbsp;It would =
of course&nbsp;be nice if all emacs modes used a unified local variable =
name (indent-offset?) to indicate the offset they have =
configured.&nbsp;</font></div><br><blockquote type=3D"cite"><div><span =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none; float: none; =
display: inline !important;">Is there some way I can access indent-bars =
without having to allow</span><br style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><span style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none; float: none; display: inline =
!important;">github's Javascript access to my machine?</span><br =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: =
none;"></div></blockquote><div><br></div>Sure, I've attached the two =
relevant files here (main branch, with the addition of the user's =
proposed fix of checking `c-ts-common-indent-offset'). &nbsp;I intend to =
submit this to ELPA soon so it should then be accessible =
there.&nbsp;<br><div><br></div><blockquote type=3D"cite"><div><span =
style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: =
12px; font-style: normal; font-variant-caps: normal; font-weight: 400; =
letter-spacing: normal; text-align: start; text-indent: 0px; =
text-transform: none; white-space: normal; word-spacing: 0px; =
-webkit-text-stroke-width: 0px; text-decoration: none; float: none; =
display: inline !important;">It would be useful for me to be able to try =
to reproduce the bug. &nbsp;Is</span><br style=3D"caret-color: rgb(0, 0, =
0); font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"><span style=3D"caret-color: rgb(0, 0, 0); =
font-family: Helvetica; font-size: 12px; font-style: normal; =
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; =
text-align: start; text-indent: 0px; text-transform: none; white-space: =
normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none; float: none; display: inline !important;">there =
anything else I need to know for this beyond what you've =
already</span><br style=3D"caret-color: rgb(0, 0, 0); font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none;"><span style=3D"caret-color: rgb(0, 0, 0); font-family: Helvetica; =
font-size: 12px; font-style: normal; font-variant-caps: normal; =
font-weight: 400; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none; float: none; display: inline !important;">said (i.e. call =
indent-bars from a major mode hook)?</span><br style=3D"caret-color: =
rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: =
normal; font-variant-caps: normal; font-weight: 400; letter-spacing: =
normal; text-align: start; text-indent: 0px; text-transform: none; =
white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; =
text-decoration: none;"></div></blockquote><div><br></div><div>If you =
eval indent-bars.el and M-x indent-bars it should work (assuming your =
Emacs supports stipples, sadly not all do, though the situation is =
improving). &nbsp; Note that TS support is not on by default; if you =
want that you must also configure&nbsp;indent-bars-treesit-scope (though =
I've not tested that with C modes). =
&nbsp;</div><div><br></div><div>Thanks =
again,</div><div>JD</div><br></div></body></html>=

--Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2
Content-Disposition: attachment;
	filename=indent-bars-ts.el
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="indent-bars-ts.el"
Content-Transfer-Encoding: 7bit

;;; indent-bars-ts.el --- treesitter support for indent-bars -*- lexical-binding: t; -*-
;; Copyright (C) 2023-2024  J.D. Smith

;;; Commentary:

;; tree-sitter support for indent-bars:

;;  - Optionally uses tree-sitter to fine-tune indentation depth
;;    (avoiding "too many bars" in e.g. argument lists)
;;  - Uses treesitter to avoid adding bars to blank lines in strings
;;    or other configurable node types.
;;  - Enable alternate styling for bars inside or outside the
;;    treesitter "scope", where scope is defined as the wrapping
;;    elements configured in `indent-bars-treesitscope`.

;; For Developers:
;;
;; This file interfaces treesitter scope queries with font-lock-based
;; bar drawing.  Some of this happens during normal font-lock,
;; querying treesitter for node information at point (typically at the
;; beginning of the line).  The scope highlight capability depends on
;; the position of the point, and so requires some subtle interface
;; between movement, tree-sitter, and font-lock.  At any given point
;; there is a scope range (a pair of markers) which determines how the
;; bars get displayed.  Small movements can change the scope
;; signficantly.
;; 
;; The technique is as follows:
;; 
;;  - A post-command hook sets up an idle-time callback if none exists.
;;  - In idle time, check for the innermost "scope" node at point.
;;  - If the node boundaries have changed from the last time they were
;;    saved (modulo insertion deletion changes markers can track),
;;    invalidate font-lock over the node's boundaries.
;;  - Short nodes occupying too-few lines are not considered.  A nil
;;    scope node indicates the entire file is the scope.
;;  - Font-lock automatically re-applies indentation bars, consulting
;;    the saved node range to determine whether a given line is
;;    in-scope or out-of-scope.
;;    
;;
;; Note the shorthand substitutions for style related prefixes (slot
;; accessors and variables); see file-local-variables at the end:
;; 
;;    ibts/  => indent-bars-ts-scope- (slot accessors)
;;    ibtcs  => indent-bars-ts-current-scope (scope struct)

;;; Code:

;;;; Requires
(require 'cl-lib)
(require 'seq)
(require 'indent-bars)
(require 'treesit nil t)

;;;; Customization
(defgroup indent-bars-ts nil
  "Customization group for indent-bars treesitter options."
  :group 'indent-bars
  :prefix "indent-bars-ts-")

(defgroup indent-bars-ts-style nil
  "Customization group for indent-bars treesitter alternate styling."
  :group 'indent-bars
  :prefix "indent-bars-ts-")

;;;;; Alternate Style Variables
(defcustom indent-bars-ts-styling-scope 'out-of-scope
  "Which scope the *-ts-* style variables apply to: in or out.
By default, the *-ts-* custom variables apply to the out-of-scope
style, and in-scope bars make use of the default (non-ts)
styling.  If instead this is set to `in-scope', the out-of-scope
bars share the default style, and in-scope bars are configured
with alternate styling using the *-ts-* variables."
  :type '(choice (const :tag "Out of scope" out-of-scope)
		 (const :tag "In scope" in-scope)))

(defun indent-bars-ts--add-customs ()
  "Add all the relevant custom variables for the alternate ts style."
  (cl-labels ((ts-cust (var &rest r)
		(eval `(indent-bars--alt-custom
			"ts" ,var ,(format "Tree-sitter (%s)"
					   indent-bars-ts-styling-scope)
			,@r))))
    (ts-cust 'color '(unspecified :blend 0.1) 'add-inherit)
    (dolist (c '( width-frac pad-frac pattern zigzag ;simple types
		  no-stipple-char-font-weight))
      (ts-cust c 'unspecified))
    (dolist (c '(color-by-depth highlight-current-depth))
      (ts-cust c 'unspecified 'add-inherit))))

;;;;; Treesit Customs
(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.  Note
that the (non-treesitter) options `indent-bars-no-descend-string'
and `indent-bars-no-descend-lists', if set, take precedence over
this option."
  :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-ts)

(defcustom indent-bars-treesit-scope nil
  "An alist of language and treesitter node types to emphasize.
If non-nil, indentation bars on text outside (or inside, if
`indent-bars-ts-styling-scope' is changed from its default) of
the innermost matching treesitter scope will use the alternative
style specified in the indent-bars-ts-os-* custom variables,
which mirror and inherit from the normal style variables."
  :type '(choice (const :tag "No scope types" nil)
		 (alist :tag "Alist of node types"
			:key-type (symbol :tag "Language")
			:value-type (repeat :tag "Types" (symbol :tag "Type"))))
  :group 'indent-bars-ts)

(defcustom indent-bars-treesit-scope-min-lines 3
  "Minimum number of lines a node must span to be counted as a scope."
  :type 'integer
  :group 'indent-bars-ts)

(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-ts)

(defcustom indent-bars-treesit-update-delay 0.125
  "Idle time in seconds for treesitter scope updates to occur.
This has effect only if `indent-bars-treesit-scope' is non-nil."
  :type 'float
  :group 'indent-bars-ts)

;;;; Node Utilities
(defvar-local indent-bars-ts--parser nil)
(defvar-local indent-bars-ts--wrap-query nil)
(defvar-local indent-bars-ts--string-query nil)

(defsubst indent-bars-ts--node-spans-p (node start end)
  "Return NODE if it fully spans START..END.
Otherwise return nil."
  (and (<= (treesit-node-start node) start)
       (>= (treesit-node-end node) end)
       node))

(defun indent-bars-ts--node-query (node query &optional
					start-only spanning min-newlines)
  "Capture node(s) matching QUERY which overlap with NODE.
QUERY is a compiled treesit query.  If START-ONLY is non-nil, the
query searches for matching nodes which overlap with NODE's
starting position.  Otherwise nodes which intersect anywhere with
NODE will be returned.

If SPANNING is non-nil, return a single spanning node which fully
spans the start..end range of NODE, if any (or just the start, if
START-ONLY is non-nil).  If SPANNING is \\='innermost, return the
latest (innermost) node on the list which fully spans NODE, which
could include NODE itself if it matches the QUERY.  For any other
non-nil value of SPANNING, check if the first node matched by
QUERY spans NODE and return it if so.  If MIN-NEWLINES is a
number, a spanning node will be returned only if spans at least
that many newlines.  E.g. MIN-NEWLINES=1 demands a two line
node (or larger).

If no spanning node is found, nil is returned."
  (when-let ((start (treesit-node-start node))
	     (end (if start-only start (treesit-node-end node)))
	     (nodes (treesit-query-capture indent-bars-ts--parser query
					   start end t)))
    (cond ((eq spanning 'innermost)
	   (cl-loop for n in (nreverse nodes)
		    if (and (or (eq n node)
				(indent-bars-ts--node-spans-p n start end))
			    (or (not min-newlines)
				(>= (count-lines
				     (treesit-node-start n) (treesit-node-end n))
				    min-newlines)))
		    return n))
	  (spanning  ; check first node, if it doesn't span, none will
	   (when-let ((n (indent-bars-ts--node-spans-p (car nodes) start end))
		      ((or (not min-newlines)
			   (>= (count-lines
				(treesit-node-start n) (treesit-node-end n))
			       min-newlines))))
	     n))
	  (t nodes))))

(defsubst indent-bars--indent-at-node (node)
  "Return the current indentation at the start of TS node NODE."
  (save-excursion
    (goto-char (treesit-node-start node))
    (current-indentation)))

;;;; Limiting Indentation depth: string and context-dependent
(defun indent-bars-ts--update-indentation-depth (d)
  "Update target depth D using the tree-sitter region.
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 first line indentation depth.  If
`indent-bars-no-descend-string' is non-nil, also looks for
enclosing string and marks indent depth no deeper than one more
than the indentation depth at string start.  This reduces depth
inside strings, and for wrapping contexts (e.g. function
arguments)."
  (if-let (((not (bobp)))
	   (node (treesit-node-on (1- (point)) (point) indent-bars-ts--parser))
	   (dnew
	    (if (and indent-bars-ts--string-query
		     (indent-bars-ts--node-query
		      node indent-bars-ts--string-query t))
		;; A string: do not descend
		(1+ (indent-bars--depth (indent-bars--indent-at-node node)))
	      ;; Check wrap context
	      (when-let (( indent-bars-ts--wrap-query)
			 (ctx (indent-bars-ts--node-query
			       node indent-bars-ts--wrap-query nil t)))
		(1+ (indent-bars--depth
		     (indent-bars--indent-at-node ctx)))))))
      (min dnew d)
    d))

;;;; Ignoring Certain Blank Lines
;; see also ts--handle-blank-lines, below
(defun indent-bars-ts--ignore-blank (beg)
  "See if blank lines at BEG should be ignored using tree-sitter.
Blank lines to ignore are those within nodes of the types
mentioned in `indent-bars-treesit-ignore-blank-lines-types'."
  (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)))))

;;;; Scope Highlighting
(defvar indent-bars-ts-alt-style nil)
(defvar indent-bars-ts-in-out-style nil
  "A cons of (IN-STYLE . OUT-OF-STYLE).")

(cl-declaim (optimize (safety 0))) ; no need for type check
(cl-defstruct
    (indent-bars-ts-scope
     (:copier nil)
     (:conc-name ibts/)
     (:constructor ibts/create))
  "A structure for tracking treesitter-based scope information."
  ( range (cons (point-min-marker) (point-min-marker)) :type cons
    :documentation "The current scope node's range.")
  ( clip-win (cons (point-max-marker) (point-max-marker)) :type cons
    :documentation "The clipping window for the current scope.")
  ( start-bars 0 :type integer
    :documentation "The number of bars shown at start of current scope.")
  ( invalid-ranges nil :type list
    :documentation "List of current invalid ranges -- (start . end) conses.")
  ( query nil :type ts-query
    :documentation "The treesitter scope query object."))

(defvar-local ibtcs nil  ; N.B. see shorthands at bottom of file
  "The current `indent-bars-ts-scope' struct.")

(defvar-local indent-bars-ts--scope-timer nil)
(defun indent-bars-ts--out-of-scope (pos)
  "Return t if POS is outside the current treesitter scope.
If there is no scope defined, every position is considered in
scope.  When the timer is running, only consider the most
recently clipped node ranges in scope."
  (when ibtcs
    (or (< pos (car (ibts/range ibtcs)))
	(> pos (cdr (ibts/range ibtcs))))))

(defun indent-bars-ts--display ()
  "Display indentation bars, accounting for current treesitter scope."
  (if (indent-bars-ts--out-of-scope (match-beginning 1))
      (indent-bars--display (match-beginning 1) (match-end 1)
			    (cdr indent-bars-ts-in-out-style))
    ;; In scope: switch from out to in-scope style
    (indent-bars--display (match-beginning 1) (match-end 1)
			  (cdr indent-bars-ts-in-out-style)
			  (ibts/start-bars ibtcs)
			  (car indent-bars-ts-in-out-style))))

(defun indent-bars-ts--handle-blank-lines ()
  "Display bars on blank lines, respecting treesitter scope."
  (let ((beg (match-beginning 0)))
    (unless (indent-bars-ts--ignore-blank beg)
      (if (indent-bars-ts--out-of-scope beg) ;fully out of scope
	  (indent-bars--handle-blank-lines (match-beginning 0) (match-end 0)
					   (cdr indent-bars-ts-in-out-style))
	;; Switch from out of scope to in scope after start-bars
	(indent-bars--handle-blank-lines (match-beginning 0) (match-end 0)
					 (cdr indent-bars-ts-in-out-style)
					 (ibts/start-bars ibtcs)
					 (car indent-bars-ts-in-out-style))))))

(defmacro indent-bars-ts--order-ranges (a b)
  "Order ranges A and B by start position."
  `(if (< (car ,b) (car ,a)) (setq ,b (prog1 ,a (setq ,a ,b)))))

(defun indent-bars-ts--union (a b)
  "Return the union between ranges A and B.
Ranges A and B are (start . end) conses.  Their union is a list
of ranges that either cover."
  (indent-bars-ts--order-ranges a b)
  (if (< (cdr a) (car b))
      (list a b) ; no overlap, use both
    (list (cons (car a) (max (cdr a) (cdr b))))))

(defun indent-bars-ts--update-scope1 (buf)
  "Perform the treesitter scope font-lock update in buffer BUF.
If the buffer is modified or the point has moved, re-query the
scope bounds at point.  If the current scope range, clipped to
the window's bounds, falls outside the prior scope (beyond simple
marker movement), refontify the union of all old invalid ranges
and the new window ranges clipped to the window(s) showing BUF.
Note that the updated node range clips to an \"extended window\"
with 50% padding on either side."
  (with-current-buffer buf
    (setq indent-bars-ts--scope-timer nil)
    (let* ((pmn (point-min)) (pmx (point-max))
	   (old (ibts/range ibtcs))
	   (node (treesit-node-on
		  (max pmn (1- (point))) (point)
		  indent-bars-ts--parser))
	   (scope (and node
		       (indent-bars-ts--node-query
			node (ibts/query ibtcs) nil 'innermost
			indent-bars-treesit-scope-min-lines)))
	   (new (if scope		; no scope = full file
		    (cons (treesit-node-start scope) (treesit-node-end scope))
		  (cons pmn pmx))))
      (unless (and (= (car new) (car old))  ; if node is unchanged (spans
		   (= (cdr new) (cdr old))) ; same range) no update needed
	(setf (ibts/start-bars ibtcs)
	      (save-excursion
		(goto-char (car new))
		(indent-bars--current-indentation-depth)))
	(dolist (inv (indent-bars-ts--union old new))
	  (font-lock-flush (car inv) (cdr inv)))
	(set-marker (car old) (car new)) ;updates ibts/range
	(set-marker (cdr old) (cdr new))))))

(defun indent-bars-ts--update-scope ()
  "Update treesit scope when possible."
  (unless indent-bars-ts--scope-timer
    (setq indent-bars-ts--scope-timer
	  (run-with-idle-timer indent-bars-treesit-update-delay nil
			       #'indent-bars-ts--update-scope1
			       (current-buffer)))))

;;;; Setup
(defun indent-bars-ts--init-scope (&optional force)
  "Initialize scope style and variables.
If FORCE is non-nil, initialize even if this has already been
performed."
  (unless (or force (get 'indent-bars-ts-setup :init-scope))
    (indent-bars-ts--add-customs)
    (put 'indent-bars-ts-setup :init-scope t))
  (indent-bars--initialize-style
   (setq indent-bars-ts-alt-style
	 (indent-bars--new-style "ts")))
  (setq indent-bars-ts-in-out-style
	(if (eq indent-bars-ts-styling-scope 'out-of-scope)
	    (cons indent-bars-style indent-bars-ts-alt-style)
	  (cons indent-bars-ts-alt-style indent-bars-style))))

(defun indent-bars-ts--setup (lang)
  "Setup indent-bars treesitter support in this buffer for language LANG."
  (setq indent-bars-ts--parser
	(cl-find lang (treesit-parser-list) :key #'treesit-parser-language))

  ;; Wrap: prevent additional bars inside wrapped entities
  (when-let ((types (alist-get lang indent-bars-treesit-wrap)))
    (setq indent-bars-ts--wrap-query
	  (treesit-query-compile lang `([,@(mapcar #'list types)] @ctx))
	  indent-bars--update-depth-function
	  #'indent-bars-ts--update-indentation-depth))

  ;; Strings (avoid descending deeper inside strings using TS)
  (when (stringp indent-bars-no-descend-string)
    (let ((query `([(,indent-bars-no-descend-string)] @s))
	  (pm (point-min)))
      (setq indent-bars-ts--string-query (treesit-query-compile lang query))
      ;; Test it to be sure it works
      (condition-case err
	  (treesit-query-capture indent-bars-ts--parser
				 indent-bars-ts--string-query pm pm t)
	(treesit-query-error
	 (setq indent-bars-no-descend-string nil)
	 (message "%s. See `indent-bars-no-descend-string'.\n%s"
		  "indent-bars: malformed treesitter string query; disabling"
		  err))
	(:success (setq indent-bars--update-depth-function
			#'indent-bars-ts--update-indentation-depth)))))

  ;; Emphasis Scope: use alternate styling outside(/inside) current scope
  (when-let ((types (alist-get lang indent-bars-treesit-scope)))
    (indent-bars-ts--init-scope)
    (setq ibtcs (ibts/create))
    (setq-local
     indent-bars--display-form '(indent-bars-ts--display)
     indent-bars--handle-blank-lines-form '(indent-bars-ts--handle-blank-lines))
    (setf (ibts/query ibtcs)
	  (treesit-query-compile lang `([,@(mapcar #'list types)] @ctx)))
    (add-hook 'post-command-hook #'indent-bars-ts--update-scope nil t)
    (add-hook 'indent-bars--teardown-functions 'indent-bars-ts--teardown)))

(defun indent-bars-ts--teardown ()
  "Teardown indent-bars-ts."
  (when indent-bars-ts--scope-timer
    (cancel-timer indent-bars-ts--scope-timer)
    (setq indent-bars-ts--scope-timer nil))
  (kill-local-variable 'indent-bars--display-form)
  (kill-local-variable 'indent-bars--handle-blank-lines-form)
  (remove-hook 'post-command-hook #'indent-bars-ts--update-scope t)
  (remove-hook 'indent-bars--teardown-functions 'indent-bars-ts--teardown))

;;;###autoload
(define-minor-mode indent-bars-ts-mode
  "Minor mode for indent-bars using treesitter."
  :group 'indent-bars-ts
  (if indent-bars-ts-mode
      (if-let (((fboundp #'treesit-available-p))
	       ((treesit-available-p))
	       (lang (treesit-language-at (point-min))))
	  (indent-bars-ts--setup lang)
	(setq indent-bars-ts-mode nil))
    (indent-bars-ts--teardown)))

(defun indent-bars-ts--custom-update-scope ()
  "Update the TS scope for custom setting."
  (when indent-bars-ts-mode
    (indent-bars-ts--update-scope1 (current-buffer))))
(add-hook 'indent-bars-custom-set #'indent-bars-ts--custom-update-scope)

(provide 'indent-bars-ts)
;;; indent-bars-ts.el ends here
;; Local Variables:
;; read-symbol-shorthands: (("ibts/" . "indent-bars-ts-scope-") ("ibtcs" . "indent-bars-ts-current-scope"))
;; End:

--Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2
Content-Transfer-Encoding: 7bit
Content-Type: text/html;
	charset=us-ascii

<html><body style="overflow-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><head><meta http-equiv="content-type" content="text/html; charset=us-ascii"></head></body></html>
--Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2
Content-Disposition: attachment;
	filename=indent-bars.el
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="indent-bars.el"
Content-Transfer-Encoding: quoted-printable

;;;=20indent-bars.el=20---=20Highlight=20indentation=20with=20bars=20-*-=20=
lexical-binding:=20t;=20-*-=0A;;=20Copyright=20(C)=202023-2024=20=20J.D.=20=
Smith=0A=0A;;=20Author:=20J.D.=20Smith=0A;;=20Homepage:=20=
https://github.com/jdtsmith/indent-bars=0A;;=20Package-Requires:=20=
((emacs=20"27.1")=20(compat=20"29.1.4.5"))=0A;;=20Version:=200.6.1=0A;;=20=
Keywords:=20convenience=0A;;=20Prefix:=20indent-bars=0A;;=20Separator:=20=
-=0A=0A;;=20indent-bars=20is=20free=20software:=20you=20can=20=
redistribute=20it=20and/or=0A;;=20modify=20it=20under=20the=20terms=20of=20=
the=20GNU=20General=20Public=20License=20as=0A;;=20published=20by=20the=20=
Free=20Software=20Foundation,=20either=20version=203=20of=20the=0A;;=20=
License,=20or=20(at=20your=20option)=20any=20later=20version.=0A=0A;;=20=
indent-bars=20is=20distributed=20in=20the=20hope=20that=20it=20will=20be=20=
useful,=0A;;=20but=20WITHOUT=20ANY=20WARRANTY;=20without=20even=20the=20=
implied=20warranty=20of=0A;;=20MERCHANTABILITY=20or=20FITNESS=20FOR=20A=20=
PARTICULAR=20PURPOSE.=20=20See=20the=20GNU=0A;;=20General=20Public=20=
License=20for=20more=20details.=0A=0A;;=20You=20should=20have=20received=20=
a=20copy=20of=20the=20GNU=20General=20Public=20License=0A;;=20along=20=
with=20this=20program.=20=20If=20not,=20see=20=
<https://www.gnu.org/licenses/>.=0A=0A;;;=20Commentary:=0A=0A;;=20=
indent-bars=20highlights=20indentation=20with=20configurable=20font-lock=0A=
;;=20based=20vertical=20bars,=20using=20stipples.=20=20The=20color=20and=20=
appearance=0A;;=20(weight,=20pattern,=20position=20within=20the=20=
character,=20zigzag,=20etc.)=20are=0A;;=20all=20configurable.=20=20=
Includes=20the=20option=20for=20depth-varying=20colors=20and=0A;;=20=
highlighting=20the=20indentation=20level=20of=20the=20current=20line.=20=20=
Bars=20span=0A;;=20blank=20lines,=20by=20default.=20=20indent-bars=20=
works=20in=20any=20mode=20using=20fixed=20tab=0A;;=20or=20space-based=20=
indentation.=20=20In=20the=20terminal=20(or=20on=20request)=20it=0A;;=20=
uses=20vertical=20bar=20characters=20instead=20of=20stipple=20patterns.=0A=
=0A;;=20For=20Developers:=0A;;=0A;;=20To=20efficiently=20accommodate=20=
simultaneous=20alternative=20bar=20styling,=20we=0A;;=20do=20two=20=
things:=0A;;=0A;;=20=201.=20Collect=20all=20the=20style=20related=20=
information=20(color,=20stipple=0A;;=20=20=20=20=20pattern,=20etc.)=20=
into=20a=20single=20struct,=20operating=20on=20one=20such=0A;;=20=20=20=20=
=20"current"=20style=20struct=20at=20a=20time.=0A;;=0A;;=20=202.=20=
Provide=20convenience=20functions=20for=20replicating=20"alternative"=0A=
;;=20=20=20=20=20custom=20variables=20the=20user=20can=20configure;=20=
see=0A;;=20=20=20=20=20`indent-bars--style'.=20=20These=20variables=20=
can=20"inherit"=20nil=20or=0A;;=20=20=20=20=20omitted=20plist=20=
variables=20from=20their=20parent=20var.=0A;;=0A;;=20To=20temporarily=20=
alter=20the=20current=20style,=20it's=20enough=20to=20bind=20the=0A;;=20=
variable=20`indent-bars-current-style'=20dynamically.=0A;;=0A;;=20Note=20=
the=20shorthand=20substitution=20for=20style=20related=20slot;=0A;;=20=
see=20file-local-variables=20at=20the=20end:=0A;;=20=0A;;=20=20=20=20=
ibs/=20=20=3D>=20indent-bars-style-=0A=0A;;;=20Code:=0A;;;;=20Requires=0A=
(require=20'cl-lib)=0A(require=20'map)=0A(require=20'seq)=0A(require=20=
'subr-x)=0A(require=20'color)=0A(require=20'timer)=0A(require=20=
'outline)=0A(require=20'font-lock)=0A(require=20'face-remap)=0A(require=20=
'cus-edit)=0A(require=20'compat)=0A=0A;;;;=20Variables=0A(defvar=20=
indent-bars-mode)=0A;;;;=20Customization=0A(defgroup=20indent-bars=20nil=0A=
=20=20"Highlight=20indentation=20bars."=0A=20=20:group=20'convenience=0A=20=
=20:prefix=20"indent-bars-")=0A=0A(defgroup=20indent-bars-style=20nil=0A=20=
=20"Highlight=20indentation=20bars."=0A=20=20:group=20'indent-bars=0A=20=20=
:prefix=20"indent-bars-")=0A=0A(defvar=20indent-bars-depth-update-delay)=0A=
(defvar=20indent-bars-custom-set=20nil)=0A(defun=20=
indent-bars--custom-set=20(sym=20val)=0A=20=20"Set=20SYM=20to=20VAL,=20=
and=20reset=20indent-bars=20in=20the=20`other-window'."=0A=20=20=
(set-default-toplevel-value=20sym=20val)=0A=20=20(when=20(boundp=20=
'indent-bars-mode)=0A=20=20=20=20(cl-loop=20for=20win=20in=20=
(window-list)=0A=09=20=20=20=20=20if=20(buffer-local-value=20=
'indent-bars-mode=20(window-buffer=20win))=20do=0A=09=20=20=20=20=20=
(with-selected-window=20win=0A=09=20=20=20=20=20=20=20=
(indent-bars-reset)=0A=09=20=20=20=20=20=20=20(let=20=
((indent-bars-depth-update-delay=200))=0A=09=09=20=
(indent-bars--highlight-current-depth=20'force))=0A=09=20=20=20=20=20=20=20=
(run-hooks=20'indent-bars-custom-set))=0A=09=20=20=20=20=20and=20return=20=
win)))=0A=0A;;;;;=20Stipple=20Bar=20Shape=0A(defcustom=20=
indent-bars-width-frac=200.375=0A=20=20"The=20width=20of=20the=20indent=20=
bar=20as=20a=20fraction=20of=20the=20character=20width.=0AApplies=20to=20=
stipple-based=20bars=20only."=0A=20=20:type=20'(float=20:tag=20"Width=20=
Fraction"=0A=09=09:match=20(lambda=20(_=20val)=20(and=20val=20(<=3D=20=
val=201)=20(>=3D=20val=200)))=0A=09=09:type-error=20"Fraction=20must=20=
be=20between=200=20and=201")=0A=20=20:group=20'indent-bars-style=0A=20=20=
:set=20#'indent-bars--custom-set)=0A=0A(defcustom=20indent-bars-pad-frac=20=
0.1=0A=20=20"The=20offset=20of=20the=20bar=20from=20the=20left=20edge=20=
of=20the=20character.=0AA=20float,=20the=20fraction=20of=20the=20=
character=20width.=20=20Applies=20to=0A=20stipple-based=20bars=20only."=0A=
=20=20:type=20'(float=20:tag=20"Offset=20Fraction"=0A=09=09:match=20=
(lambda=20(_=20val)=20(and=20val=20(<=3D=20val=201)=20(>=3D=20val=200)))=0A=
=09=09:type-error=20"Fraction=20must=20be=20between=200=20and=201")=0A=20=
=20:group=20'indent-bars-style=0A=20=20:set=20#'indent-bars--custom-set)=0A=
=0A(defcustom=20indent-bars-pattern=20"=20..=20=20..=20"=0A=20=20"A=20=
pattern=20specifying=20the=20vertical=20structure=20of=20indent=20bars.=0A=
Space=20signifies=20blank=20regions,=20and=20any=20other=20character=20=
signifies=0Afilled=20regions.=20=20The=20pattern=20length=20is=20scaled=20=
to=20match=20the=0Acharacter=20height.=20=20Example:=20\".=20.=20\"=20=
would=20specify=20alternating=0Afilled=20and=20blank=20regions=20each=20=
approximately=20one-quarter=20of=20the=0Acharacter=20height.=20=20Note=20=
that=20the=20non-blank=20characters=20need=20not=20be=0Athe=20same=20=
(e.g.,=20see=20`indent-bars-zigzag').=20=20Applies=20to=0Astipple-based=20=
bars=20only."=0A=20=20:type=20'(string=20:tag=20"Fill=20Pattern")=0A=20=20=
:group=20'indent-bars-style=0A=20=20:set=20#'indent-bars--custom-set)=0A=0A=
(defcustom=20indent-bars-zigzag=20nil=0A=20=20"The=20zigzag=20to=20apply=20=
to=20the=20bar=20pattern.=0AIf=20non-nil,=20an=20alternating=20zigzag=20=
offset=20will=20be=20applied=20to=0Aconsecutive=20groups=20of=20=
identical=20non-space=20characters=20in=0A`indent-bars-pattern'.=20=20=
Starting=20from=20the=20top=20of=20the=20pattern,=0Apositive=20values=20=
will=20zigzag=20(right,=20left,=20right,=20..)=20and=20negative=0Avalues=20=
(left,=20right,=20left,=20...).=0A=0AExample:=0A=0A=20=20pattern:=20\"=20=
.**.\"=0A=20=20width:=20=20=200.5=0A=20=20pad:=20=20=20=20=200.25=0A=20=20=
zigzag:=20-0.25=0A=0Awould=20produce=20a=20zigzag=20pattern=20which=20=
differs=20from=20the=20normal=0Abar=20pattern=20as=20follows:=0A=0A=20=20=
=20=20|=20=20=20=20|=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=20=20|=0A=
=20=20=20=20|=20..=20|=20=3D=3D=3D=3D=3D=3D=3D=3D=3D>=20|..=20=20|=0A=20=20=
=20=20|=20..=20|=20=20=20=20=20=20=20=20=20=20=20=20|=20=20..|=0A=20=20=20=
=20|=20..=20|=20apply=20zig-=20|=20=20..|=0A=20=20=20=20|=20..=20|=20zag=20=
-0.25=20=20|..=20=20|=0A=0ANote=20that=20the=20pattern=20will=20be=20=
truncated=20at=20both=20left=20and=20right=0Aboundaries,=20so=20=
(although=20this=20is=20not=20required)=20achieving=20an=20equal=0A=
zigzag=20left=20and=20right=20requires=20leaving=20sufficient=20padding=20=
on=20each=0Aside=20of=20the=20bar;=20see=20`indent-bars-pad-frac'=20and=0A=
`indent-bars-width-frac'.=20=20Applies=20to=20stipple-based=20bars=20=
only."=0A=20=20:type=20'(choice=20:tag=20"Zigzag=20Options"=0A=09=09=20=
(const=20:tag=20"No=20Zigzag"=20:value=20nil)=0A=09=09=20(float=20:value=20=
0.1=20:tag=20"Zigzag=20Fraction"=0A=09=09=09:match=20(lambda=20(_=20val)=20=
(and=20val=20(<=3D=20val=201)=20(>=3D=20val=20-1)))=0A=09=09=09=
:type-error=20"Fraction=20must=20be=20between=20-1=20and=201"))=0A=20=20=
:group=20'indent-bars-style=0A=20=20:set=20#'indent-bars--custom-set)=0A=0A=
;;;;;=20Bar=20Colors=0A(defcustom=20indent-bars-color=0A=20=20=
'(highlight=20:face-bg=20t=20:blend=200.5)=0A=20=20"The=20main=20indent=20=
bar=20color.=0AThe=20format=20is=20a=20list=20of=201=20required=20=
element,=20followed=20by=20an=0Aoptional=20plist=20(keyword/value=20=
pairs):=0A=0A=20=20(main_color=20[:face-bg=20:blend])=0A=0Awhere:=0A=0A=20=
=20MAIN_COLOR:=20Specifies=20the=20main=20indentation=20bar=0A=20=20=20=20=
color=20(required).=20=20It=20is=20either=20a=20face=20name=20symbol,=20=
from=0A=20=20=20=20which=20the=20foreground=20color=20will=20be=20used=20=
as=20the=20primary=20bar=0A=20=20=20=20color,=20or=20an=20explicit=20=
color=20(a=20string).=20=20If=20nil,=20the=20default=0A=20=20=20=20color=20=
foreground=20will=20be=20used.=0A=0A=20=20FACE-BG:=20A=20boolean=20=
controlling=20interpretation=20of=20the=0A=20=20=20=20MAIN_COLOR=20face=20=
(if=20configured).=20=20If=20non-nil,=20the=20background=0A=20=20=20=20=
color=20of=20the=20face=20will=20be=20used=20as=20the=20main=20bar=20=
color=20instead=0A=20=20=20=20of=20its=20foreground.=0A=0A=20=20BLEND:=20=
an=20optional=20blend=20factor,=20a=20float=20between=200=20and=201.=20=20=
If=0A=20=20=20=20non-nil,=20the=20main=20bar=20color=20will=20be=20=
computed=20as=20a=20blend=0A=20=20=20=20between=20MAIN_COLOR=20and=20the=20=
frame=20background=20color,=0A=20=20=20=20notionally:=0A=0A=20=20=20=20=20=
=20BLEND=20*=20MAIN_COLOR=20+=20(1=20-=20BLEND)=20*=20frame-background=0A=
=0A=20=20=20=20If=20BLEND=20is=20nil=20or=20unspecified,=20no=20blending=20=
is=20done,=20and=0A=20=20=20=20MAIN_COLOR=20is=20used=20as-is."=0A=20=20=
:type=0A=20=20'(list=20:tag=20"Color=20Options"=0A=09=20(choice=20:tag=20=
"Main=20Bar=20Color"=0A=09=09=20color=0A=09=09=20(face=20:tag=20"from=20=
Face")=0A=09=09=20(const=20:tag=20"Use=20default"=20nil))=0A=09=20(plist=20=
:tag=20"Other=20Options"=0A=09=09:inline=20t=0A=09=09:options=0A=09=09=
((:face-bg=20(boolean=0A=09=09=09=20=20=20=20:tag=20"Use=20Face's=20=
Background=20Color"=0A=09=09=09=20=20=20=20:value=20t))=0A=09=09=20=
(:blend=20(float=0A=09=09=09=20=20:tag=20"Blend=20Factor"=0A=09=09=09=20=20=
:value=200.5=0A=09=09=09=20=20:match=20(lambda=20(_=20val)=20(and=20val=20=
(<=3D=20val=201)=20(>=3D=20val=200)))=0A=09=09=09=20=20:type-error=20=
"Factor=20must=20be=20between=200=20and=201")))))=0A=20=20:group=20=
'indent-bars-style=0A=20=20:set=20#'indent-bars--custom-set)=0A=0A=
(defcustom=20indent-bars-color-by-depth=0A=20=20'(:regexp=20=
"outline-\\([0-9]+\\)"=20:blend=201)=0A=20=20"Configuration=20for=20=
depth-varying=20indentation=20bar=20coloring.=0AIf=20non-nil,=20=
depth-based=20coloring=20is=20performed.=20=20This=20should=20be=20a=0A=
plist=20with=20keys:=0A=0A=20=20=20=20([:regexp=20[:face-bg]=20|=20=
:palette]=20[:blend])=0A=0Awith:=0A=0A=20=20REGEXP:=20A=20regular=20=
expression=20string=20used=20to=20match=20against=20all=0A=20=20=20=20=
face=20names.=20=20For=20the=20matching=20faces,=20the=20first=20match=20=
group=20in=0A=20=20=20=20the=20regex=20(if=20any)=20will=20be=20=
interpreted=20as=20a=20number,=20and=20used=0A=20=20=20=20to=20sort=20=
the=20resulting=20list=20of=20faces.=20=20The=20foreground=20color=20of=0A=
=20=20=20=20each=20matching=20face=20will=20then=20constitute=20the=20=
depth=20color=0A=20=20=20=20palette=20(see=20also=20PALETTE,=20which=20=
this=20option=20overrides).=0A=0A=20=20FACE-BG:=20A=20boolean.=20=20If=20=
non-nil,=20use=20the=20background=20color=0A=20=20=20=20from=20the=20=
faces=20matching=20REGEXP=20for=20the=20palette=20instead=20of=0A=20=20=20=
=20their=20foreground=20colors.=0A=0A=20=20PALETTE:=20An=20explicit=20=
cyclical=20palette=20of=20colors/faces=20for=0A=20=20=20=20depth-varying=20=
bar=20colors.=20=20Note=20that=20REGEXP=20takes=20precedence=0A=20=20=20=20=
over=20this=20setting.=20=20The=20format=20is=20a=20list=20of=20faces=20=
(symbols)=0A=20=20=20=20or=20colors=20(strings)=20to=20be=20used=20as=20=
a=20color=20cycle=20for=20coloring=0A=20=20=20=20indentations=20at=20=
increasing=20levels.=20=20Each=20face=20can=20optionally=0A=20=20=20=20=
be=20specified=20as=20a=20cons=20cell=20(face=20.=20\\=3D'bg)=20to=20=
specify=20using=0A=20=20=20=20that=20face's=20background=20color=20=
instead=20of=20its=20foreground.=0A=0A=20=20=20=20=20=20(face_or_color=20=
|=20(face=20.=20\\=3D'bg)=20...)=0A=0A=20=20=20=20While=20this=20list=20=
can=20contain=20a=20single=20element,=20it=20makes=20little=0A=20=20=20=20=
sense=20to=20do=20so.=20=20The=20depth=20palette=20will=20be=20used=20=
cyclically,=0A=20=20=20=20i.e.=20when=20a=20bar's=20indentation=20depth=20=
exceeds=20the=20length=20of=20the=0A=20=20=20=20palette,=20colors=20will=20=
be=20obtained=20by=20wrapping=20around=20to=20the=0A=20=20=20=20=
beginning=20of=20the=20list.=0A=0A=20=20BLEND:=20a=20blend=20factor=20=
(0..1)=20which=20controls=20how=20palette=20colors=0A=20=20=20=20are=20=
blended=20with=20the=20main=20color,=20prior=20to=20possibly=20blending=0A=
=20=20=20=20with=20the=20frame=20background=20color=20(see=20=
`indent-bars-color'=20for=0A=20=20=20=20information=20on=20how=20blend=20=
factors=20are=20specified).=20=20A=20nil=20value=0A=20=20=20=20causes=20=
the=20palette=20colors=20to=20be=20used=20as-is.=20=20A=20unity=20value=0A=
=20=20=20=20causes=20the=20palette=20color=20to=20be=20blended=20=
directly=20with=20the=0A=20=20=20=20background=20using=20any=20blend=20=
factor=20from=20`indent-bars-color'.=0A=0ANote=20that,=20for=20this=20=
setting=20to=20have=20any=20effect,=20one=20of=20REGEXP=20or=0APALETTE=20=
is=20required=20(the=20former=20overriding=20the=20latter).=20=20If=20=
both=0Aare=20omitted=20or=20nil,=20all=20bars=20will=20have=20the=20same=20=
color,=20based=20on=0AMAIN_COLOR=20(aside=20possibly=20from=20the=20bar=20=
at=20the=20current=0Aindentation=20level,=20if=20configured;=20see=0A=
`indent-bars-highlight-current-depth')."=0A=20=20:type=20'(choice=20:tag=20=
"Depth=20Palette"=0A=09=09=20(const=20:tag=20"No=20Depth-Coloring"=20=
nil)=0A=09=09=20(plist=20:tag=20"Depth-Coloring"=0A=09=09=09:options=0A=09=
=09=09((:regexp=20(regexp=20:tag=20"Face=20regexp"))=0A=09=09=09=20=
(:face-bg=0A=09=09=09=20=20(boolean=0A=09=09=09=20=20=20:value=20t=0A=09=09=
=09=20=20=20:tag=20"Use=20Matching=20Face's=20Background=20Colors"))=0A=09=
=09=09=20(:palette=0A=09=09=09=20=20(repeat=20:tag=20"Explicit=20=
Color/Face=20List"=0A=09=09=09=09=20=20(choice=20(color=20:tag=20=
"Color")=0A=09=09=09=09=09=20=20(face=20:tag=20"Foreground=20from=20=
Face")=0A=09=09=09=09=09=20=20(cons=20:tag=20"Background=20from=20Face"=0A=
=09=09=09=09=09=09:format=20"Background=20from=20%v"=0A=09=09=09=09=09=09=
face=0A=09=09=09=09=09=09(const=20:format=20"\n"=20:value=20bg)))))=0A=09=
=09=09=20(:blend=0A=09=09=09=20=20(float=20:tag=20"Blend=20Fraction=20=
into=20Main=20Color"=0A=09=09=09=09=20:value=200.5=0A=09=09=09=09=20=
:match=20(lambda=20(_=20val)=0A=09=09=09=09=09=20=20(and=20val=20(<=3D=20=
val=201)=20(>=3D=20val=200)))=0A=09=09=09=09=20:type-error=0A=09=09=09=09=
=20"Factor=20must=20be=20between=200=20and=201")))))=0A=20=20:group=20=
'indent-bars-style=0A=20=20:set=20#'indent-bars--custom-set)=0A=0A;;;;;=20=
Depth=20Highlighting=0A(defcustom=20indent-bars-highlight-current-depth=0A=
=20=20'(:pattern=20".")=09=09=09;=20solid=20bar,=20no=20color=20change=0A=
=20=20"Current=20indentation=20depth=20bar=20highlight=20configuration.=0A=
Use=20this=20to=20configure=20optional=20highlighting=20of=20the=20bar=20=
at=20the=0Acurrent=20line's=20indentation=20depth=20level.=0A=0AFormat:=0A=
=0A=20=20=20=20nil=20|=20(:color=20:face=20:face-bg=20:background=20=
:blend=20:palette=0A=20=20=20=20=20=20=20=20=20=20=20:width=20:pad=20=
:pattern=20:zigzag)=0A=0AIf=20nil,=20no=20highlighting=20will=20be=20=
applied=20to=20bars=20at=20the=20current=0Adepth=20of=20the=20line=20at=20=
point.=20=20Otherwise,=20a=20plist=20describes=20what=0Ahighlighting=20=
to=20apply,=20which=20can=20include=20changes=20to=20color=20and/or=0A=
bar=20pattern.=20=20At=20least=20one=20of=20:blend,=20:color,=20=
:palette,=20:face,=0A:width,=20:pad,=20:pattern,=20or=20:zigzag=20must=20=
be=20set=20and=20non-nil=20for=0Athis=20setting=20to=20take=20effect.=0A=0A=
By=20default,=20the=20highlighted=20bar's=20color=20will=20be=20the=20=
same=20as=20the=0Aunderlying=20bar=20color.=20=20With=20PALETTE,=20COLOR=20=
or=20FACE=20set,=20all=20bars=0Aat=20the=20current=20depth=20will=20be=20=
highlighted=20in=20the=20appropriate=0Acolor,=20either=20from=20an=20=
explicit=20COLOR,=20a=20PALETTE=20list=20(see=0A=
`indent-bars-color-by-depth'),=20or,=20if=20FACE=20is=20set,=20FACE's=0A=
foreground=20or=20background=20color=20(the=20latter=20if=20FACE-BG=20is=0A=
non-nil).=20=20If=20PALETTE=20is=20provided,=20it=20overrides=20any=20=
other=0Aforeground=20color=20setting=20for=20the=20current=20depth=20=
highlight=20bar.=20=20If=0ABACKGROUND=20is=20set=20to=20a=20color,=20=
this=20will=20be=20used=20for=20the=0Abackground=20color=20of=20the=20=
current=20depth=20bar.=0A=0AIf=20BLEND=20is=20provided,=20it=20is=20a=20=
blend=20fraction=20between=200=20and=201=20for=0Ablending=20the=20=
specified=20highlight=20color=20with=20the=0Aexisting=20(depth-based=20=
or=20main)=20bar=20color;=20see=20`indent-bars-color'=0Afor=20its=20=
meaning.=20=20BLEND=3D1=20indicates=20using=20the=20full,=20unblended=0A=
highlight=20color=20(and=20is=20the=20same=20as=20omitting=20BLEND).=0A=0A=
As=20a=20special=20case,=20if=20BLEND=20is=20provided,=20but=20neither=20=
COLOR=20nor=0AFACE=20is,=20BLEND=20is=20used=20as=20a=20(presumably=20=
distinct)=20blend=20factor=0Abetween=20the=20usual=20color=20for=20that=20=
bar=20and=20the=20frame=20background.=0AThe=20original=20colors=20are=20=
specified=20in=20`indent-bars-color-by-depth'=0Aor=20=
`indent-bars-color'.=20=20In=20this=20manner=20the=20current-depth=0A=
highlight=20can=20be=20made=20a=20more=20(or=20less)=20prominent=20=
version=20of=20the=0Adefault=20coloring,=20just=20by=20setting=20BLEND.=0A=
=0AIf=20any=20of=20WIDTH,=20PAD,=20PATTERN,=20or=20ZIGZAG=20are=20set,=20=
the=20stipple=20bar=0Apattern=20at=20the=20current=20level=20will=20be=20=
altered=20as=20well.=20=20Note=20that=0A`indent-bars-width-frac',=20=
`indent-bars-pad-frac',=0A`indent-bars-pattern',=20and=20=
`indent-bars-zigzag'=20will=20be=20used=20as=0Adefaults=20for=20any=20=
missing=20values;=20see=20these=20variables.=0A=0ANote:=20on=20terminal,=20=
or=20if=20`indent-bars-prefer-character'=20is=0Anon-nil,=20any=20stipple=20=
appearance=20parameters=20will=20be=20ignored."=0A=20=20:type=20'(choice=0A=
=09=20=20:tag=20"Highlighting=20Options"=0A=09=20=20(const=20:tag=20"No=20=
Current=20Highlighting"=20:value=20nil)=0A=09=20=20(plist=20:tag=20=
"Highlight=20Current=20Depth"=0A=09=09=20:options=0A=09=09=20((:color=20=
(color=20:tag=20"Highlight=20Color"))=0A=09=09=20=20(:face=20(face=20=
:tag=20"Color=20from=20Face"))=0A=09=09=20=20(:face-bg=20(boolean=20:tag=20=
"Use=20Face's=20Background=20Color"))=0A=09=09=20=20(:background=20=
(color=20:tag=20"Background=20Color=20of=20Current=20Bar"))=0A=09=09=20=20=
(:blend=20(float=20:tag=20"Blend=20Fraction=20into=20Existing=20Color")=0A=
=09=09=09=20=20:value=200.5=0A=09=09=09=20=20:match=20(lambda=20(_=20=
val)=20(and=20(<=3D=20val=201)=20(>=3D=20val=200)))=0A=09=09=09=20=20=
:type-error=20"Factor=20must=20be=20between=200=20and=201")=0A=09=09=20=20=
(:palette=0A=09=09=20=20=20(repeat=20:tag=20"Explicit=20Color/Face=20=
List"=0A=09=09=09=20=20=20(choice=20(color=20:tag=20"Color")=0A=09=09=09=09=
=20=20=20(face=20:tag=20"Foreground=20from=20Face")=0A=09=09=09=09=20=20=20=
(cons=20:tag=20"Background=20from=20Face"=0A=09=09=09=09=09=20:format=20=
"Background=20from=20%v"=0A=09=09=09=09=09=20face=0A=09=09=09=09=09=20=
(const=20:format=20"\n"=20:value=20bg)))))=0A=09=09=20=20(:width=20=
(float=20:tag=20"Bar=20Width"))=0A=09=09=20=20(:pad=20(float=20:tag=20=
"Bar=20Padding=20(from=20left)"))=0A=09=09=20=20(:pattern=20(string=20=
:tag=20"Fill=20Pattern"))=0A=09=09=20=20(:zigzag=20(float=20:tag=20=
"Zig-Zag")))))=0A=20=20:group=20'indent-bars-style=0A=20=20:set=20=
#'indent-bars--custom-set)=0A=0A(defcustom=20=
indent-bars-highlight-selection-method=20'context=0A=20=20"Method=20for=20=
selecting=20bar=20depth=20for=20current=20indentation=20highlight.=0AIf=20=
nil,=20the=20last=20showing=20bar=20on=20the=20current=20line=20is=20=
selected=20for=0Ahighlight.=20=20If=20the=20symbol=20`on-bar',=20and=20=
the=20start=20of=20the=20text=20on=0Athe=20line=20would=20have=20fallen=20=
directly=20on=20a=20bar,=20highlight=20that=20bar=0Adepth=20instead.=20=20=
If=20`context',=20use=20`on-bar'=20logic,=20but=20only=20if=20a=0A=
directly=20adjacent=20(non-blank)=20context=20line=20is=20indented=20=
deeper,=20by=0Amore=20than=20one=20indent=20spacing.=20=20Otherwise=20=
select=20the=20last=20bar=0Ashowing=20for=20highlight=20(i.e.=20the=20=
same=20as=20CONTEXT=20nil)."=0A=20=20:type=20'(choice=20(const=20:tag=20=
"Containing"=20nil)=0A=09=09=20(const=20:tag=20"On=20Bar"=20on-bar)=0A=09=
=09=20(const=20:tag=20"Context"=20context))=0A=20=20:group=20=
'indent-bars)=0A=0A(defcustom=20indent-bars-depth-update-delay=200.075=0A=
=20=20"Minimum=20delay=20time=20in=20seconds=20between=20depth=20=
highlight=20updates.=0AHas=20effect=20only=20if=20=
`indent-bars-highlight-current-depth'=20is=0Anon-nil.=20=20Set=20to=200=20=
for=20instant=20depth=20updates."=0A=20=20:type=20'float=0A=20=20:group=20=
'indent-bars)=0A=0A;;;;;=20Other=0A(defcustom=20=
indent-bars-display-on-blank-lines=20t=0A=20=20"Whether=20to=20display=20=
bars=20on=20blank=20lines."=0A=20=20:type=20'boolean=0A=20=20:set=20=
#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A(defcustom=20=
indent-bars-no-descend-string=20t=0A=20=20"Configure=20bar=20behavior=20=
inside=20strings.=0AIf=20non-nil,=20displayed=20bars=20inside=20the=20=
string=20will=20go=20no=20deeper=0Athan=20the=20indent=20level=20of=20=
the=20string's=20starting=20line."=0A=20=20:local=20t=0A=20=20:type=20=
'boolean=0A=20=20:set=20#'indent-bars--custom-set=0A=20=20:group=20=
'indent-bars)=0A=0A(defcustom=20indent-bars-no-descend-lists=20t=0A=20=20=
"Configure=20bar=20behavior=20inside=20lists.=0AIf=20non-nil,=20=
displayed=20bars=20will=20go=20no=20deeper=20than=20the=20indent=0Alevel=20=
at=20the=20starting=20line=20of=20the=20innermost=20containing=20list."=0A=
=20=20:local=20t=0A=20=20:type=20'boolean=0A=20=20:set=20=
#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A(defcustom=20=
indent-bars-prefer-character=20nil=0A=20=20"Use=20characters=20instead=20=
of=20stipple=20to=20draw=20bars.=0ANormally=20characters=20are=20used=20=
on=20terminal=20only.=20=20A=20non-nil=20value=0Aspecifies=20using=20=
character=20bars=20exclusively.=20=20See=0A=
`indent-bars-no-stipple-char'."=0A=20=20:type=20'boolean=0A=20=20:set=20=
#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A(defcustom=20=
indent-bars-no-stipple-char=20?\=E2=94=82=0A=20=20"Character=20to=20=
display=20when=20stipple=20is=20unavailable=20(as=20in=20the=20=
terminal)."=0A=20=20:type=20'char=0A=20=20:set=20=
#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A(defcustom=20=
indent-bars-no-stipple-char-font-weight=20nil=0A=20=20"Font=20weight=20=
to=20use=20to=20draw=20the=20character=20bars.=0AIf=20non-nil,=20set=20=
the=20no-stipple=20character=20font=20weight=20accordingly."=0A=20=20=
:type=20`(choice=0A=20=20=20=20=20=20=20=20=20=20(const=20:tag=20"Use=20=
Default=20Weight"=20nil)=0A=20=20=20=20=20=20=20=20=20=20,@(mapcar=20=
(lambda=20(item)=20(list=20'const=20(aref=20item=201)))=0A=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20font-weight-table))=0A=20=20=
:set=20#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A=
(defcustom=20indent-bars-unspecified-bg-color=20"black"=0A=20=20"Color=20=
to=20use=20as=20the=20frame=20background=20color=20if=20unspecified.=0A=
Unless=20actively=20set,=20most=20terminal=20frames=20do=20not=20have=20=
a=0Abackground=20color=20specified.=20=20This=20setting=20controls=20the=20=
background=0Acolor=20to=20use=20for=20color=20blending=20in=20that=20=
case."=0A=20=20:type=20'color=0A=20=20:set=20#'indent-bars--custom-set=0A=
=20=20:group=20'indent-bars)=0A=0A(defcustom=20=
indent-bars-unspecified-fg-color=20"white"=0A=20=20"Color=20to=20use=20=
as=20the=20default=20foreground=20color=20if=20unspecified."=0A=20=20=
:type=20'color=0A=20=20:set=20#'indent-bars--custom-set=0A=20=20:group=20=
'indent-bars)=0A=0A(defcustom=20indent-bars-starting-column=20nil=0A=20=20=
"The=20starting=20column=20on=20which=20to=20display=20the=20first=20=
bar.=0ASet=20to=20nil,=20for=20the=20default=20behavior=20(first=20bar=20=
at=20the=20first=0Aindent=20level)=20or=20an=20integer=20value=20for=20=
some=20other=20column."=0A=20=20:type=20'(choice=20(const=20:tag=20=
"Default:=201st=20indent=20position"=20nil)=0A=09=09=20(integer=20:tag=20=
"Specified=20column"))=0A=20=20:set=20#'indent-bars--custom-set=0A=20=20=
:group=20'indent-bars)=0A=0A(defcustom=20indent-bars-spacing-override=20=
nil=0A=20=20"Override=20for=20default,=20major-mode=20based=20=
indentation=20spacing.=0ASet=20only=20if=20the=20default=20guessed=20=
spacing=20is=20incorrect.=20=20Becomes=0Abuffer-local=20automatically."=0A=
=20=20:local=20t=0A=20=20:type=20'(choice=20integer=20(const=20:tag=20=
"Discover=20automatically"=20:value=20nil))=0A=20=20:set=20=
#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A(defcustom=20=
indent-bars-treesit-support=20nil=0A=20=20"Whether=20to=20enable=20=
tree-sitter=20support=20(if=20available)."=0A=20=20:type=20'boolean=0A=20=
=20:set=20#'indent-bars--custom-set=0A=20=20:group=20'indent-bars)=0A=0A=
;;;;;=20Color=20Utilities=0A(defun=20=
indent-bars--frame-background-color()=0A=20=20"Return=20the=20frame=20=
background=20color."=0A=20=20(let=20((fb=20(frame-parameter=20nil=20=
'background-color)))=0A=20=20=20=20(cond=20((not=20fb)=20"white")=0A=09=20=
=20((string=3D=20fb=20"unspecified-bg")=20=
indent-bars-unspecified-bg-color)=0A=09=20=20(t=20fb))))=0A=0A(defun=20=
indent-bars--blend-colors=20(c1=20c2=20fac)=0A=20=20"Return=20a=20=
fractional=20color=20between=20two=20colors=20C1=20and=20C2.=0AEach=20is=20=
a=20string=20color.=20=20The=20fractional=20blend=20point=20is=20the=0A=
float=20FAC,=20with=201.0=20matching=20C1=20and=200.0=20C2."=0A=20=20=
(apply=20#'color-rgb-to-hex=0A=09=20(cl-mapcar=20(lambda=20(a=20b)=0A=09=09=
=20=20=20=20=20=20(+=20(*=20a=20fac)=20(*=20b=20(-=201.0=20fac))))=0A=09=09=
=20=20=20=20(color-name-to-rgb=20c1)=20(color-name-to-rgb=20c2))))=0A=0A=
(defun=20indent-bars--colors-from-regexp=20(regexp=20&optional=20=
face-bg)=0A=20=20"Return=20a=20list=20of=20colors=20(strings)=20for=20=
faces=20matching=20REGEXP.=0AThe=20first=20capture=20group=20in=20REGEXP=20=
will=20be=20interpreted=20as=20a=20number=0Aand=20used=20to=20sort=20the=20=
list=20numerically.=20=20A=20list=20of=20the=20foreground=0Acolor=20of=20=
the=20matching,=20sorted=20faces=20will=20be=20returned,=20unless=0A=
FACE-BG=20is=20non-nil,=20in=20which=20case=20the=20background=20color=20=
is=0Areturned."=0A=20=20(mapcar=20(lambda=20(x)=20(funcall=20(if=20=
face-bg=20#'face-background=20#'face-foreground)=0A=09=09=09=20=20=20=20=20=
=20=20(cdr=20x)=20nil=20t))=0A=20=20=20=20=20=20=20=20=20=20(seq-sort-by=20=
#'car=0A=09=09=20=20=20=20=20=20=20(lambda=20(a=20b)=20(cond=0A=09=09=09=09=
=20=20=20=20=20=20((not=20(numberp=20b))=20t)=0A=09=09=09=09=20=20=20=20=20=
=20((not=20(numberp=20a))=20nil)=0A=09=09=09=09=20=20=20=20=20=20(t=20(<=20=
a=20b))))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20(delq=20nil=0A=09=09=09=20=20=20=20=20(seq-map=0A=09=09=09=20=20=20=
=20=20=20(lambda=20(x)=0A=09=09=09=09(let=20((n=20(symbol-name=20x)))=0A=09=
=09=09=09=20=20(if=20(string-match=20regexp=20n)=0A=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20(cons=20(string-to-number=20(match-string=201=20n))=0A=09=09=
=09=09=09=20=20=20=20x))))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(face-list))))))=0A=0A=
(defun=20indent-bars--unpack-palette=20(palette)=0A=20=20"Process=20a=20=
face=20or=20color-based=20PALETTE."=0A=20=20(delq=20nil=0A=09(cl-loop=20=
for=20el=20in=20palette=0A=09=09=20collect=20(cond=0A=09=09=09=20=20=
((and=20(consp=20el)=20(facep=20(car=20el)))=0A=09=09=09=20=20=20=
(face-background=20(car=20el)))=0A=09=09=09=20=20((facep=20el)=0A=09=09=09=
=20=20=20(face-foreground=20el))=0A=09=09=09=20=20((color-defined-p=20=
el)=20el)=0A=09=09=09=20=20(t=20nil)))))=0A;;;;=20Style=0A(defvar=20=
indent-bars-style=20nil=0A=20=20"The=20`indent-bars-style'=20struct=20=
for=20the=20main=20style.")=0A=0A(defvar=20indent-bars--styles=20nil=0A=20=
=20"List=20of=20known=20indent-bars=20style=20structs.")=0A=0A=
(cl-declaim=20(optimize=20(safety=200)))=20;=20no=20need=20for=20type=20=
check=0A(cl-defstruct=0A=20=20=20=20(indent-bars-style=0A=20=20=20=20=20=
(:copier=20nil)=0A=20=20=20=20=20(:conc-name=20ibs/)=09;=20Note:=20ibs/=20=
=3D>=20indent-bars-style-=20in=20this=20file=0A=20=20=20=20=20=
(:constructor=20nil)=0A=20=20=20=20=20(:constructor=20ibs/create=0A=09=09=
=20=20=20(=20&optional=20tag=20&aux=0A=09=09=20=20=20=20=20(stipple-face=0A=
=09=09=20=20=20=20=20=20(intern=20(format=20"indent-bars%s-face"=0A=09=09=
=09=09=20=20=20=20=20=20(if=20tag=20(concat=20"-"=20tag)=20"")))))))=0A=20=
=20"A=20style=20configuration=20structure=20for=20indent-bars."=0A=20=20=
(=20tag=20nil=20:type=20string=0A=20=20=20=20:documentation=20"An=20=
optional=20tag=20to=20include=20in=20face=20name")=0A=20=20;;=20Colors=20=
and=20Faces=0A=20=20(=20main-color=20nil=20:type=20string=0A=20=20=20=20=
:documentation=20"The=20main=20bar=20color")=0A=20=20(=20depth-palette=20=
nil=0A=20=20=20=20:documentation=20"Palette=20of=20depth=20colors.=0AMay=20=
be=20nil,=20a=20color=20string=20or=20a=20vector=20of=20colors=20=
strings.")=0A=20=20(=20faces=20nil=20:type=20vector=0A=20=20=20=20=
:documentation=20"Depth-based=20faces.")=0A=20=20;;=20Stipple=0A=20=20(=20=
stipple-face=20nil=20:type=20face=0A=20=20=20=20:documentation=20"A=20=
stipple=20face=20to=20inherit=20from.")=0A=20=20(=20no-stipple-chars=20=
nil=0A=20=20=20=20:documentation=20"A=20vector=20of=20styled=20=
non-stipple=20chars.")=0A=20=20;;=20Current=20depth=20highlighting=0A=20=20=
(=20current-stipple-face=20nil=20:type=20face=0A=20=20=20=20=
:documentation=20"A=20current=20depth=20stipple=20face=20to=20inherit=20=
from.")=0A=20=20(=20current-bg-color=20nil=20:type=20color=0A=20=20=20=20=
:documentation=20"The=20background=20color=20of=20the=20current=20depth=20=
highlight.")=0A=20=20(=20current-depth-palette=20nil=0A=20=20=20=20=
:documentation=20"Depth=20palette=20of=20highlighted=20colors."))=0A=0A=
(defsubst=20indent-bars--tag=20(format-str=20s=20&rest=20r)=0A=20=20"Tag=20=
FORMAT-STR=20with=20style=20S=20and=20return=20the=20associate=20=
interned=20symbol.=0AAdditional=20`format'=20arguments=20can=20be=20=
passed=20as=20R."=0A=20=20(intern=20(apply=20#'format=20format-str=0A=09=09=
=20(if=20(ibs/tag=20s)=20(concat=20"-"=20(ibs/tag=20s))=20"")=20r)))=0A=0A=
(defun=20indent-bars--new-style=20(&optional=20tag)=0A=20=20"Create=20=
and=20record=20a=20new=20style=20struct=20with=20TAG.=0AA=20new=20style=20=
is=20only=20created=20if=20an=20existing=20style=20with=20that=20TAG=20=
is=0Ano=20yet=20recorded."=0A=20=20(or=20(seq-find=20(lambda=20(s)=20=
(equal=20(ibs/tag=20s)=20tag))=20indent-bars--styles)=0A=20=20=20=20=20=20=
(let=20((style=20(ibs/create=20tag)))=0A=09(cl-pushnew=20style=20=
indent-bars--styles=20:test=20#'equal)=0A=09style)))=0A=0A;;;;;=20Colors=0A=
(defun=20indent-bars--main-color=20(style=20&optional=20tint=20=
tint-blend=20blend-override)=0A=20=20"Calculate=20the=20main=20bar=20=
color=20for=20STYLE.=0AUses=20`indent-bars-color'=20for=20color=20and=20=
background=20blend=20config.=0AIf=20TINT=20and=20TINT-BLEND=20are=20=
passed,=20first=20blend=20the=20TINT=20color=0Ainto=20the=20main=20color=20=
with=20the=20requested=20blend,=20prior=20to=20blending=0Ainto=20the=20=
background=20color.=20=20If=20BLEND-OVERRIDE=20is=20set,=20use=20it=0A=
instead=20of=20the=20:blend=20factor=20in=20`indent-bars-color'."=0A=20=20=
(cl-destructuring-bind=20(main=20&key=20face-bg=20blend)=20=
(indent-bars--style=20style=20"color")=0A=20=20=20=20(let=20((col=20=
(cond=20((facep=20main)=0A=09=09=20=20=20=20=20=20(funcall=20(if=20=
face-bg=20#'face-background=20#'face-foreground)=0A=09=09=09=20=20=20=20=20=
=20=20main))=0A=09=09=20=20=20=20=20((color-defined-p=20main)=20main)))=0A=
=09=20=20(blend=20(or=20blend-override=20blend)))=0A=20=20=20=20=20=20=
(if=20(and=20tint=20tint-blend=20(color-defined-p=20tint))=20;tint=20=
main=20color=0A=09=20=20(setq=20col=20(indent-bars--blend-colors=20tint=20=
col=20tint-blend)))=0A=20=20=20=20=20=20(if=20blend=09=09=09=09;now=20=
blend=20into=20BG=0A=09=20=20(setq=20col=20(indent-bars--blend-colors=0A=09=
=09=20=20=20=20=20col=20(indent-bars--frame-background-color)=20blend)))=0A=
=20=20=20=20=20=20col)))=0A=0A(defun=20indent-bars--depth-palette=20=
(style=20&optional=20blend-override)=0A=20=20"Calculate=20the=20palette=20=
of=20depth-based=20colors=20(a=20vector)=20for=20STYLE.=0AIf=20=
BLEND-OVERRIDE=20is=20set,=20the=20main=20color's=20:blend=20will=20be=20=
ignored=0Aand=20this=20value=20will=20be=20used=20instead,=20for=20=
blending=20into=20the=20frame=0Abackground=20color.=20=20See=20=
`indent-bars-color-by-depth'."=0A=20=20(when-let=20((cbd=20=
(indent-bars--style=20style=20"color-by-depth")))=0A=20=20=20=20=
(cl-destructuring-bind=20(&key=20regexp=20face-bg=20palette=20blend)=20=
cbd=0A=20=20=20=20=20=20(let=20((colors=0A=09=20=20=20=20=20(cond=0A=09=20=
=20=20=20=20=20(regexp=0A=09=20=20=20=20=20=20=20=
(indent-bars--colors-from-regexp=20regexp=20face-bg))=0A=09=20=20=20=20=20=
=20(palette=0A=09=20=20=20=20=20=20=20(indent-bars--unpack-palette=20=
palette)))))=0A=09(vconcat=0A=09=20(if=20(or=20blend=20blend-override)=0A=
=09=20=20=20=20=20(mapcar=20(lambda=20(c)=0A=09=09=20=20=20=20=20=20=20=
(indent-bars--main-color=20style=20c=20blend=20blend-override))=0A=09=09=20=
=20=20=20=20colors)=0A=09=20=20=20colors))))))=0A=0A(defun=20=
indent-bars--current-depth-palette=20(style)=0A=20=20"Colors=20for=20=
highlighting=20the=20current=20depth=20bar=20for=20STYLE.=0AA=20color=20=
or=20palette=20(vector)=20of=20colors=20is=20returned,=20which=20may=20=
be=0Anil,=20in=20which=20case=20no=20special=20current=20depth-coloring=20=
is=20used.=0ASee=20`indent-bars-highlight-current-depth'=20for=20=
configuration."=0A=20=20(when-let=20((hcd=20(indent-bars--style=20style=20=
"highlight-current-depth")))=0A=20=20=20=20(cl-destructuring-bind=20=
(&key=20color=20face=20face-bg=0A=09=09=09=09=20blend=20palette=20=
&allow-other-keys)=0A=09hcd=0A=20=20=20=20=20=20(let=20((color=0A=09=20=20=
=20=20=20(cond=0A=09=20=20=20=20=20=20((facep=20face)=0A=09=20=20=20=20=20=
=20=20(funcall=20(if=20face-bg=20#'face-background=20#'face-foreground)=0A=
=09=09=09face))=0A=09=20=20=20=20=20=20((and=20color=20(color-defined-p=20=
color))=20color))))=0A=09(cond=0A=09=20;;=20An=20explicit=20palette=0A=09=
=20(palette=0A=09=20=20(vconcat=20(indent-bars--unpack-palette=20=
palette)))=0A=0A=09=20;;=20A=20specified=20color=20(possibly=20to=20=
blend=20in)=0A=09=20(color=0A=09=20=20(if=20(string=3D=20color=20=
"unspecified-fg")=0A=09=20=20=20=20=20=20(setq=20color=20=
indent-bars-unspecified-fg-color))=0A=09=20=20(if=20blend=0A=09=20=20=20=20=
=20=20(if-let=20((palette=20(indent-bars--depth-palette=20style)))=20;=20=
blend=20into=20normal=20depth=20palette=0A=09=09=20=20(vconcat=0A=09=09=20=
=20=20(mapcar=20(lambda=20(c)=0A=09=09=09=20=20=20=20=20=
(indent-bars--blend-colors=20color=20c=20blend))=0A=09=09=09=20=20=20=
palette))=0A=09=09;;=20Just=20blend=20into=20main=20color=0A=09=09=
(indent-bars--blend-colors=20color=20(ibs/main-color=20style)=20blend))=0A=
=09=20=20=20=20color))=0A=09=20=0A=09=20;;=20blend-only=20without=20a=20=
specified=20color:=20re-blend=20originals=20with=20BG=0A=09=20(blend=0A=09=
=20=20(or=20(indent-bars--depth-palette=20style=20blend)=0A=09=20=20=20=20=
=20=20(indent-bars--main-color=20style=20nil=20nil=20blend))))))))=0A=0A=
(defun=20indent-bars--get-color=20(style=20depth=20=20&optional=20=
current-highlight)=0A=20=20"Return=20the=20color=20appropriate=20for=20=
indentation=20DEPTH=20in=20STYLE.=0AIf=20CURRENT-HIGHLIGHT=20is=20=
non-nil,=20return=20the=20appropriate=20highlight=0Acolor,=20if=20setup=20=
(see=20`indent-bars-highlight-current-depth')."=0A=20=20(let*=20=
((palette=20(or=20(and=20current-highlight=0A=09=09=09=20=20=20=
(ibs/current-depth-palette=20style))=0A=09=09=20=20=20=20=20=20=
(ibs/depth-palette=20style))))=0A=20=20=20=20(cond=0A=20=20=20=20=20=
((vectorp=20palette)=0A=20=20=20=20=20=20(aref=20palette=20(mod=20(1-=20=
depth)=20(length=20palette))))=0A=20=20=20=20=20(palette)=20=20;=20=
single=20color=0A=20=20=20=20=20(t=20(ibs/main-color=20style)))))=0A=0A=
;;;;;=20Faces=0A(defun=20indent-bars--stipple-face-spec=20(w=20h=20rot=20=
&optional=20style=20stipple)=0A=20=20"Create=20a=20face=20specification=20=
for=20the=20stipple=20face=20for=20STYLE.=0ACreate=20for=20character=20=
size=20W=20x=20H=20with=20offset=20ROT.=20=20If=20STIPPLE=20is=0A=
non-nil,=20use=20it=20instead=20of=20calculating.=20=20Includes=0A=
:weight=20(affecting=20only=20non-stipple=20character=20display)=20if=0A=
`indent-bars-no-stipple-char-font-weight'=20(or=20equivalent=20for=20the=0A=
STYLE)=20is=20non-nil."=0A=20=20(let=20((stipple=20(or=20stipple=20=
(indent-bars--stipple=20w=20h=20rot=20style)))=0A=09(wt=20=
(indent-bars--style=20style=20"no-stipple-char-font-weight")))=0A=20=20=20=
=20`((t=20(=20:inherit=20nil=0A=09=20=20=20,@(and=20stipple=20`(:stipple=20=
,stipple))=0A=09=20=20=20,@(and=20wt=20`(:weight=20,wt)))))))=0A=0A=
(defun=20indent-bars--calculate-face-spec=20(style=20depth)=0A=20=20=
"Calculate=20the=20face=20spec=20for=20bar=20at=20DEPTH=20in=20STYLE.=0A=
DEPTH=20starts=20at=201."=0A=20=20`((t=20.=20(=20:inherit=20=
,(ibs/stipple-face=20style)=0A=09=20=20=20:foreground=20=
,(indent-bars--get-color=20style=20depth)))))=0A=0A(defun=20=
indent-bars--create-faces=20(style=20num=20&optional=20redefine)=0A=20=20=
"Create=20bar=20faces=20up=20to=20depth=20NUM=20for=20STYLE.=0ARedefine=20=
them=20if=20REDEFINE=20is=20non-nil."=0A=20=20(vconcat=0A=20=20=20=
(cl-loop=0A=20=20=20=20for=20i=20from=201=20to=20num=0A=20=20=20=20for=20=
face=20=3D=20(indent-bars--tag=20"indent-bars%s-%d"=20style=20i)=20do=0A=20=
=20=20=20(if=20(and=20redefine=20(facep=20face))=20(face-spec-reset-face=20=
face))=0A=20=20=20=20(face-spec-set=20face=20=
(indent-bars--calculate-face-spec=20style=20i))=0A=20=20=20=20collect=20=
face)))=0A=0A(defsubst=20indent-bars--face=20(style=20depth)=0A=20=20=
"Return=20the=20bar=20face=20for=20bar=20DEPTH=20in=20STYLE.=0AThe=20=
face=20is=20created=20if=20necessary."=0A=20=20(when=20(>=20depth=20=
(length=20(ibs/faces=20style)))=0A=20=20=20=20(setf=20(ibs/faces=20=
style)=0A=09=20=20(indent-bars--create-faces=20style=20depth)))=0A=20=20=
(aref=20(ibs/faces=20style)=20(1-=20depth)))=0A=0A;;;;;=20No=20stipple=20=
characters=20(e.g.=20terminal)=0A(defun=20indent-bars--no-stipple-char=20=
(style=20depth)=0A=20=20"Return=20the=20no-stipple=20bar=20character=20=
for=20DEPTH=20in=20STYLE."=0A=20=20(when=20(>=20depth=20(length=20=
(ibs/no-stipple-chars=20style)))=0A=20=20=20=20(setf=20=
(ibs/no-stipple-chars=20style)=0A=09=20=20=
(indent-bars--create-no-stipple-chars=20style=20depth)))=0A=20=20(aref=20=
(ibs/no-stipple-chars=20style)=20(1-=20depth)))=0A=0A(defun=20=
indent-bars--create-no-stipple-chars=20(style=20num)=0A=20=20"Setup=20=
bar=20characters=20for=20bar=20faces=20up=20to=20depth=20NUM=20in=20=
STYLE.=0AUsed=20when=20not=20using=20stipple=20display=20(on=20terminal,=20=
or=20by=20request;=0Asee=20`indent-bars-prefer-character')."=0A=20=20=
(vconcat=0A=20=20=20(nreverse=0A=20=20=20=20(cl-loop=0A=20=20=20=20=20=
with=20chars=20=3D=20(ibs/no-stipple-chars=20style)=0A=20=20=20=20=20=
with=20l=20=3D=20(length=20chars)=0A=20=20=20=20=20for=20d=20from=20num=20=
downto=201=0A=20=20=20=20=20collect=0A=20=20=20=20=20(or=20(and=20(<=20d=20=
l)=20(aref=20chars=20(1-=20d)))=0A=09=20(propertize=20(string=20=
indent-bars-no-stipple-char)=0A=09=09=20=20=20=20=20'face=20=
(indent-bars--face=20style=20d)))))))=0A=0A;;;;;=20Alternate=20Style=20=
Support=0A(defmacro=20indent-bars--alt-custom=0A=20=20=20=20(alt=20opt=20=
alt-description=20std-val=20&optional=20add-inherit=20no-inherit=20&rest=20=
r)=0A=20=20"Define=20a=20custom=20ALT=20variable=20for=20option=20OPT.=0A=
The=20new=20custom=20options=20default=20value=20is=20set=20to=20=
STD-VAL.=20=20This=0Acreates=20a=20new=20variable=20indent-bars-alt-opt,=20=
based=20on=0Aindent-bars-opt=20(referred=20to=20as=20the=20parent=20=
variable).=0AALT-DESCRIPTION=20will=20be=20used=20to=20identify=20the=20=
alternate=20variable=0Ain=20the=20customize=20interface.=0A=0AIf=20=
ADD-INHERIT=20is=20non-nil,=20expand=20the=20type=20to=20a=20cons:=0A=0A=20=
=20(inherit=20.=20type)=0A=0Awhere=20INHERIT=20is=20either=20`inherit'=20=
or=20`no-inherit',=20depending=0Aon=20the=20value=20of=20NO-INHERIT.=0A=0A=
Additional=20`defcustom`=20keyword=20arguments=20can=20be=20given=20as=20=
R."=0A=20=20(require=20'cus-edit)=0A=20=20(let*=20((optname=20=
(symbol-name=20opt))=0A=09=20(group=20(intern=20(concat=20"indent-bars-"=20=
alt=20"-style")))=0A=09=20(symname=20(concat=20"indent-bars-"=20=
optname))=0A=09=20(sym=20(intern=20(concat=20"indent-bars-"=20optname)))=0A=
=09=20(tsym=20(intern=20(concat=20"indent-bars-"=20alt=20"-"=20=
optname)))=0A=09=20(type=20(custom-variable-type=20sym))=0A=09=20(choice=20=
(cond=20((eq=20(car=20type)=20'choice)=20type)=0A=09=09=20=20=20=20=20=20=
=20((eq=20(car=20type)=20'list)=0A=09=09=09(seq-find=0A=09=09=09=20=
(lambda=20(el)=20(and=20(consp=20el)=20(eq=20(car=20el)=20'choice)))=0A=09=
=09=09=20type))=0A=09=09=20=20=20=20=20=20=20(t=20(setq=20type=20=
`(choice=20,type))))))=0A=20=20=20=20;;=20Add=20an=20unspecified=20=
choice=0A=20=20=20=20(when-let=20((tag-pos=20(member=20:tag=20choice)))=0A=
=20=20=20=20=20=20(setq=20choice=20(cdr=20tag-pos)))=09;after=20tag=0A=20=
=20=20=20(setcdr=20choice=0A=09=20=20=20=20(push=0A=09=20=20=20=20=20=
`(const=20:tag=20,(concat=20"No-value=20(use=20parent=20"=20optname=20=
")")=20unspecified)=0A=09=20=20=20=20=20(cdr=20choice)))=0A=0A=20=20=20=20=
;;=20Add=20leading=20inherit=20flag,=20if=20needed=0A=20=20=20=20(when=20=
(or=20no-inherit=20add-inherit)=0A=20=20=20=20=20=20(setq=20type=0A=09=20=
=20=20=20`(cons=20:tag=20,(concat=20alt-description=20"=20Style")=0A=09=09=
=20=20=20(choice=20:tag=0A=09=09=09=20=20=20,(concat=20"Inherit=20=
missing=20data=20from=20`indent-bars-"=0A=09=09=09=09=20=20=20=20optname=20=
"'")=0A=09=09=09=20=20=20(const=20:tag=20"Do=20not=20inherit"=20=
no-inherit)=0A=09=09=09=20=20=20(const=20:tag=20"Inherit"=20inherit))=0A=09=
=09=20=20=20,type)=0A=09=20=20=20=20std-val=20`(=20,(if=20no-inherit=20=
'no-inherit=20'inherit)=20.=20,std-val=20)))=0A=20=20=20=20`(defcustom=20=
,tsym=20',std-val=0A=20=20=20=20=20=20=20,(concat=20"Alternate=20"=20=
alt-description=20"=20version=20of=20`"=20symname=20"'.")=0A=20=20=20=20=20=
=20=20:type=20',type=0A=20=20=20=20=20=20=20:link=20'(variable-link=20=
,sym)=0A=20=20=20=20=20=20=20:set=20#'indent-bars--custom-set=0A=20=20=20=
=20=20=20=20:group=20',group=0A=20=20=20=20=20=20=20,@r)))=0A=0A=
(defsubst=20indent-bars--alt=20(name=20alt)=0A=20=20"Find=20the=20symbol=20=
value=20of=20NAME,=20with=20alternate=20style=20ALT.=0ANAME=20is=20a=20=
string,=20and=20ALT=20and=20be=20a=20string=20or=20nil."=0A=20=20(intern=20=
(format=20"indent-bars%s-%s"=0A=09=09=20=20(if=20alt=20(concat=20"-"=20=
alt)=20"")=20name)))=0A=0A(defun=20indent-bars--style1=20(style=20name)=0A=
=20=20"Return=20the=20value=20of=20style=20variable=20NAME=20for=20=
STYLE.=0AConsiders=20([no-]inherit=20.=20rest)=20inheritance."=0A=20=20=
(let*=20((tag=20(ibs/tag=20style))=0A=09=20(sym=20(indent-bars--alt=20=
name=20tag))=0A=09=20(val=20(symbol-value=20sym))=0A=09=20(inhrt=20t))=20=
=20;=20inherit=20by=20default=0A=20=20=20=20(when=20tag=0A=20=20=20=20=20=
=20;;=20Check=20for=20the=20([no-]inherit=20.=20actual-val)=20form=0A=20=20=
=20=20=20=20(when=20(and=20(consp=20val)=20(memq=20(car=20val)=20=
'(inherit=20no-inherit)))=0A=09(setq=20inhrt=20(and=20(car=20val)=20(not=20=
(eq=20(car=20val)=20'no-inherit)))=0A=09=20=20=20=20=20=20val=20(cdr=20=
val)))=0A=20=20=20=20=20=20(when=20inhrt=0A=09(setq=20val=20=
(indent-bars--custom-inherit=0A=09=09=20=20=20(symbol-value=20=
(indent-bars--alt=20name=20nil))=20val))))=0A=20=20=20=20val))=0A=0A=
(defun=20indent-bars--style=20(style=20name)=0A=20=20"Return=20the=20=
value=20of=20style=20variable=20NAME=20for=20STYLE.=0ADetermines=20=
variables=20to=20use=20based=20on=20the=20style=20tag.=20=20For=20style=0A=
variable=20values=20of=20the=20form=20(`inherit'|`no-inherit'=20.=20=
plist),=0Ainheritance=20of=20the=20plist=20is=20handled.=20=20If=20style=20=
is=20the=20symbol=0A`any',=20return=20the=20first=20non-nil=20value=20=
for=20all=20styles=20in=0A`indent-bars--styles'."=0A=20=20(if=20(eq=20=
style=20'any)=0A=20=20=20=20=20=20(cl-some=20(lambda=20(s)=20=
(indent-bars--style1=20s=20name))=0A=09=20=20=20=20=20=20=20=
indent-bars--styles)=0A=20=20=20=20(indent-bars--style1=20style=20=
name)))=0A=0A(defun=20indent-bars--custom-inherit=20(old=20new)=0A=20=20=
"Inherit=20the=20values=20of=20NEW=20and=20OLD,=20which=20can=20be=20=
values=20or=20lists.=0ANEW=20and=20OLD=20must=20have=20the=20same=20=
form,=20composed=20of=20atoms=0Aand=20(optionally)=20a=20final=20plist.=20=
=20The=20symbol=20`unspecified'=20in=0ANEW=20indicates=20that=20that=20=
value=20should=20be=20replaced=20by=20the=0Acorresponding=20value=20in=20=
OLD.=20=20Any=20trailing=20PLIST=20in=20NEW=20and=20OLD=0Awill=20be=20=
merged=20(with=20NEW=20taking=20precedence).=20=20The=20merged=20value=20=
is=0Areturned."=0A=20=20(cond=0A=20=20=20((and=20old=20(eq=20new=20=
'unspecified))=0A=20=20=20=20old)=20=09;=20fully=20inherited=0A=20=20=20=0A=
=20=20=20((and=20(atom=20old)=20(atom=20new))=0A=20=20=20=20(if=20(eq=20=
new=20'unspecified)=20old=20new))=0A=20=20=20=0A=20=20=20((and=20(consp=20=
old)=20(consp=20new))=0A=20=20=20=20(let*=20((old=20(copy-sequence=20=
old))=20=09;=20avoid=20editing=20old=0A=09=20=20=20(n=20new)=20(o=20old)=20=
last-o)=0A=20=20=20=20=20=20(while=20(and=20n=20o)=0A=09(if=20(and=20=
(plistp=20n)=20(plistp=20o)=20(keywordp=20(car=20o)))=0A=09=20=20=20=20=
(let=20((m=20(map-merge=20'plist=20o=20n)))=0A=09=20=20=20=20=20=20(if=20=
last-o=20(setcdr=20last-o=20m)=20(setq=20old=20m))=0A=09=20=20=20=20=20=20=
(setq=20o=20nil))=09=09;=20signify=20list=20complete=0A=09=20=20(unless=20=
(eq=20(car=20n)=20'unspecified)=0A=09=20=20=20=20(setcar=20o=20(car=20=
n))))=0A=09(setq=20last-o=20o=20n=20(cdr=20n)=20o=20(cdr=20o)))=0A=20=20=20=
=20=20=20old))=0A=0A=20=20=20(t=20new)))=0A=0A(defun=20=
indent-bars-reset-styles=20(&rest=20_r)=0A=20=20"Reset=20all=20styles'=20=
colors=20and=20faces.=0AUseful=20for=20calling=20after=20theme=20=
changes."=0A=20=20(interactive)=0A=20=20(dolist=20(s=20=
indent-bars--styles)=0A=20=20=20=20(indent-bars--initialize-style=20s)))=0A=
=0A(defun=20indent-bars--initialize-style=20(style)=0A=20=20"Initialize=20=
STYLE."=0A=20=20;;=20Colors=0A=20=20(setf=20(ibs/main-color=20style)=0A=09=
(indent-bars--main-color=20style)=0A=09(ibs/depth-palette=20style)=0A=09=
(indent-bars--depth-palette=20style)=0A=09(ibs/current-depth-palette=20=
style)=0A=09(indent-bars--current-depth-palette=20style)=0A=09(ibs/faces=20=
style)=20(indent-bars--create-faces=20style=207=20'reset)=0A=09=
(ibs/no-stipple-chars=20style)=20(indent-bars--create-no-stipple-chars=20=
style=207))=0A=20=20=0A=20=20;;=20Base=20stipple=20face=0A=20=20=
(face-spec-set=0A=20=20=20(ibs/stipple-face=20style)=0A=20=20=20=
(indent-bars--stipple-face-spec=0A=20=20=20=20(frame-char-width)=20=
(frame-char-height)=0A=20=20=20=20(indent-bars--stipple-rot=20=
(selected-window)=20(frame-char-width))=0A=20=20=20=20style))=0A=20=20=0A=
=20=20;;=20Current=20depth=20highlight=20faces/stipple=0A=20=20(setf=20=
(ibs/current-bg-color=20style)=0A=09(indent-bars--current-bg-color=20=
style))=0A=20=20(when-let=20((stipple=20=
(indent-bars--current-depth-stipple=20nil=20nil=20nil=20style)))=0A=20=20=
=20=20(setf=20(ibs/current-stipple-face=20style)=0A=09=20=20=
(indent-bars--tag=20"indent-bars%s-current-face"=20style))=0A=20=20=20=20=
(face-spec-set=20(ibs/current-stipple-face=20style)=20nil)))=0A=0A;;;;=20=
Indentation=20and=20Drawing=0A(defvar-local=20indent-bars-spacing=20nil)=0A=
(defvar-local=20indent-bars--offset=20nil)=0A(defvar-local=20=
indent-bars--no-stipple=20nil)=0A=0A(defsubst=20indent-bars--depth=20=
(len)=0A=20=20"Number=20of=20possible=20bars=20for=20initial=20blank=20=
string=20of=20length=20LEN.=0ANote=20that=20the=20first=20bar=20is=20=
expected=20at=20`indent-bars-starting-column'."=0A=20=20(if=20(>=20len=20=
indent-bars--offset)=0A=20=20=20=20=20=20(1+=20(/=20(-=20len=20=
indent-bars--offset=201)=20indent-bars-spacing))=0A=20=20=20=200))=0A=0A=
(defun=20indent-bars--context-depth=20()=0A=20=20"Return=20the=20maximum=20=
`current-indentation'=20around=20current=20line.=0ASkips=20any=20fully=20=
blank=20lines."=0A=20=20(let=20(b=20(p=20(point)))=0A=20=20=20=20=
(beginning-of-line)=0A=20=20=20=20(skip-chars-backward=20"[:space:]\n")=0A=
=20=20=20=20(setq=20b=20(current-indentation))=0A=20=20=20=20(goto-char=20=
p)=0A=20=20=20=20(forward-line=201)=0A=20=20=20=20(skip-chars-forward=20=
"[:space:]\n")=0A=20=20=20=20(prog1=0A=09(max=20(current-indentation)=20=
b)=0A=20=20=20=20=20=20(goto-char=20p))))=0A=0A(defvar=20=
indent-bars--update-depth-function=20nil)=0A(defvar=20indent-bars--ppss=20=
nil)=0A(defun=20indent-bars--current-indentation-depth=20(&optional=20=
on-bar)=0A=20=20"Calculate=20current=20indentation=20depth.=0AIf=20=
ON-BAR=20is=20nil,=20return=20the=20depth=20of=20the=20last=20visible=20=
bar=20on=20the=0Aline.=20=20If=20ON-BAR=20is=20non-nil=20and=20content=20=
begins=20at=20a=20column=20where=0Aa=20bar=20would=20otherwise=20have=20=
fallen,=20report=20the=20depth=20of=0Athat=20(undrawn)=20bar.=20=20If=20=
ON-BAR=20is=20the=20symbol=20`context',=20and=20the=0Afirst=20non-blank=20=
line=20immediately=20above=20or=20below=20the=20current=20line=0Ais=20=
not=20at=20a=20deeper=20indentation=20level=20(by=20at=20least=20one=20=
bar=0Aspacing),=20disable=20on-bar=20and=20use=20the=20last-visible-bar=20=
depth=20for=0Athat=20line=20instead.=0A=0AIf=20=
`indent-bars-no-descend-string'=20is=20non-nil=20and=20point=20at=20line=0A=
beginning=20is=20inside=20a=20string,=20do=20not=20add=20bars=20deeper=20=
than=20one=0Amore=20than=20the=20string's=20start.=20=20If=20=
`indent-bars-no-descend-lists'=0Ais=20non-nil,=20perform=20the=20same=20=
check=20for=20lists.=0A=0AIf=20`indent-bars--update-depth-function'=20is=20=
non-nil,=20it=20will=20be=0Acalled=20with=20the=20indentation=20depth=20=
(prior=20to=20the=20ON-BAR=20check),=0Aand=20can=20return=20an=20updated=20=
depth."=0A=20=20(let*=20((c=20(current-indentation))=0A=09=20(d=20=
(indent-bars--depth=20c))=20=09;last=20visible=20bar=0A=09=20ppss-ind)=0A=
=20=20=20=20(when=20indent-bars--ppss=0A=20=20=20=20=20=20(let*=20((p=20=
(prog1=20(point)=20(forward-line=200)))=0A=09=20=20=20=20=20(ppss=20=
(syntax-ppss))=20=09;=20moves=20point!=0A=09=20=20=20=20=20(ss=20(and=20=
indent-bars-no-descend-string=20(nth=208=20ppss)))=0A=09=20=20=20=20=20=
(sl=20(and=20indent-bars-no-descend-lists=20(nth=201=20ppss))))=0A=09=
(when=20(setq=20ppss-ind=20(if=20(and=20ss=20sl)=20(max=20ss=20sl)=20(or=20=
ss=20sl)))=0A=09=20=20(goto-char=20ppss-ind)=0A=09=20=20(let*=20((cnew=20=
(current-indentation))=0A=09=09=20(dnew=20(1+=20(indent-bars--depth=20=
cnew))))=0A=09=20=20=20=20(when=20(<=20dnew=20d)=20(setq=20d=20dnew=20c=20=
cnew))))=0A=09(goto-char=20p)))=0A=20=20=20=20(when=20(and=20=
indent-bars--update-depth-function=20(not=20ppss-ind))=0A=20=20=20=20=20=20=
(setq=20d=20(funcall=20indent-bars--update-depth-function=20d)))=0A=20=20=
=20=20(when=20(and=20(eq=20on-bar=20'context)=0A=09=20=20=20=20=20=20=20=
(<=20(indent-bars--context-depth)=20(+=20c=20indent-bars-spacing)))=0A=20=
=20=20=20=20=20(setq=20on-bar=20nil))=0A=20=20=20=20(if=20(and=20on-bar=20=
(=3D=20c=20(+=20indent-bars--offset=20(*=20d=20indent-bars-spacing))))=0A=
=09(1+=20d)=20d)))=0A=0A(defun=20indent-bars--blank-string=20(style=20=
off=20nbars=20bar-from=0A=09=09=09=09=09&optional=20width=0A=09=09=09=09=09=
switch-after=20style2)=0A=20=20"Return=20a=20blank=20string=20with=20=
bars=20displayed,=20using=20style=20STYLE.=0AOFF=20is=20the=20character=20=
offset=20within=20the=20string=20to=20draw=20the=20first=0Abar,=20NBARS=20=
is=20the=20desired=20number=20of=20bars=20to=20add,=20and=20BAR-FROM=20=
is=0Athe=20starting=20index=20of=20the=20first=20bar=20(>=3D1).=20=20=
WIDTH=20is=20the=20total=0Astring=20width=20to=20return,=20right=20=
padding=20with=20space=20if=20needed.=0A=0AIf=20SWITCH-AFTER=20is=20=
supplied=20and=20is=20an=20integer,=20switch=20from=20STYLE=0Ato=20=
STYLE2=20after=20drawing=20that=20many=20bars.=20=20If=20it=20is=20t,=20=
use=0ASTYLE2=20for=20all=20bars.=0A=0ABars=20are=20displayed=20using=20=
stipple=20properties=20or=20characters;=20see=0A=
`indent-bars-prefer-character'."=0A=20=20(concat=20(make-string=20off=20=
?\s)=0A=09=20=20(string-join=0A=09=20=20=20(cl-loop=0A=09=20=20=20=20for=20=
i=20from=200=20to=20(1-=20nbars)=0A=09=20=20=20=20for=20depth=20=3D=20(+=20=
bar-from=20i)=0A=09=20=20=20=20for=20sty=20=3D=20(if=20switch-after=0A=09=
=09=09=20=20(if=20(or=20(eq=20switch-after=20t)=0A=09=09=09=09=20=20(>=3D=20=
i=20switch-after))=0A=09=09=09=20=20=20=20=20=20style2=0A=09=09=09=20=20=20=
=20style)=0A=09=09=09style)=0A=09=20=20=20=20collect=20(if=20=
indent-bars--no-stipple=0A=09=09=09(indent-bars--no-stipple-char=20sty=20=
depth)=0A=09=09=20=20=20=20=20=20(propertize=20"=20"=20'face=20=
(indent-bars--face=20sty=20depth))))=0A=09=20=20=20(make-string=20(1-=20=
indent-bars-spacing)=20?\s))=0A=09=20=20(if=20width=0A=09=20=20=20=20=20=20=
(make-string=20(-=20width=0A=09=09=09=20=20=20=20=20=20(+=20off=20nbars=20=
(*=20(1-=20nbars)=20(1-=20indent-bars-spacing))))=0A=09=09=09=20=20=20=
?\s))))=0A=0A(defun=20indent-bars--tab-display=20(style=20p=20off=20=
bar-from=20max=20&rest=20r)=0A=20=20"Display=20up=20to=20MAX=20bars=20on=20=
the=20tab=20at=20P,=20offseting=20them=20by=20OFF.=0ABars=20are=20spaced=20=
by=20`indent-bars-spacing'=20and=20displayed=20with=20style=0ASTYLE.=20=20=
BAR-FROM=20is=20the=20bar=20number=20for=20the=20first=20bar.=20=20Other=0A=
arguments=20R=20are=20passed=20to=20`indent-bars--blank-string'.=20=20=
Returns=0Athe=20number=20of=20bars=20actually=20displayed."=0A=20=20=
(let*=20((nb=20(min=20max=20(/=20(-=20tab-width=20off=20-1)=20=
indent-bars-spacing)))=0A=09=20(str=20(apply=20=
#'indent-bars--blank-string=20style=20off=20nb=0A=09=09=20=20=20=20=20=
bar-from=20tab-width=20r)))=0A=20=20=20=20(put-text-property=20p=20(+=20=
p=201)=20'display=20str)=0A=20=20=20=20nb))=0A=0A(defun=20=
indent-bars--draw-line=20(style=20nbars=20start=20end=20&optional=0A=09=09=
=09=09=20=20=20=20=20invent=20switch-after=20style2)=0A=20=20"Draw=20=
NBARS=20bars=20on=20the=20line=20between=20positions=20START=20and=20=
END.=0ABars=20are=20drawn=20in=20style=20STYLE,=20`indent-bars-style'=20=
by=20default=0ASTART=20is=20assumed=20to=20be=20on=20a=20line=20=
beginning=20position.=20=20Drawing=0Astarts=20at=20a=20column=20=
determined=20by=20`indent-bars-starting-column'.=0ATabs=20at=20the=20=
line=20beginning=20have=20appropriate=20display=20properties=0Aapplied=20=
if=20`indent-tabs-mode'=20is=20enabled.=0A=0AIf=20SWITCH-AFTER=20is=20an=20=
integer,=20switch=20from=20STYLE=20to=20STYLE2=0Aafter=20drawing=20that=20=
many=20bars.=20=20If=20it=20is=20t,=20use=20STYLE2=0Aexclusively.=0A=0A=
If=20INVENT=20is=20non-nil=20and=20the=20line's=20length=20is=20=
insufficient=20to=0Adisplay=20all=20NBARS=20bars=20(whether=20by=20=
replacing=20tabs=20or=20adding=0Aproperties=20to=20existing=20non-tab=20=
whitespace),=20bars=20will=20be=0A\"invented\".=20=20That=20is,=20the=20=
line's=20final=20newline,=20which=20is=20(only=0Ain=20this=20case)=20=
expected=20to=20be=20located=20at=20END,=20will=20have=20its=0Adisplay=20=
properties=20set=20to=20fill=20out=20the=20remaining=20bars,=20if=20any=20=
are=0Aneeded."=0A=20=20(let*=20((tabs=20(when=20(and=20indent-tabs-mode=0A=
=09=09=09=20=20(save-excursion=0A=09=09=09=20=20=20=20(goto-char=20=
start)=20(looking-at=20"^\t+")))=0A=09=09=20(-=20(match-end=200)=20=
(match-beginning=200))))=0A=09=20(vp=20indent-bars--offset)=0A=09=20=
(style=20(or=20style=20indent-bars-style))=0A=09=20(bar=201)=20prop=20=
fun=20tnum=20bars-drawn)=0A=20=20=20=20(when=20tabs=09=09=09=09;=20deal=20=
with=20initial=20tabs=0A=20=20=20=20=20=20(while=20(and=20(<=3D=20bar=20=
nbars)=20(<=20(setq=20tnum=20(/=20vp=20tab-width))=20tabs))=0A=09(setq=20=
bars-drawn=0A=09=20=20=20=20=20=20(indent-bars--tab-display=20style=20(+=20=
start=20tnum)=20(mod=20vp=20tab-width)=0A=09=09=09=09=09bar=20(-=20nbars=20=
bar=20-1)=0A=09=09=09=09=09switch-after=20style2))=0A=09(when=20=
(integerp=20switch-after)=0A=09=20=20(cl-decf=20switch-after=20=
bars-drawn)=0A=09=20=20(if=20(<=3D=20switch-after=200)=20(setq=20=
switch-after=20t)))=20;=20switch=20the=20rest=0A=09(cl-incf=20bar=20=
bars-drawn)=0A=09(cl-incf=20vp=20(*=20bars-drawn=20=
indent-bars-spacing)))=0A=20=20=20=20=20=20(cl-incf=20start=20(+=20(mod=20=
vp=20tab-width)=20(/=20vp=20tab-width))))=0A=20=20=20=20(when=20(<=3D=20=
bar=20nbars)=09=09;=20still=20bars=20to=20show=0A=20=20=20=20=20=20(if=20=
indent-bars--no-stipple=0A=09=20=20(setq=20prop=20'display=20fun=20=
#'indent-bars--no-stipple-char)=0A=09(setq=20prop=20'face=20fun=20=
#'indent-bars--face))=0A=20=20=20=20=20=20(let=20((pos=20(if=20tabs=20=
start=20(+=20start=20indent-bars--offset))))=0A=09(while=20(and=20(<=3D=20=
bar=20nbars)=20(<=20pos=20end))=0A=09=20=20(put-text-property=20pos=20=
(1+=20pos)=0A=09=09=09=20=20=20=20=20prop=20(funcall=20fun=0A=09=09=09=09=
=09=20=20=20(cond=20((integerp=20switch-after)=0A=09=09=09=09=09=09=20=20=
(prog1=0A=09=09=09=09=09=09=20=20=20=20=20=20(if=20(>=20switch-after=20=
0)=20style=20style2)=0A=09=09=09=09=09=09=20=20=20=20(cl-decf=20=
switch-after)=0A=09=09=09=09=09=09=20=20=20=20(if=20(<=3D=20switch-after=20=
0)=0A=09=09=09=09=09=09=09(setq=20switch-after=20t))))=0A=09=09=09=09=09=09=
=20((eq=20switch-after=20t)=20style2)=0A=09=09=09=09=09=09=20(t=20=
style))=0A=09=09=09=09=09=20=20=20bar))=0A=09=20=20(cl-incf=20bar)=0A=09=20=
=20(cl-incf=20pos=20indent-bars-spacing))=0A=09;;=20STILL=20bars=20to=20=
show:=20invent=20them=20(if=20requested)=0A=09(if=20(and=20invent=20(<=3D=20=
bar=20nbars))=0A=09=20=20=20=20(put-text-property=0A=09=20=20=20=20=20=
end=20(1+=20end)=20'display=0A=09=20=20=20=20=20(concat=20=
(indent-bars--blank-string=0A=09=09=20=20=20=20=20=20style=20(-=20pos=20=
end)=20(-=20nbars=20bar=20-1)=20bar=20nil=0A=09=09=20=20=20=20=20=20=
switch-after=20style2)=0A=09=09=20=20=20=20=20"\n")))))))=0A=0A;;;;=20=
Font=20Lock=0A(defvar-local=20indent-bars--font-lock-keywords=20nil)=0A=
(defvar=20indent-bars--font-lock-blank-line-keywords=20nil)=0A=0A(defvar=20=
indent-bars-orig-unfontify-region=20nil)=0A(defun=20=
indent-bars--unfontify=20(beg=20end)=0A=20=20"Unfontify=20region=20=
between=20BEG=20and=20END.=0ARemoves=20the=20display=20properties=20in=20=
addition=20to=20the=20normal=20managed=0Afont-lock=20properties."=0A=20=20=
(let=20((font-lock-extra-managed-props=0A=20=20=20=20=20=20=20=20=20=
(append=20'(display)=20font-lock-extra-managed-props)))=0A=20=20=20=20=
(funcall=20indent-bars-orig-unfontify-region=20beg=20end)))=0A=0A(defun=20=
indent-bars--display=20(beg=20end=20&optional=20style=20switch-after=20=
style2)=0A=20=20"Draw=20indentation=20bars=20from=20BEG..END,=20based=20=
on=20line=20contents.=0ABEG=20and=20END=20should=20be=20on=20the=20same=20=
line.=20=20STYLE,=20SWITCH-AFTER=20and=0ASTYLE2=20are=20as=20in=20=
`indent-bars--draw-line'.=20=20If=20STYLE=20is=20not=0Apassed,=20uses=20=
`indent-bars-style'=20for=20drawing."=0A=20=20(let=20((n=20=
(save-excursion=0A=09=20=20=20=20=20(goto-char=20beg)=0A=09=20=20=20=20=20=
(indent-bars--current-indentation-depth))))=0A=20=20=20=20(when=20(>=20n=20=
0)=20(indent-bars--draw-line=20style=20n=20beg=20end=20nil=0A=09=09=09=09=
=09=20=20switch-after=20style2))))=0A=0A(defsubst=20=
indent-bars--context-bars=20(end)=0A=20=20"Maximum=20number=20of=20bars=20=
at=20point=20and=20END.=0AMoves=20point."=0A=20=20(max=20=
(indent-bars--current-indentation-depth)=0A=20=20=20=20=20=20=20(progn=0A=
=09=20(goto-char=20(1+=20end))=09=09;=20end=20is=20always=20eol=0A=09=20=
(indent-bars--current-indentation-depth))))=0A=0A(defun=20=
indent-bars--handle-blank-lines=20(beg=20end=20&optional=20style=20=
switch-after=20style2)=0A=20=20"Display=20the=20appropriate=20bars=20=
over=20the=20blank-only=20lines=20from=20BEG..END.=0AOnly=20called=20if=20=
`indent-bars-display-on-blank-lines'=20is=20non-nil.=0ATo=20be=20called=20=
on=20complete=20multi-line=20blank=20line=20regions.=0A=0AIt=20is=20=
ambigious=20how=20many=20bars=20to=20draw=20on=20each=20line=20in=20a=20=
stretch=0Aof=20blank=20lines,=20so=20this=20uses=20the=20maximum=20depth=20=
of=20the=20surrounding=0Aline=20indentation,=20above=20and=20below.=20=20=
Drawing=20using=0A`indent-bars--draw-line'.=20=20STYLE,=20SWITCH-AFTER=20=
and=20STYLE2=20are=20as=0Ain=20`indent-bars--draw-line'.=0A=0ANote:=20=
blank=20lines=20at=20the=20very=20beginning=20or=20end=20of=20the=20=
buffer=20are=0Anot=20indicated,=20even=20if=20they=20otherwise=20would=20=
be.=20=20This=20function=20is=0Aconfigured=20by=20default=20in=20=
`indent-bars--handle-blank-lines-form'."=0A=20=20(let=20((pm=20=
(point-max))=20ctxbars)=0A=20=20=20=20(save-excursion=0A=20=20=20=20=20=20=
(goto-char=20(1-=20beg))=0A=20=20=20=20=20=20(beginning-of-line=201)=0A=20=
=20=20=20=20=20(when=20(>=20(setq=20ctxbars=20(indent-bars--context-bars=20=
end))=200)=0A=09(goto-char=20beg)=0A=09(while=20(<=20(point)=20end)=20=
;note:=20end=20extends=201=20char=20beyond=20blank=20line=20range=0A=09=20=
=20(let*=20((bp=20(line-beginning-position))=0A=09=09=20(ep=20=
(line-end-position)))=0A=09=20=20=20=20(unless=20(=3D=20ep=20pm)=0A=09=20=
=20=20=20=20=20(indent-bars--draw-line=20style=20ctxbars=20bp=20ep=20=
'invent=0A=09=09=09=09=20=20=20=20=20=20switch-after=20style2))=0A=09=20=20=
=20=20(beginning-of-line=202)))))=0A=20=20=20=20nil))=0A=0A(defvar=20=
font-lock-beg)=20(defvar=20font-lock-end)=20;=20Dynamic=20font-lock=20=
variables!=0A(defun=20indent-bars--extend-blank-line-regions=20()=0A=20=20=
"Extend=20the=20region=20about=20to=20be=20font-locked=20to=20include=20=
stretches=20of=20blank=20lines."=0A=20=20;;=20(message=20"request=20to=20=
extend:=20%d->%d"=20font-lock-beg=20font-lock-end)=0A=20=20(let=20=
((changed=20nil)=20(chars=20"=20\t\n"))=0A=20=20=20=20(goto-char=20=
font-lock-beg)=0A=20=20=20=20(when=20(<=20(skip-chars-backward=20chars)=20=
0)=0A=20=20=20=20=20=20(unless=20(bolp)=20(beginning-of-line=202))=20;=20=
spaces=20at=20end=20don't=20count=0A=20=20=20=20=20=20(when=20(<=20=
(point)=20font-lock-beg)=0A=09(setq=20changed=20t=20font-lock-beg=20=
(point))))=0A=20=20=20=20(goto-char=20font-lock-end)=0A=20=20=20=20(when=20=
(>=20(skip-chars-forward=20chars)=200)=0A=20=20=20=20=20=20(unless=20=
(bolp)=20(beginning-of-line=201))=0A=20=20=20=20=20=20(when=20(>=20=
(point)=20font-lock-end)=0A=09(setq=20changed=20t=20font-lock-end=20=
(point))))=0A=20=20=20=20;;=20(if=20changed=20(message=20"expanded=20to=20=
%d->%d"=20font-lock-beg=20font-lock-end))=0A=20=20=20=20changed))=0A=0A=
;;;;=20Current=20indentation=20depth=20highlighting=0A(defvar-local=20=
indent-bars--current-depth=200)=0A(defvar-local=20indent-bars--remaps=20=
nil=0A=20=20"An=20alist=20of=20active=20face-remap=20cookies=20for=20=
faces.=0AKeyed=20by=20style=20tag=20(nil=20for=20the=20main=20style).=20=20=
These=20remaps=20are=0Aused=20to=20change=20the=20current=20depth=20=
highlight=20(aside=20from=20stipple=20changes).")=0A=0A(defun=20=
indent-bars--current-bg-color=20(style)=0A=20=20"Return=20the=20current=20=
bar=20background=20color=20appropriate=20for=20STYLE."=0A=20=20(when-let=20=
((hcd=0A=09=20=20=20=20=20=20(indent-bars--style=20style=20=
"highlight-current-depth")))=0A=20=20=20=20(plist-get=20hcd=20=
:background)))=0A=0A(defun=20indent-bars--current-depth-stipple=20=
(&optional=20w=20h=20rot=20style)=0A=20=20"Return=20the=20current=20=
depth=20stipple=20highlight=20(if=20any)=20for=20STYLE.=0AOne=20of=20the=20=
keywords=20:width,=20:pad,=20:pattern,=20or=20:zigzag=20must=20be=0Aset=20=
in=20`indent-bars-highlight-current-depth'=20config.=20=20W,=20H,=20and=0A=
ROT=20are=20as=20in=20`indent-bars--stipple',=20and=20have=20similar=20=
default=20values."=0A=20=20(cl-destructuring-bind=20(&key=20width=20pad=20=
pattern=20zigzag=20&allow-other-keys)=0A=20=20=20=20=20=20=
(indent-bars--style=20style=20"highlight-current-depth")=0A=20=20=20=20=
(when=20(or=20width=20pad=20pattern=20zigzag)=0A=20=20=20=20=20=20(let*=20=
((w=20(or=20w=20(window-font-width)))=0A=09=20=20=20=20=20(h=20(or=20h=20=
(window-font-height)))=0A=09=20=20=20=20=20(rot=20(or=20rot=20=
(indent-bars--stipple-rot=20nil=20w))))=0A=09(indent-bars--stipple=20w=20=
h=20rot=20style=20width=20pad=20pattern=20zigzag)))))=0A=0A(defvar-local=20=
indent-bars--highlight-timer=20nil)=0A(defun=20=
indent-bars--update-current-depth-highlight=20(depth)=0A=20=20"Update=20=
highlight=20for=20the=20current=20DEPTH.=0AWorks=20by=20remapping=20the=20=
appropriate=20indent-bars[-tag]-N=20face=20for=0Aall=20styles=20in=20the=20=
`indent-bars--styles'=20list.=20=20DEPTH=20should=20be=0Agreater=20than=20=
zero."=0A=20=20(setq=20indent-bars--highlight-timer=20nil)=0A=20=20=
(dolist=20(s=20indent-bars--styles)=0A=20=20=20=20(let*=20((face=20=
(indent-bars--face=20s=20depth))=0A=09=20=20=20(hl-col=20(and=20=
(ibs/current-depth-palette=20s)=0A=09=09=09(indent-bars--get-color=20s=20=
depth=20'highlight)))=0A=09=20=20=20(hl-bg=20(ibs/current-bg-color=20=
s)))=0A=20=20=20=20=20=20(when=20(or=20hl-col=20hl-bg=20=
(ibs/current-stipple-face=20s))=0A=09(when-let=20((c=20(alist-get=20=
(ibs/tag=20s)=20indent-bars--remaps)))=20;=20out=20with=20the=20old=0A=09=
=20=20(face-remap-remove-relative=20c))=0A=09(setf=20(alist-get=20=
(ibs/tag=20s)=20indent-bars--remaps)=0A=09=20=20=20=20=20=20=
(face-remap-add-relative=0A=09=20=20=20=20=20=20=20face=0A=09=20=20=20=20=
=20=20=20`(,@(when=20hl-col=20`(:foreground=20,hl-col))=0A=09=09=20=
,@(when=20hl-bg=20`(:background=20,hl-bg)))=0A=09=20=20=20=20=20=20=20=
(ibs/current-stipple-face=20s)))))))=0A=0A(defun=20=
indent-bars--update-current-depth-highlight-in-buffer=20(buf=20depth)=0A=20=
=20"Highlight=20bar=20at=20DEPTH=20in=20buffer=20BUF."=0A=20=20(if=20=
(buffer-live-p=20buf)=0A=20=20=20=20=20=20(with-current-buffer=20buf=0A=09=
(indent-bars--update-current-depth-highlight=20depth))))=0A=0A(defun=20=
indent-bars--highlight-current-depth=20(&optional=20force)=0A=20=20=
"Refresh=20current=20indentation=20depth=20highlight.=0ARate=20limit=20=
set=20by=20`indent-bars-depth-update-delay'.=20=20If=20FORCE=20is=0A=
non-nil,=20update=20depth=20even=20if=20it=20has=20not=20changed."=0A=20=20=
(unless=20(or=20indent-bars--highlight-timer=20(not=20indent-bars-mode))=0A=
=20=20=20=20(let*=20((depth=20(indent-bars--current-indentation-depth=0A=09=
=09=20=20=20indent-bars-highlight-selection-method)))=0A=20=20=20=20=20=20=
(when=20(and=20depth=20(or=20force=20(not=20(=3D=20depth=20=
indent-bars--current-depth)))=0A=09=09=20(>=20depth=200))=0A=09(setq=20=
indent-bars--current-depth=20depth)=0A=09(if=20(zerop=20=
indent-bars-depth-update-delay)=0A=09=20=20=20=20=
(indent-bars--update-current-depth-highlight=20depth)=0A=09=20=20(setq=20=
indent-bars--highlight-timer=0A=09=09(run-with-idle-timer=0A=09=09=20=
indent-bars-depth-update-delay=20nil=0A=09=09=20=
#'indent-bars--update-current-depth-highlight-in-buffer=0A=09=09=20=
(current-buffer)=20depth)))))))=0A=0A;;;;=20Stipple=20Display=0A=
(defsubst=20indent-bars--block=20(n)=0A=20=20"Create=20a=20block=20of=20=
N=20low-order=201=20bits."=0A=20=20(-=20(ash=201=20n)=201))=0A=0A(defun=20=
indent-bars--stipple-rot=20(win=20w)=0A=20=20"Return=20the=20stipple=20=
rotation=20for=20window=20WIN=20and=20pattern=20width=20W.=0AWIN=20=
defaults=20to=20the=20selected=20window=20if=20nil."=0A=20=20(mod=20(car=20=
(window-edges=20win=20t=20nil=20t))=20w))=0A=0A(defun=20indent-bars--rot=20=
(num=20w=20n)=0A=20=20"Shift=20number=20NUM=20of=20W=20bits=20up=20by=20=
N=20bits,=20carrying=20around=20to=20the=20low=20bits.=0AN=20should=20be=20=
strictly=20less=20than=20W=20and=20the=20returned=20value=20will=20fit=0A=
within=20W=20bits."=0A=20=20(logand=20(indent-bars--block=20w)=20(logior=20=
(ash=20num=20n)=20(ash=20num=20(-=20n=20w)))))=0A=0A(defun=20=
indent-bars--row-data=20(w=20pad=20rot=20width-frac)=0A=20=20"Calculate=20=
stipple=20row=20data=20to=20fit=20in=20character=20of=20width=20W.=0AThe=20=
width=20of=20the=20pattern=20of=20filled=20pixels=20is=20determined=20by=0A=
WIDTH-FRAC.=20=20The=20pattern=20itself=20is=20shifted=20up=20by=20PAD=20=
bits=20(which=0Ashifts=20the=20pattern=20to=20the=20right,=20for=20=
positive=20values=20of=20PAD).=0ASubsequently,=20the=20value=20is=20=
shifted=20up=20(with=20W-bit=20wrap-around)=20by=0AROT=20bits,=20and=20=
returned.=20=20ROT=20is=20the=20starting=20bit=20offset=20of=20a=0A=
character=20within=20the=20closest=20stipple=20repeat=20to=20the=20left;=20=
i.e.=20if=0Apixel=201=20of=20the=20stipple=20aligns=20with=20pixel=201=20=
of=20the=20chacter,=20ROT=3D0.=0AROT=20should=20be=20less=20than=20W."=0A=
=20=20(let*=20((bar-width=20(max=201=20(round=20(*=20w=20width-frac))))=0A=
=09=20(num=20(indent-bars--rot=0A=09=20=20=20=20=20=20=20(ash=20=
(indent-bars--block=20bar-width)=20pad)=20w=20rot)))=0A=20=20=20=20=
(apply=20#'unibyte-string=0A=09=20=20=20(cl-loop=20for=20boff=20=3D=200=20=
then=20(+=20boff=208)=20while=20(<=20boff=20w)=0A=09=09=20=20=20=20for=20=
nbits=20=3D=20(min=208=20(-=20w=20boff))=0A=09=09=20=20=20=20collect=20=
(ash=20(logand=20num=0A=09=09=09=09=09=20(ash=20(indent-bars--block=20=
nbits)=20boff))=0A=09=09=09=09=20(-=20boff))))))=0A=0A;;=20**=20Notes=20=
on=20the=20stipples:=0A;;=0A;;=20indent-bars=20by=20default=20uses=20a=20=
selectively-revealed=20stipple=20pattern=0A;;=20with=20a=20width=20=
equivalent=20to=20the=20(presumed=20fixed)=20width=20of=20individual=0A=
;;=20characters=20to=20efficiently=20draw=20bars.=20=20A=20stipple=20=
pattern=20is=20drawn=20as=0A;;=20a=20fixed=20repeating=20bit=20pattern,=20=
with=20its=20lowest=20bits=20and=20earlier=0A;;=20bytes=20leftmost.=20=20=
It=20is=20drawn=20with=20respect=20to=20the=20*entire=20frame*,=0A;;=20=
with=20its=20first=20bit=20aligned=20with=20the=20first=20(leftmost)=20=
frame=20pixel.=0A;;=20=0A;;=20Turning=20on=20:stipple=20for=20a=20=
character=20merely=20"opens=20a=20window"=20on=20that=0A;;=20=
frame-filling,=20repeating=20stipple=20pattern.=20=20Since=20the=20=
pattern=20starts=0A;;=20outside=20the=20body=20(in=20literally=20the=20=
first=20frame=20pixel,=20typically=0A;;=20within=20the=20fringe),=20you=20=
must=20consider=20the=20shift=20between=20the=20first=0A;;=20pixel=20of=20=
a=20window=20character=20and=20the=20first=20pixel=20of=20the=20=
repeating=0A;;=20stipple=20block=20at=20that=20pixel=20position=20or=20=
above:=0A;;=20=0A;;=20=20=20=20=20|<-frame=20edge=20|<---buffer/window=20=
edge=0A;;=20=20=20=20=20|<--w-->|<--w-->|<--w-->|=20=20=20=20=20w=20=3D=20=
pattern=20width=0A;;=20=20=20=20=20|=20marg+fringe=20|<-chr->|=20=20=20=20=
=20chr=20=3D=20character=20width=20=3D=20w=0A;;=20=20=20=20=20=20=20=20=20=
=20=20=20=20|<-g->|=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20g=20=3D=20=
gutter=20offset=20of=20chr=20start,=20g<w=0A;;=0A;;=20Or,=20when=20the=20=
character=20width=20exceeds=20the=20margin/fringe=20offset:=0A;;=20=0A;;=20=
=20=20=20=20|<-frame=20edge=20|<---buffer/window=20edge=0A;;=20=20=20=20=20=
|<--------w-------->|<---------w-------->|=0A;;=20=20=20=20=20|=20=
marg+fringe=20|<-------chr------->|=0A;;=20=20=20=20=20|<-----g----->|=0A=
;;=0A;;=20So=20g=20=3D=20(mod=20marg+fringe=20w).=0A;;=20=0A;;=20When=20=
the=20block/zigzag/whatever=20stipple=20pattern=20is=20made,=20to=20=
align=0A;;=20with=20characters,=20it=20must=20get=20shifted=20up=20(=3D=20=
right)=20by=20g=20bits,=20with=0A;;=20carry=20over=20(wrap)=20around=20=
w=3D(window-font-width)=20bits=20(i.e=20the=20width=0A;;=20of=20the=20=
bitmap).=20=20The=20byte/bit=20pattern=20is=20first-lowest-leftmost.=0A=
;;=0A;;=20Note=20that=20different=20windows=20may=20have=20different=20g=20=
values=0A;;=20(e.g.=20left/right),=20which=20means=20the=20same=20bitmap=20=
cannot=20work=20for=20the=0A;;=20buffer=20in=20both=20windows.=20=20In=20=
practice=20that=20means=20that=20all=20stipple=0A;;=20face=20attributes=20=
must=20be=20set=20via=20filtered=20face=20remaps,=20with=20the=0A;;=20=
filter=20set=20to=20match=20the=20pattern=20size=20(width=20and=20=
height)=20as=20well=20as=0A;;=20gutter=20offset=20"rot"=20value=20in=20=
that=20window,=20which=20we=20combine=20into=20a=0A;;=20single=20=
integer.=0A;;=0A;;=20Note:=20a=20bug=20in=20Emacs=20<29=20means=20=
`face-remapping-alist'=20is=0A;;=20unintentionally=20shared=20between=20=
indirect=20and=20master=20buffers.=20=20Fixed=0A;;=20in=20Emacs=2029.=0A=0A=
(defun=20indent-bars--stipple=20(w=20h=20rot=20&optional=20style=0A=09=09=
=09=20=20=20=20=20=20=20width-frac=20pad-frac=20pattern=20zigzag)=0A=20=20=
"Calculate=20stipple=20bitmap=20pattern=20for=20char=20width=20W=20and=20=
height=20H.=0AROT=20is=20the=20number=20of=20bits=20to=20rotate=20the=20=
pattern=20around=20to=20the=0Aright=20(with=20wrap).=0A=0AUses=20=
configuration=20variables=20`indent-bars-width-frac',=0A=
`indent-bars-pad-frac',=20`indent-bars-pattern',=20and=0A=
`indent-bars-zigzag',=20unless=20PAD-FRAC,=20WIDTH-FRAC,=20PATTERN,=0A=
and/or=20ZIGZAG=20are=20set=20(the=20latter=20overriding=20the=20config=0A=
variables,=20which=20see).=20=20If=20STYLE=20is=20set,=20use=20config=20=
variables=0Aappropriate=20for=20that=20style."=0A=20=20(unless=20(or=20=
(not=20(display-graphic-p))=20indent-bars-prefer-character)=0A=20=20=20=20=
(let*=20((rowbytes=20(/=20(+=20w=207)=208))=0A=09=20=20=20(pattern=20(or=20=
pattern=20(indent-bars--style=20style=20"pattern")))=0A=09=20=20=20(pat=20=
(if=20(<=20h=20(length=20pattern))=20(substring=20pattern=200=20h)=20=
pattern))=0A=09=20=20=20(plen=20(length=20pat))=0A=09=20=20=20(chunk=20=
(/=20(float=20h)=20plen))=0A=09=20=20=20(small=20(floor=20chunk))=0A=09=20=
=20=20(large=20(ceiling=20chunk))=0A=09=20=20=20(pad-frac=20(or=20=
pad-frac=20(indent-bars--style=20style=20"pad-frac")))=0A=09=20=20=20=
(pad=20(round=20(*=20w=20pad-frac)))=0A=09=20=20=20(zigzag=20(or=20=
zigzag=20(indent-bars--style=20style=20"zigzag")))=0A=09=20=20=20(zz=20=
(if=20zigzag=20(round=20(*=20w=20zigzag))=200))=0A=09=20=20=20(zeroes=20=
(make-string=20rowbytes=20?\0))=0A=09=20=20=20(width-frac=20(or=20=
width-frac=20(indent-bars--style=20style=20"width-frac")))=0A=09=20=20=20=
(dlist=20(if=20(and=20(=3D=20plen=201)=20(not=20(string=3D=20pat=20"=20=
")))=20;=20solid=20bar=0A=09=09=20=20=20=20=20=20(list=20=
(indent-bars--row-data=20w=20pad=20rot=20width-frac))=20;=20one=20row=0A=09=
=09=20=20=20=20(cl-loop=20for=20last-fill-char=20=3D=20nil=20then=20x=0A=09=
=09=09=20=20=20=20=20for=20x=20across=20pat=0A=09=09=09=20=20=20=20=20=
for=20n=20=3D=20small=20then=20(if=20(and=20(/=3D=20x=20?\s)=20(=3D=20n=20=
small))=0A=09=09=09=09=09=09=20=20=20=20large=0A=09=09=09=09=09=09=20=20=
small)=0A=09=09=09=20=20=20=20=20for=20zoff=20=3D=20zz=20then=20(if=20=
(and=20last-fill-char=0A=09=09=09=09=09=09=09=20(/=3D=20x=20?\s)=0A=09=09=
=09=09=09=09=09=20(/=3D=20x=20last-fill-char))=0A=09=09=09=09=09=09=20=20=
=20=20(-=20zoff)=20zoff)=0A=09=09=09=20=20=20=20=20for=20row=20=3D=20(if=20=
(=3D=20x=20?\s)=20zeroes=0A=09=09=09=09=09=20(indent-bars--row-data=20w=20=
(+=20pad=20zoff)=0A=09=09=09=09=09=09=09=09rot=20width-frac))=0A=09=09=09=
=20=20=20=20=20append=20(cl-loop=20repeat=20n=20collect=20row)))))=0A=20=20=
=20=20=20=20(list=20w=20(length=20dlist)=20(string-join=20dlist)))))=0A=0A=
(defsubst=20indent-bars--whr=20(w=20h=20r)=0A=20=20"Encoded=20value=20=
for=20W,=20H=20and=20R."=0A=20=20(+=20(ash=20w=2016)=20(ash=20h=208)=20=
r))=0A=0A(defvar-local=20indent-bars--stipple-remaps=20nil=0A=20=20"A=20=
hash=20table=20of=20active=20stipple=20face-remap=20cookies.=0AThe=20=
hash=20table=20is=20keyed=20by=20the=20`whr',=20an=20integer=20composing=20=
font=0Awidth,=20height,=20and=20window=20starting=20offset=20(see=0A=
`indent-bars--whr').=20=20Elements=20of=20the=20hash=20table=20are=20=
plists=20with=0Athe=20following=20keys:=0A=0A=20=20(:main[-styletag]=20=
...=20:current[-styletag]=20...)=0A=0AThese=20are=20used=20to=20update=20=
relevant=20stipples=20for=20main=20and=20current=0Astipple=20faces=20for=20=
the=20various=20styles=20as=20character=20size=20and/or=0Awindow=20pixel=20=
start=20(pattern=20rotation=20\"rot\")=20change,=20due=20to=0Awindow=20=
layout=20or=20font=20size=20changes.=20=20Note=20that=20all=20stipple=0A=
attributes=20are=20set=20via=20filtered=20face=20remaps.")=0A=0A=
(defsubst=20indent-bars--remove-plist-remaps=20(pl)=0A=20=20"Remove=20=
remaps=20key=20values=20from=20plist=20PL."=0A=20=20(cl-loop=20for=20(_k=20=
r)=20on=20pl=20by=20#'cddr=20do=0A=09=20=20=20=
(face-remap-remove-relative=20r)))=0A=0A(defun=20=
indent-bars--create-stipple-remaps=20(w=20h=20rot)=0A=20=20"Create=20and=20=
store=20stipple=20remaps=20for=20the=20given=20font=20size=20and=20pixel=20=
start.=0AW=20is=20the=20`window-font-width',=20H=20is=20the=20=
corresponding=20height,=20and=0AROT=20is=20the=20number=20of=20bits=20to=20=
rotate=20the=20pattern=20start.=20=20An=20entry=0Ais=20created=20for=20=
each=20active=20style,=20for=20both=20:main[-styletag]=20and=0A=
:current[-styletag]=20highlight=20contexts."=0A=20=20(let*=20((whr=20=
(indent-bars--whr=20w=20h=20rot))=0A=09=20(filter=20`(:window=20=
indent-bars-whr=20,whr))=0A=09=20remap)=0A=20=20=20=20(dolist=20(s=20=
indent-bars--styles)=0A=20=20=20=20=20=20;;=20Main=20stipple=20face=0A=20=
=20=20=20=20=20(when=20(ibs/stipple-face=20s)=0A=09(setq=20remap=0A=09=20=
=20=20=20=20=20(plist-put=0A=09=20=20=20=20=20=20=20remap=20=
(indent-bars--tag=20":main%s"=20s)=0A=09=20=20=20=20=20=20=20=
(face-remap-add-relative=0A=09=09(ibs/stipple-face=20s)=0A=09=09`(=20=
:filtered=20,filter=0A=09=09=20=20=20(:stipple=20,(indent-bars--stipple=20=
w=20h=20rot=20s)))))))=0A=20=20=20=20=20=20;;=20Currently=20highlighted=20=
stipple=20face=0A=20=20=20=20=20=20(when=20(ibs/current-stipple-face=20=
s)=0A=09(setq=20remap=0A=09=20=20=20=20=20=20(plist-put=0A=09=20=20=20=20=
=20=20=20remap=20(indent-bars--tag=20":current%s"=20s)=0A=09=20=20=20=20=20=
=20=20(face-remap-add-relative=0A=09=09(ibs/current-stipple-face=20s)=0A=09=
=09`(=20:filtered=20,filter=0A=09=09=20=20=20(:stipple=20=
,(indent-bars--current-depth-stipple=20w=20h=20rot=20s))))))=0A=09=
(indent-bars--highlight-current-depth=20'force)))=0A=20=20=20=20(puthash=20=
whr=20remap=20indent-bars--stipple-remaps)))=0A=0A;;;;=20Window=20change=20=
and=20cleanup=0A(defvar-local=20indent-bars--needs-cleanup=20nil)=0A=
(defun=20indent-bars--schedule-remap-cleanup=20()=0A=20=20"Schedule=20=
cleanup=20of=20stipple=20remaps,=20unless=20already=20scheduled."=0A=20=20=
(unless=20indent-bars--needs-cleanup=0A=20=20=20=20(setq=20=
indent-bars--needs-cleanup=20t)=0A=20=20=20=20(run-with-idle-timer=0A=20=20=
=20=20=201=20nil=0A=20=20=20=20=20(apply-partially=20=
#'indent-bars--cleanup-stipple-remaps=20(current-buffer)))))=0A=0A(defun=20=
indent-bars--update-all-stipples=20()=0A=20=20"Update=20all=20stipples=20=
for=20current=20buffer."=0A=20=20(dolist=20(w=20(get-buffer-window-list=20=
nil=20nil=20t))=0A=20=20=20=20(indent-bars--window-change=20w)))=0A=0A=
(defun=20indent-bars--window-change=20(&optional=20win)=0A=20=20"Update=20=
the=20stipples=20for=20buffer=20in=20window=20WIN.=0AWIN=20defaults=20to=20=
the=20selected=20window.=20=20To=20be=20set=20as=20a=20local=0A=
`window-state-change-functions'=20hook."=0A=20=20(unless=20win=20(setq=20=
win=20(selected-window)))=0A=20=20(let*=20((w=20(window-font-width=20=
win))=0A=09=20(h=20(window-font-height=20win))=0A=09=20(rot=20=
(indent-bars--stipple-rot=20win=20w))=0A=09=20(whr=20(indent-bars--whr=20=
w=20h=20rot))=0A=09=20(cur-whr=20(window-parameter=20win=20=
'indent-bars-whr)))=0A=20=20=20=20(unless=20(eq=20cur-whr=20whr)=0A=20=20=
=20=20=20=20(set-window-parameter=20win=20'indent-bars-whr=20whr))=0A=20=20=
=20=20(when-let=20((buf=20(window-buffer=20win))=0A=09=20=20=20=20=20=20=20=
(ht=20(buffer-local-value=20'indent-bars--stipple-remaps=20buf))=0A=09=20=
=20=20=20=20=20=20((null=20(gethash=20whr=20ht))))=0A=20=20=20=20=20=20=
(with-current-buffer=20buf=20;=20we=20may=20be=20called=20from=20an=20=
arbitrary=20buffer=0A=09(indent-bars--create-stipple-remaps=20w=20h=20=
rot)=0A=09(indent-bars--schedule-remap-cleanup)))))=0A=0A(defun=20=
indent-bars--cleanup-stipple-remaps=20(buf)=0A=20=20"Clean=20up=20unused=20=
stipple=20face=20remaps=20for=20buffer=20BUF."=0A=20=20(when-let=20=
(((buffer-live-p=20buf))=0A=09=20=20=20=20=20(wins=20=
(get-buffer-window-list=20buf=20nil=20t))=0A=09=20=20=20=20=20(whrs=20=
(cl-loop=20for=20win=20in=20wins=0A=09=09=09=20=20=20=20collect=20=
(window-parameter=20win=20'indent-bars-whr)))=0A=09=20=20=20=20=20=
(rmp-hsh=20(buffer-local-value=20'indent-bars--stipple-remaps=20buf))=0A=09=
=20=20=20=20=20(rkeys=20(hash-table-keys=20rmp-hsh))=0A=09=20=20=20=20=20=
(unneeded=20(seq-difference=20rkeys=20whrs)))=0A=20=20=20=20=
(with-current-buffer=20buf=20;N.B.=20face-remapping-alist=20is=20=
buffer-local=0A=20=20=20=20=20=20(dolist=20(rk=20unneeded)=0A=09=
(indent-bars--remove-plist-remaps=20(gethash=20rk=20rmp-hsh))=0A=09=
(remhash=20rk=20rmp-hsh)))=0A=20=20=20=20(setf=20(buffer-local-value=20=
'indent-bars--needs-cleanup=20buf)=20nil)))=0A=0A;;;;=20Setup=20and=20=
mode=0A(defun=20indent-bars--guess-spacing=20()=0A=20=20"Get=20=
indentation=20spacing=20of=20current=20buffer.=0AAdapted=20from=20=
`highlight-indentation-mode'."=0A=20=20(cond=0A=20=20=20=
(indent-bars-spacing-override)=0A=20=20=20((and=20(derived-mode-p=20=
'python-mode)=20(boundp=20'py-indent-offset))=0A=20=20=20=20=
py-indent-offset)=0A=20=20=20((and=20(derived-mode-p=20'python-mode)=20=
(boundp=20'python-indent-offset))=0A=20=20=20=20python-indent-offset)=0A=20=
=20=20((and=20(derived-mode-p=20'ruby-mode)=20(boundp=20=
'ruby-indent-level))=0A=20=20=20=20ruby-indent-level)=0A=20=20=20((and=20=
(derived-mode-p=20'scala-mode)=20(boundp=20'scala-indent:step))=0A=20=20=20=
=20scala-indent:step)=0A=20=20=20((and=20(derived-mode-p=20'scala-mode)=20=
(boundp=20'scala-mode-indent:step))=0A=20=20=20=20=
scala-mode-indent:step)=0A=20=20=20((and=20(or=20(derived-mode-p=20=
'scss-mode)=20(derived-mode-p=20'css-mode))=0A=09=20(boundp=20=
'css-indent-offset))=0A=20=20=20=20css-indent-offset)=0A=20=20=20((and=20=
(derived-mode-p=20'nxml-mode)=20(boundp=20'nxml-child-indent))=0A=20=20=20=
=20nxml-child-indent)=0A=20=20=20((and=20(derived-mode-p=20'coffee-mode)=20=
(boundp=20'coffee-tab-width))=0A=20=20=20=20coffee-tab-width)=0A=20=20=20=
((and=20(derived-mode-p=20'js-mode)=20(boundp=20'js-indent-level))=0A=20=20=
=20=20js-indent-level)=0A=20=20=20((and=20(derived-mode-p=20'js2-mode)=20=
(boundp=20'js2-basic-offset))=0A=20=20=20=20js2-basic-offset)=0A=20=20=20=
((and=20(derived-mode-p=20'sws-mode)=20(boundp=20'sws-tab-width))=0A=20=20=
=20=20sws-tab-width)=0A=20=20=20((and=20(derived-mode-p=20'web-mode)=20=
(boundp=20'web-mode-markup-indent-offset))=0A=20=20=20=20=
web-mode-markup-indent-offset)=0A=20=20=20((and=20(derived-mode-p=20=
'web-mode)=20(boundp=20'web-mode-html-offset))=20;=20old=20var=0A=20=20=20=
=20web-mode-html-offset)=0A=20=20=20((and=20(local-variable-p=20=
'c-basic-offset)=20(numberp=20c-basic-offset))=0A=20=20=20=20=
c-basic-offset)=0A=20=20=20((and=20(local-variable-p=20=
'c-ts-common-indent-offset)=0A=09=20(symbolp=20=
c-ts-common-indent-offset))=0A=20=20=20=20(numberp=20(symbol-value=20=
c-ts-common-indent-offset)))=0A=20=20=20((and=20(derived-mode-p=20=
'yaml-mode)=20(boundp=20'yaml-indent-offset))=0A=20=20=20=20=
yaml-indent-offset)=0A=20=20=20((and=20(derived-mode-p=20'elixir-mode)=20=
(boundp=20'elixir-smie-indent-basic))=0A=20=20=20=20=
elixir-smie-indent-basic)=0A=20=20=20((and=20(derived-mode-p=20=
'lisp-data-mode)=20(boundp=20'lisp-body-indent))=0A=20=20=20=20=
lisp-body-indent)=0A=20=20=20((and=20(derived-mode-p=20'cobol-mode)=20=
(boundp=20'cobol-tab-width))=0A=20=20=20=20cobol-tab-width)=0A=20=20=20=
((or=20(derived-mode-p=20'go-ts-mode)=20(derived-mode-p=20'go-mode))=0A=20=
=20=20=20tab-width)=0A=20=20=20((derived-mode-p=20'nix-mode)=0A=20=20=20=20=
tab-width)=0A=20=20=20((and=20(derived-mode-p=20'nix-ts-mode)=20(boundp=20=
'nix-ts-mode-indent-offset))=0A=20=20=20=20nix-ts-mode-indent-offset)=0A=20=
=20=20((and=20(derived-mode-p=20'json-ts-mode)=20(boundp=20=
'json-ts-mode-indent-offset))=0A=20=20=20=20json-ts-mode-indent-offset)=0A=
=20=20=20((and=20(derived-mode-p=20'json-mode)=20(boundp=20=
'js-indent-level))=0A=20=20=20=20js-indent-level)=0A=20=20=20((and=20=
(boundp=20'standard-indent)=20standard-indent))=0A=20=20=20(t=204)))=20=09=
=09=09=09;=20backup=0A=0A(defvar=20indent-bars--display-form=0A=20=20=
'(indent-bars--display=20(match-beginning=201)=20(match-end=201)))=0A=
(defvar=20indent-bars--handle-blank-lines-form=0A=20=20=
'(indent-bars--handle-blank-lines=20(match-beginning=200)=20(match-end=20=
0)))=0A(defun=20indent-bars--setup-font-lock=20()=0A=20=20"Setup=20font=20=
lock=20keywords=20and=20functions=20for=20indent-bars."=0A=20=20(unless=20=
(eq=20font-lock-unfontify-region-function=20#'indent-bars--unfontify)=0A=20=
=20=20=20(setq=20indent-bars-orig-unfontify-region=20=
font-lock-unfontify-region-function))=0A=20=20(setq-local=20=
font-lock-unfontify-region-function=20#'indent-bars--unfontify)=0A=20=20=
(setq=20indent-bars--font-lock-keywords=20;=20basic=20blank=20prefix=20=
detection=0A=09`((,(rx-to-string=20`(seq=20bol=0A=09=09=09=09(group=0A=09=
=09=09=09=20,(if=20(not=20indent-tabs-mode)=0A=09=09=09=09=20=20=20=20=20=
=20`(>=3D=20,(1+=20indent-bars--offset)=20?\s)=0A=09=09=09=09=20=20=20=20=
'(+=20(any=20?\t=20?\s))))=0A=09=09=09=09(not=20(any=20?\t=20?\s=20=
?\n))))=0A=09=20=20=20(1=20,indent-bars--display-form))))=0A=20=20=
(font-lock-add-keywords=20nil=20indent-bars--font-lock-keywords=20t)=0A=20=
=20(if=20indent-bars-display-on-blank-lines=0A=20=20=20=20=20=20(let=20=
((re=20(rx=20bol=20(*=20(or=20?\s=20?\t=20?\n))=20?\n)))=20;=20=
multi-line=20blank=20regions=0A=09(setq=20=
indent-bars--font-lock-blank-line-keywords=0A=09=20=20=20=20=20=20`((,re=20=
(0=20,indent-bars--handle-blank-lines-form))))=0A=09=
(font-lock-add-keywords=20nil=20=
indent-bars--font-lock-blank-line-keywords=20t)=0A=09(add-hook=20=
'font-lock-extend-region-functions=0A=09=09=20=20=
#'indent-bars--extend-blank-line-regions=2095=20t))))=0A=0A=
(declare-function=20indent-bars-ts-mode=20"indent-bars-ts")=0A=0A(defun=20=
indent-bars-setup=20()=0A=20=20"Setup=20all=20face,=20color,=20bar=20=
size,=20and=20indentation=20info=20for=20the=20current=20buffer."=0A=20=20=
;;=20Spacing=0A=20=20(setq=20indent-bars-spacing=20=
(indent-bars--guess-spacing)=0A=09indent-bars--offset=20(or=20=
indent-bars-starting-column=20indent-bars-spacing))=0A=0A=20=20;;=20No=20=
Stipple=20(e.g.=20terminal)=0A=20=20(setq=20indent-bars--no-stipple=0A=09=
(or=20(not=20(display-graphic-p))=20indent-bars-prefer-character))=0A=0A=20=
=20;;=20Style=20(color=20+=20stipple)=0A=20=20(unless=20(and=20=
indent-bars-style=20indent-bars--styles)=0A=20=20=20=20=
(indent-bars--initialize-style=0A=20=20=20=20=20(setq=20=
indent-bars-style=20(indent-bars--new-style))))=0A=0A=20=20;;=20Window=20=
state:=20selection/size=0A=20=20(unless=20indent-bars--no-stipple=0A=20=20=
=20=20(add-hook=20'window-state-change-functions=0A=09=20=20=20=20=20=20=
#'indent-bars--window-change=20nil=20t))=0A=0A=20=20;;=20PPSS=0A=20=20=
(setq=20indent-bars--ppss=0A=09(or=20indent-bars-no-descend-string=20=
indent-bars-no-descend-lists))=0A=20=20=0A=20=20;;=20Treesitter=0A=20=20=
(if=20indent-bars-treesit-support=20(indent-bars-ts-mode=201))=20;=20=
autoloads=0A=0A=20=20;;=20Remap/Resize=0A=20=20(unless=20=
indent-bars--no-stipple=0A=20=20=20=20(setq=20=
indent-bars--stipple-remaps=20(make-hash-table))=0A=20=20=20=20(add-hook=20=
'text-scale-mode-hook=20#'indent-bars--update-all-stipples=20nil=20t)=0A=20=
=20=20=20(indent-bars--update-all-stipples))=20;=20sets=20all=20remaps=20=
for=20current=20buffer=0A=0A=20=20;;=20Current=20depth=20Highlighting=0A=20=
=20(when=20(indent-bars--style=20'any=20"highlight-current-depth")=0A=20=20=
=20=20(add-hook=20'post-command-hook=0A=09=20=20=20=20=20=20=
#'indent-bars--highlight-current-depth=20nil=20t)=0A=20=20=20=20(setq=20=
indent-bars--current-depth=200)=0A=20=20=20=20=
(indent-bars--highlight-current-depth))=0A=20=20=0A=20=20;;=20Font-lock=0A=
=20=20(indent-bars--setup-font-lock)=0A=20=20(font-lock-flush))=0A=0A=
(defvar=20indent-bars--teardown-functions=20nil)=0A(defun=20=
indent-bars-teardown=20()=0A=20=20"Tears=20down=20indent-bars."=0A=20=20=
;;=20Remove=20current=20highlight=20remaps=0A=20=20(dolist=20(s=20=
indent-bars--styles)=0A=20=20=20=20(face-remap-remove-relative=0A=20=20=20=
=20=20(alist-get=20(ibs/tag=20s)=20indent-bars--remaps)))=0A=0A=20=20;;=20=
Remove=20stipple=20remaps=20and=20window=20parameters=0A=20=20(unless=20=
indent-bars--no-stipple=0A=20=20=20=20(when=20=
indent-bars--stipple-remaps=0A=20=20=20=20=20=20(maphash=20(lambda=20(_k=20=
pl)=0A=09=09=20(indent-bars--remove-plist-remaps=20pl))=0A=09=20=20=20=20=
=20=20=20indent-bars--stipple-remaps)=0A=20=20=20=20=20=20(setq=20=
indent-bars--stipple-remaps=20nil)))=0A=20=20=0A=20=20=
(font-lock-remove-keywords=20nil=20indent-bars--font-lock-keywords)=0A=20=
=20(font-lock-remove-keywords=20nil=20=
indent-bars--font-lock-blank-line-keywords)=0A=20=20(font-lock-flush)=0A=20=
=20(font-lock-ensure)=0A=20=20=0A=20=20(when=20=
indent-bars-orig-unfontify-region=0A=20=20=20=20(setq=20=
font-lock-unfontify-region-function=0A=09=20=20=
indent-bars-orig-unfontify-region))=0A=20=20(setq=20=
indent-bars--current-depth=200)=0A=20=20(remove-hook=20=
'text-scale-mode-hook=20#'indent-bars--update-all-stipples=20t)=0A=20=20=
(remove-hook=20'post-command-hook=20=
#'indent-bars--highlight-current-depth=20t)=0A=20=20(remove-hook=20=
'font-lock-extend-region-functions=0A=09=20=20=20=20=20=20=20=
#'indent-bars--extend-blank-line-regions=20t)=0A=20=20(remove-hook=20=
'window-state-change-functions=0A=09=20=20=20=20=20=20=20=
#'indent-bars--window-change=20t)=0A=20=20(run-hooks=20=
'indent-bars--teardown-functions))=0A=0A(defun=20indent-bars-reset=20=
(&rest=20_r)=0A=20=20"Reset=20indent-bars=20config."=0A=20=20=
(interactive)=0A=20=20(indent-bars-teardown)=0A=20=20(setq=20=
indent-bars-style=20nil=20indent-bars--styles=20nil)=0A=20=20=
(indent-bars-setup))=0A=0A(defun=20indent-bars-setup-and-remove=20=
(frame)=0A=20=20"Setup=20indent=20bars=20for=20FRAME=20and=20remove=20=
from=20`after-make-frame-functions'."=0A=20=20(when=20(display-graphic-p=20=
frame)=0A=20=20=20=20(with-selected-frame=20frame=0A=20=20=20=20=20=20=
(remove-hook=20'after-make-frame-functions=20=
#'indent-bars-setup-and-remove=20t)=0A=20=20=20=20=20=20=
(indent-bars-setup))))=0A=0A;;;###autoload=0A(define-minor-mode=20=
indent-bars-mode=0A=20=20"Indicate=20indentation=20with=20configurable=20=
bars."=0A=20=20:global=20nil=0A=20=20=0A=20=20(if=20indent-bars-mode=0A=20=
=20=20=20=20=20(if=20(and=20(daemonp)=20(not=20(frame-parameter=20nil=20=
'client)))=0A=09=20=20(add-hook=20'after-make-frame-functions=20=
#'indent-bars-setup-and-remove=0A=09=09=20=20=20=20nil=20t)=0A=09=
(indent-bars-setup))=0A=20=20=20=20(indent-bars-teardown)))=0A=0A;;=20=
Theme=20support=0A(if=20(boundp=20'enable-theme-functions)=0A=20=20=20=20=
(add-hook=20'enable-theme-functions=20#'indent-bars-reset-styles))=0A=0A=
(provide=20'indent-bars)=0A=0A;;;=20indent-bars.el=20ends=20here=0A;;=20=
Local=20Variables:=0A;;=20read-symbol-shorthands:=20(("ibs/"=20.=20=
"indent-bars-style-"))=0A;;=20End:=0A=

--Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2
Content-Transfer-Encoding: 7bit
Content-Type: text/html;
	charset=us-ascii

<html><head><meta http-equiv="content-type" content="text/html; charset=us-ascii"></head><body style="overflow-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"></body></html>
--Apple-Mail=_C888637E-22A8-40AE-9AB0-5A5337C16EA2--

--Apple-Mail=_8D1B272E-2958-49A5-AF96-FD9831F9042C--


--===============6027475216738180127==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--===============6027475216738180127==--