% RainbowBrackets v. 1.2.1 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-07-20 v. 1.2.1 LaTeX package that colors parentheses on the same nesting level with a matching color.] \RequirePackage{xparse, xstring, xcolor, expl3, kvoptions} \SetupKeyvalOptions{ family=RB, prefix=RB@ } \DeclareStringOption[default]{style} \DeclareStringOption[(]{lbracket} \DeclareStringOption[)]{rbracket} \DeclareStringOption[10]{max} \ProcessKeyvalOptions* \let\@rbmaxbackup\RB@max \NewDocumentCommand{\rbsetbracketmax}{m}{% \IfInteger{#1}{% \ifnum#1<2% \PackageWarning{rainbowbrackets}{Color repearing maximum not in range [2-10], using default value!}% \else\ifnum#1>10% \PackageWarning{rainbowbrackets}{Color repearing maximum not in range [2-10], using default value!}% \else% \def\RB@max{#1}% \fi\fi% }{% \PackageWarning{rainbowbrackets}{Passed argument '#1' is not an integer, using default value as color repeating maximum!} }% } \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 { \clist_set:Nx \l_colorcodes_clist { 000000 , b4611E , 3CB44B , 4363D8 , F58231 , 911EB4 , B41E2D , 7BB41E , B41E93 , FFE119 } \str_case:nnF { #1 } { { default } {} { neon } { \clist_set:Nx \l_colorcodes_clist { 000000, 001EFF , 39FF14 , FFFF00 , FF00FF, 00FFFF , FFA500 , FF0000 , FF6600 , 74EE15 } } { pastel } { \clist_set:Nx \l_colorcodes_clist { 000000 , 66c5cc , f6cf71 , dcb0f2 , f89c74 , 87c55f , 9eb9f3 , fe88b1 , c9db74 , 8be0a4 } } { spectral } { \clist_set:Nx \l_colorcodes_clist { 000000 , 9e0142 , 5e4fa2 , d53e4f , 3288bd , f46d43 , 66c2a5 , fee08b , abdda4 , e6f598 } } { dualpolar } { \clist_set:Nx \l_colorcodes_clist { 000000 , a55a22 , 41b764, 5aad4e, 6da33a, 7c9829, 898d1b, 938114, 9b7414, a1671a} } { warning } { \clist_set:Nx \l_colorcodes_clist { 000000 , 750000, ffb37d, ff9160, ff6e44, ff4928, ff1407, dd0000, b90000, 960000} } { 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% } } \NewDocumentCommand{\disablerb}{} {% \rb_define_style_colors:n { disabled }% } \NewDocumentCommand{\rbsetstyle}{m} {% \tl_set:Nn \@rbcurrentstyle { #1 }% \rb_define_style_colors:n { #1 }% } % Bracket coloring logic \cs_new:Npn \RB_get_modulo_max: { \int_eval:n { \RB@max } } \NewDocumentCommand{\@rbbrainbow}{m} {% \modswitch_fixed_counter:n {#1}% } \cs_new_protected:Npn \modswitch_fixed_counter:n #1 { % Read value from counter \int_set:Nn \l_tmpa_int { \int_mod:nn { \value{@rbbparenscounter} } { \RB_get_modulo_max: } } % Perform "switch-case" logic \int_case:nnF { \l_tmpa_int } { { 0 } {\textcolor{rbbcpcolor1}{#1}} { 1 } {\textcolor{rbbcpcolor0}{#1}} { 2 } {\textcolor{rbbcpcolor2}{#1}} { 3 } {\textcolor{rbbcpcolor3}{#1}} { 4 } {\textcolor{rbbcpcolor4}{#1}} { 5 } {\textcolor{rbbcpcolor5}{#1}} { 6 } {\textcolor{rbbcpcolor6}{#1}} { 7 } {\textcolor{rbbcpcolor7}{#1}} { 8 } {\textcolor{rbbcpcolor8}{#1}} { 9 } {\textcolor{rbbcpcolor9}{#1}} } {\textcolor{rbbcpcolor0}{#1}} } \ExplSyntaxOff \newcommand{\rbresetstyle}{\expandafter\rbsetstyle\expandafter{\RB@style}}% \newcommand{\rbenable}{\expandafter\rbsetstyle\expandafter{\@rbcurrentstyle}}% \newcommand{\rbdisable}{\rbsetstyle{}}% \expandafter\rbsetstyle\expandafter{\RB@style}% \endinput % End of file `rainbowbrackets.sty'.