\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{highlightlatex}[2021/03/14]

% Repository: https://github.com/vkuhlmann/highlight-latex

% Copyright (c) 2021 Vincent Kuhlmann
% 
% Permission is hereby granted, free of charge, to any person
% obtaining a copy of this software and associated documentation
% files (the "Software"), to deal in the Software without
% restriction, including without limitation the rights to use,
% copy, modify, merge, publish, distribute, sublicense, and/or sell
% copies of the Software, and to permit persons to whom the
% Software is furnished to do so, subject to the following
% conditions:
% 
% The above copyright notice and this permission notice shall be
% included in all copies or substantial portions of the Software.
% 
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
% OTHER DEALINGS IN THE SOFTWARE.

\RequirePackage{listings}
\RequirePackage{xcolor}

\RequirePackage{xkeyval}
\RequirePackage{etoolbox}

% Snippet source:
% https://tex.stackexchange.com/questions/406015/defining-macro-gsetlength-as-global-setlength-reliable
\gdef\gsetlength#1#2{%
	\begingroup
		\setlength\skip@{#2}%
		\global#1=\skip@
	\endgroup
}


\def\@highlight@frame{none}
\newlength{\hll@offset@fixskip}
\setlength{\hll@offset@fixskip}{0pt}

\newif\ifhllStyleAnywhere
\newcommand\defaultgobble{0}
\newif\ifhllDebugFrame
%\hllDebugFrametrue
\newlength\hllBeforeSkip
\setlength\hllBeforeSkip{2pt}
\newlength\hllAfterSkip
\setlength\hllAfterSkip{0pt}


\DeclareOptionX{frame}{
	\setframe{#1}
}

\DeclareOptionX{noframe}{
	\setframe{none}
}

\def\setframe#1{%
	\def\frameval{#1}%
	\ifx\relax#1\relax
		\def\frameval{lines}%
	\fi
	\def\linesvalue{lines}%
	%
	\xdef\@highlight@frame{\frameval}%
	%
	\ifx\frameval\linesvalue
		\gsetlength{\hll@offset@fixskip}{6pt}%
	\else
		\gsetlength{\hll@offset@fixskip}{0pt}%
	\fi
}

\DeclareOptionX{styleanywhere}{
	\hllStyleAnywheretrue
}

\DeclareOptionX*{\PackageWarning{highlight-latex}{`\CurrentOption' ignored}}
\ExecuteOptionsX{frame=lines}

\ProcessOptionsX\relax

\colorlet{curlyBrackets}{red!50!blue}
\colorlet{squareBrackets}{blue!50!white}
\colorlet{codeBackground}{gray!10!white}
\colorlet{comment}{green!40!black}

\colorlet{accDefault}{blue!90!black}
\definecolor{accStructure}{RGB}{0,149,255}

\colorlet{accentB}{green!60!black}

\catcode`\{=11
\let\myopenbrace={
\catcode`\{=1

\catcode`\}=11
\let\myclosebrace=}
\catcode`\}=2

\lstdefinelanguage{ColoredLaTeX}
[LaTeX]{TeX}{
	backgroundcolor=\color{codeBackground},
	keywordstyle=\bfseries\color{accDefault},
	basicstyle=\footnotesize\ttfamily,
	commentstyle=\color{comment},
	numbers=none,
	tabsize=2,
	mathescape=false,
	alsoletter={$@_!|?$},
%
	classoffset=0,
	keywordstyle=\color{accDefault},
	texcsstyle=*\color{accDefault},
	moretexcs={subsection,thesubsection,thesection,theoremstyle},
	deletetexcs={begin,end},
%
	classoffset=1,
	keywordstyle=\color{accStructure},
	texcsstyle=*\color{accStructure},
	moretexcs={begin,end},
%
	classoffset=2,
	escapeinside=~~,
%	
	classoffset=0,
	%literate={textA}{textB}{3}%
	%https://tex.stackexchange.com/questions/172945/coloring-in-listing
	literate={\{}{{\textcolor{curlyBrackets}{\myopenbrace}}}{1}
		{\}}{{\textcolor{curlyBrackets}{\myclosebrace}}}{1}
		{[}{{\textcolor{squareBrackets}{[}}}{1}
		{]}{{\textcolor{squareBrackets}{]}}}{1}%
	%		{(}{{\textcolor{delimiterColor}{(}}}{1}
	%		{)}{{\textcolor{delimiterColor}{)}}}{1}%
	%texcsstyle=\color{blue}
}
\lstset{language=ColoredLaTeX}


\newcommand{\hllDelimDollar}[1][\color{green!40!black}]{%
	\lstset{
		classoffset=2,
		moredelim=**[s][#1]{$}{$},
		classoffset=0
	}%
}

\newcommand{\hllDelimParen}[1][\color{green!40!black}]{%
	\lstset{
		classoffset=2,
		moredelim=**[s][#1]{\(}{\)},
		classoffset=0
	}%
}

\def\hll@assignlabel#1#2{%
	\expandafter\gdef\csname hll@labeltoclassoffset@#1\endcsname{#2}%
}

\hll@assignlabel{default}{0}
\hll@assignlabel{structure}{1}


% =========================
%   block & inline markup
% =========================

\newcommand{\hllPrepareBlock}{%
	\lstset{framesep=0.1cm,framerule=0.6pt}%
	\def\setHighlightFrame##1{%
		\lstset{frame=##1}%
	}%
	\expandafter\setHighlightFrame\expandafter{\@highlight@frame}%
	\lstset{belowskip=0pt,aboveskip=0pt,belowcaptionskip=0pt,gobble=\defaultgobble}%
	\setlength\parskip{0pt}%
	\setlength\parindent{0pt}%
}

\lstnewenvironment{highlightblock}[1][]
{%
	\vspace{\hllBeforeSkip}%
	\begingroup
	\hllPrepareBlock\lstset{#1}%
}{%
	\nointerlineskip
	\endgroup
	\vspace{\hllAfterSkip}%
}

\lstnewenvironment{hllblock}[1][]
{%
	\vspace{\hllBeforeSkip}%
	\begingroup
	\hllPrepareBlock\lstset{#1}%
}{%
	\endgroup
	\vspace{\hllAfterSkip}%
}

\def\hll{\lstinline}

% ================
%   block saving
% ================

\newenvironment{saveblock}[1]{%
	\expandafter\ifx\csname @codebox@#1\endcsname\relax
		\expandafter\newbox\csname @codebox@#1\endcsname\relax
	\fi
	%
	% The trick from latex/base/latex.ltx
	\edef\@bracehack{%
		\endgroup
		\expandafter\setbox\csname @codebox@#1\endcsname\vbox{%
			\begingroup\aftergroup}%
		\def\noexpand\@currenvir{\@currenvir}%
		\def\noexpand\@currenvline{\on@line}}%
	\@bracehack
	\begingroup\vspace{\hll@offset@fixskip}
	\ignorespaces%
}{%
	\endgroup
}

\newcommand\useblock[1]{%
	\ifhllDebugFrame
		\par
		{%
			\setlength{\fboxsep}{0pt}%
			\fcolorbox{orange}{red}{\expandafter\usebox\csname @codebox@#1\endcsname}%
		}%
		\par
	\else
		\par\expandafter\usebox\csname @codebox@#1\endcsname\par
	\fi
}

\newcommand\consumeblock[1]{%
	\ifhllDebugFrame
		\par
		{%
			\setlength{\fboxsep}{0pt}%
			\setlength{\fboxrule}{1pt}%
			\fcolorbox{orange}{red}{\leavevmode\expandafter\box\csname @codebox@#1\endcsname\relax}%
		}
		\par
		\expandafter\let\csname @codebox@#1\endcsname\relax
	\else
		\par\leavevmode\expandafter\box\csname @codebox@#1\endcsname\relax\par
		\expandafter\let\csname @codebox@#1\endcsname\relax
	\fi
}

% ===================
%   updatehighlight
% ===================

\def\hll@update@label{}

\newcounter{hll@update@state}

\newcounter{hll@update@nextclassoffset}
\setcounter{hll@update@nextclassoffset}{5}

\newcounter{hll@update@classoffset}

\def\hll@update@fixclassoffset{%
	\ifnum\value{hll@update@state}<1\relax
	\setcounter{hll@update@classoffset}{\thehll@update@nextclassoffset}%
	\stepcounter{hll@update@nextclassoffset}%
	\setcounter{hll@update@state}{1}%
	\fi
}

\newcommand{\updatehighlight}[1]{%
	\setcounter{hll@update@state}{-1}%
	\gdef\hll@update@label{}%
	\setkeys{updatehighlight}{#1}%
	%
	\lstset{classoffset=0}%
}


%%% KEY: label
%%% ----------

\define@key{updatehighlight}{label}{%
	\hll@update@setlabel{#1}%
}

\define@key{updatehighlight}{name}{%
	\hll@update@setlabel{#1}%
}

\def\hll@update@setlabel#1{
	\gdef\hll@update@label{#1}%
	\expandafter\def\expandafter\@capturedlabel\expandafter{\csname hll@labeltoclassoffset@#1\endcsname}%
	\expandafter\unless\expandafter\ifx\@capturedlabel\relax\relax
		% Label exists
		\edef\@val{\@capturedlabel}%
		\setcounter{hll@update@classoffset}{\@val}%
		\setcounter{hll@update@state}{1}%
	\else
		% Allocate new label
		\edef\@val{\thehll@update@nextclassoffset}%
		\setcounter{hll@update@classoffset}{\@val}%
		\stepcounter{hll@update@nextclassoffset}%
		\setcounter{hll@update@state}{1}%
		%
		\expandafter\xdef\@capturedlabel{\@val}%
	\fi
}

%%% KEY: classoffset
%%% ----------------

\define@key{updatehighlight}{classoffset}{%
	\setcounter{hll@update@classoffset}{#1}%
	\setcounter{hll@update@state}{1}%
}

%%% KEY: commands
%%% -------------

\define@key{updatehighlight}{macros}{%
	\hll@update@macros{#1}%
}

\define@key{updatehighlight}{commands}{%
	\hll@update@macros{#1}%
}


\def\hll@update@macros#1{%
	\hll@update@fixclassoffset
	\def\@inv##1{\forcsvlist{\hll@update@addmacro{##1}}{#1}}%
	\expandafter\@inv\expandafter{\the\c@hll@update@classoffset}
	%
	\setcounter{hll@update@state}{2}%
}

\def\hll@update@addmacro#1#2{%
	\if\noexpand#2\relax
		\edef\keywordpure{\expandafter\@gobble\string#2}%
		\def\callwithpure##1{\hll@update@addmacro@i{#1}{##1}}%
	\expandafter\callwithpure\expandafter{\keywordpure}%
	\else
		\hll@update@addmacro@i{#1}{#2}%
	\fi
}

\def\hll@update@addmacro@i#1#2{%
	\lstset{
		classoffset=0,deletetexcs={#2},
		classoffset=1,deletetexcs={#2},
		classoffset=#1,moretexcs={#2},
		classoffset=0
	}%
}


%%% KEY: add
%%% --------

\define@key{updatehighlight}{add}{%
	\hll@update@add{#1}%
}

\def\hll@update@add#1{%
	\hll@update@fixclassoffset
	\def\@inv##1{\forcsvlist{\hll@update@additem{##1}}{#1}}%
	\expandafter\@inv\expandafter{\the\c@hll@update@classoffset}
	%
	\setcounter{hll@update@state}{2}%
}

\def\hll@update@additem#1#2{%
	\if\noexpand#2\relax
		\edef\keywordpure{\expandafter\@gobble\string#2}%
		\def\callwithpure##1{\hll@update@addmacro@i{#1}{##1}}%
		\expandafter\callwithpure\expandafter{\keywordpure}%
	\else
		\hll@update@addkeyword{#1}{#2}%
	\fi
}

%%% KEY: remove
%%% -----------

\define@key{updatehighlight}{remove}{%
	\hll@update@remove{#1}%
}

\def\hll@update@remove#1{%
	\hll@update@fixclassoffset
	\def\@inv##1{\forcsvlist{\hll@update@removeitem{##1}}{#1}}%
	\expandafter\@inv\expandafter{\the\c@hll@update@classoffset}
	%
	\setcounter{hll@update@state}{2}%
}

\def\hll@update@removeitem#1#2{%
	\if\noexpand#2\relax
		\edef\keywordpure{\expandafter\@gobble\string#2}%
		\def\callwithpure##1{\hll@update@removemacro{#1}{##1}}%
		\expandafter\callwithpure\expandafter{\keywordpure}%
	\else
		\hll@update@removekeyword{#1}{#2}%
	\fi
}

\def\hll@update@removemacro#1#2{%
	\lstset{
		classoffset={#1},
		deletetexcs={#2}
	}%
}

\def\hll@update@removekeyword#1#2{%
	\lstset{
		classoffset={#1},
		deletekeywords={#2}
	}%
}


%%% KEY: keywords
%%% -------------

\define@key{updatehighlight}{keywords}{%
	\hll@update@keywords{#1}%
}


\def\hll@update@keywords#1{%
	\hll@update@fixclassoffset
	\def\@inv##1{\forcsvlist{\hll@update@addkeyword{##1}}{#1}}%
	\expandafter\@inv\expandafter{\the\c@hll@update@classoffset}%
	%
	\setcounter{hll@update@state}{2}%
}

\def\hll@update@addkeyword#1#2{%
	\lstset{
		classoffset=0,deletekeywords={#2},
		classoffset=1,deletekeywords={#2},
		classoffset=#1,otherkeywords={#2},morekeywords={#2},
		classoffset=0
	}%
}


%%% KEY: style
%%% ----------

\define@key{updatehighlight}{style}{%
	\hll@update@fixclassoffset
	\unless\ifhllStyleAnywhere
		\ifnum\value{hll@update@state}>1\relax
			\setcounter{hll@update@state}{0}%
			\hll@update@fixclassoffset
		\fi
	\fi
	%
	\expandafter\hll@update@setstyle\expandafter{\the\c@hll@update@classoffset}{#1}%
}

\def\hll@update@setstyle#1#2{%
	\lstset{%
		classoffset=#1,
		keywordstyle={#2},
		texcsstyle={*#2},
	}%
}

%%% KEY: color
%%% ----------

\define@key{updatehighlight}{color}{%
	\hll@update@fixclassoffset
	\unless\ifhllStyleAnywhere
		\ifnum\value{hll@update@state}>1\relax
			\setcounter{hll@update@state}{0}%
			\hll@update@fixclassoffset
		\fi
	\fi
	%
	\expandafter\hll@update@setcolor\expandafter{\the\c@hll@update@classoffset}{#1}%
}

\def\hll@update@setcolor#1#2{%
	\lstset{%
		classoffset=#1,
		keywordstyle={\color{#2}},
		texcsstyle={*\color{#2}},
	}%
}

%%% KEY: clear
%%% ----------

\define@key{updatehighlight}{clear}[]{%
	\hll@update@fixclassoffset
	\expandafter\hll@update@clear\expandafter{\the\c@hll@update@classoffset}%
}

\def\hll@update@clear#1{%
	\lstset{
		classoffset=#1,
		texcs={},
		keywords={},
		classoffset=0
	}%
}


