% RainbowBrackets v. 1.3.2 y. 2025
% 
% Copyright (C) 2025 Paul Eduard Koenig
% pauleduardkoenig (at) gmail [dot] com
% Goethe University Frankfurt, Institute of Linguistics
%
% --------------------------------
% 
% Provides automatic coloring of nested parentheses using a configurable color cycle.
% Tracks nesting depth with a counter and warns about unbalanced parentheses at end of document.
% 
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either
% version 1.3c of this license or (at your option) any later
% version. The latest version of this license is in:
% http://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of
% LaTeX version 2008-05-04 or later.
% 
% This work has the LPPL maintenance status `maintained'.
% The Current Maintainer of this work is Paul Eduard Koenig.
% This work consists of the files rainbowbrackets.sty and rainbowbrackets_doc.tex
%
% ------------------------------------------------------------ 
% header
% ------------------------------------------------------------ 
%
% The groundwork of this package is based on workd by Ryan Reich, jub0bs, and Matthew Towers
%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{rainbowbrackets}[2025-08-05 v. 1.3.2 LaTeX package that colors parentheses on the same nesting level with a matching color.]
\RequirePackage{xparse, xstring, xcolor, expl3, kvoptions}

\def\@rbmaxfallbackvalue{10}

\SetupKeyvalOptions{
	family=RB,
	prefix=RB@
}
\DeclareStringOption[default]{style}
\DeclareStringOption[(]{lbracket}
\DeclareStringOption[)]{rbracket}
\DeclareStringOption[\@rbmaxfallbackvalue]{max}
\ProcessKeyvalOptions*

\NewDocumentCommand{\rbsetbracketmax}{m O{\RB@max} O{default}}{%
\IfInteger{#1}{%
    \ifnum#1<2%
      \PackageWarning{rainbowbrackets}{Color repearing maximum not in range [2-10], using #3 value (#2)!}%
	  \gdef\RB@max{\@rbmaxfallbackvalue}
    \else\ifnum#1>10%
      \PackageWarning{rainbowbrackets}{Color repearing maximum not in range [2-10], using #3 value (#2)!}%
	  \gdef\RB@max{\@rbmaxfallbackvalue}
    \else%
      \def\RB@max{#1}%
    \fi\fi%
  }{%
    \PackageWarning{rainbowbrackets}{Passed argument '#1' is not an integer, using #3 value (#2) as color repeating maximum!}
	\gdef\RB@max{\@rbmaxfallbackvalue}
  }%
}
\expandafter\rbsetbracketmax\expandafter{\RB@max}[\@rbmaxfallbackvalue][fallback]
\let\@rbmaxbackup\RB@max
\NewDocumentCommand{\rbresetbracketmax}{}{%
\def\RB@max{\@rbmaxbackup}%
}

% This snipped is based on work by Ryan Reich, jub0bs, and Matthew Towers
% https://github.com/matthew-towers/latex-rainbow

\newcounter{@rbbparenscounter}
\NewDocumentCommand{\@countparen}{m m m}
{\IfEq{#1}{l}%
{\addtocounter{@rbbparenscounter}{1}\@rbbrainbow{#2}}%
{\@rbbrainbow{#3}\addtocounter{@rbbparenscounter}{-1}}%
}

\begingroup
\catcode`(\active%
\catcode`)\active%
\gdef\@count@rbbparenscounter#1#2{%
\newcommand{(}{\@countparen{l}{#1}{#2}}%
\newcommand{)}{\@countparen{r}{#1}{#2}}%
}
\endgroup

%% Can't use NewDocumentEnvironment because () need to be active before optional argument check is done
\edef\@rb@argA{\RB@lbracket}
\edef\@rb@argB{\RB@rbracket}

\edef\RB@lbracketbackup{\RB@lbracket}
\edef\RB@rbracketbackup{\RB@rbracket}

\newcommand{\rbtransformer}[1]{#1}
\newcommand{\rbtransformerbackup}[1]{\rbtransformer{#1}}

\newcommand{\rbsetnormalmode}{\renewcommand{\rbtransformer}[1]{##1}}
\newcommand{\rbsetboldmode}{\renewcommand{\rbtransformer}[1]{\textbf{##1}}}
\newcommand{\rbsetitalicmode}{\renewcommand{\rbtransformer}[1]{\textit{##1}}}
\newcommand{\rbresetmode}{}
\newcommand{\rbresetbrackets}{%
\rbresetlbracket%
\rbresetrbracket%
}
\newcommand{\rbresetlbracket}{%
\renewcommand{\@rb@argA}{\RB@lbracket}%
}
\newcommand{\rbresetrbracket}{%
\renewcommand{\@rb@argB}{\RB@lbracket}%
}
\newcommand{\rbsetlbracket}[1]{\renewcommand{\@rb@argA}{#1}}
\newcommand{\rbsetrbracket}[1]{\renewcommand{\@rb@argB}{#1}}

\let\rbparenl(
\let\rbparenr)

\newenvironment{rb}{%
\catcode`(\active%
\catcode`)\active%
\rbsetnormalmode%
\renewcommand{\rbresetmode}{\rbsetnormalmode}%
\@ifnextchar[{\@rb@getA}{\@rb@start}%
}{%
\@rb@errorcheck%
}

\newenvironment{rbi}{%
\catcode`(\active%
\catcode`)\active%
\rbsetitalicmode%
\renewcommand{\rbresetmode}{\rbsetitalicmode}%
\@ifnextchar[{\@rb@getA}{\@rb@start}%
}{%
\@rb@errorcheck%
}

\newenvironment{rbb}{%
\catcode`(\active%
\catcode`)\active%
\rbsetboldmode%
\renewcommand{\rbresetmode}{\rbsetboldmode}%
\@ifnextchar[{\@rb@getA}{\@rb@start}%
}{%
\@rb@errorcheck%
}

\def\@rb@getA[#1]{%
\renewcommand{\@rb@argA}{#1}%
\@ifnextchar[{\@rb@getB}{\@rb@start}%
}

\def\@rb@getB[#1]{%
\renewcommand{\@rb@argB}{#1}%
\@rb@start%
}

\def\@rb@start{%
\let\RB@lbracketbackupinternal\@rb@argA%
\let\RB@rbracketbackupinternal\@rb@argB%
\renewcommand{\rbresetlbracket}{%
\renewcommand{\@rb@argA}{\RB@lbracketbackupinternal}%
}%
\renewcommand{\rbresetrbracket}{%
\renewcommand{\@rb@argB}{\RB@rbracketbackupinternal}%
}%
\@count@rbbparenscounter{\rbtransformer{\@rb@argA}}{\rbtransformer{\@rb@argB}}%
}

\def\@rb@errorcheck{%
\ifnum\value{@rbbparenscounter}=0\relax\else\ifnum\value{@rbbparenscounter}<0\relax\PackageWarning{rainbowbrackets}{Unbalanced parentheses detected! Counter value is \number\numexpr\value{@rbbparenscounter}\relax. Did you forget an opening parenthesis?}\else\PackageWarning{rainbowbrackets}{Unbalanced parentheses detected! Counter value is \number\numexpr\value{@rbbparenscounter}\relax. Did you forget a closing parenthesis?}\fi\fi\setcounter{@rbbparenscounter}{0}%
}
% End of snippet

\ExplSyntaxOn
% Style logic
\clist_new:N \l_colorcodes_clist
\int_new:N \l_index_int

\cs_new_protected:Nn \rb_define_style_colors:n
  {%						Level			9		0		1		2			3		4		5		6		7			8
	\clist_set:Nx \l_colorcodes_clist { b4611E , 000000 , 3CB44B , 4363D8 , F58231 , 911EB4 , B41E2D , 7BB41E , B41E93 , FFE119 }
	\str_case:nnF { #1 }
      {
        { default }  {}
        { neon }  { \clist_set:Nx \l_colorcodes_clist  { 001EFF , 000000, 39FF14 , FFFF00 , FF00FF, 00FFFF , FFA500 , FF0000 , FF6600 , 74EE15 } }
        { pastel }  { \clist_set:Nx \l_colorcodes_clist  { FF746C , 000000 , f6cf71 , dcb0f2 , f89c74 , 87c55f , 9eb9f3 , fe88b1 , c9db74 , 8be0a4 } }
        { spectral }  { \clist_set:Nx \l_colorcodes_clist  { 9e0142 , 000000 , 5e4fa2 , d53e4f , 3288bd , f46d43 , 66c2a5 , fee08b , abdda4 , e6f598 } }
        { dualpolar }  { \clist_set:Nx \l_colorcodes_clist  { a55a22 , 000000 , 41b764, 5aad4e, 6da33a, 7c9829, 898d1b, 938114, 9b7414, a1671a} }
        { warning }  { \clist_set:Nx \l_colorcodes_clist  { 750000, 000000 , ffb37d, ff9160, ff6e44, ff4928, ff1407, dd0000, b90000, 960000} }
        { java }  { \clist_set:Nx \l_colorcodes_clist  { 0DAA85, 000000 , 3F9101, 0E4A8E, BC0BA2, B4960A, AA0D25, 0D98AA, AA610D, 590DAA} }
        { disabled }  { \clist_set:Nx \l_colorcodes_clist  { 000000 , 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000} }
      }
      {\PackageWarning{rainbowbrackets}{Unknown~style~'#1',~using~default~style!}}
	  \int_zero:N \l_index_int
		\clist_map_inline:Nn \l_colorcodes_clist
		  {
			\definecolor{rbbcpcolor\int_use:N \l_index_int}{HTML}{##1}%
			\int_incr:N \l_index_int%
		  }
  }

\cs_new:Npn \RB_get_modulo_max: {
  \int_eval:n { \RB@max }
}

\NewDocumentCommand{\@rbbrainbow}{m}
{%
\int_set:Nn \l_tmpa_int { \int_mod:nn { \value{@rbbparenscounter} } { \RB_get_modulo_max: } }%
\textcolor{rbbcpcolor\int_use:N \l_tmpa_int}{#1}%
}

\NewDocumentCommand{\rbdisable}{}
{%
\rb_define_style_colors:n { disabled }%
}

\NewDocumentCommand{\rbsetstyle}{m}
{%
\tl_set:Nn \@rbcurrentstyle { #1 }%
\rb_define_style_colors:n { #1 }%
}

\ExplSyntaxOff
\newcommand{\rbresetstyle}{\expandafter\rbsetstyle\expandafter{\RB@style}}%
\newcommand{\rbenable}{\expandafter\rbsetstyle\expandafter{\@rbcurrentstyle}}%
\expandafter\rbsetstyle\expandafter{\RB@style}%
\endinput
% End of file `rainbowbrackets.sty'.