% pygmentex.sty

\NeedsTeXFormat{LaTeX2e}

\ProvidesPackage{pygmentex}[2026/03/19 v0.12.1 A Pygmentex layer for LaTeX]

\newif\ifpygmented@opt@force\pygmented@opt@forcefalse
\DeclareOption{force}{\pygmented@opt@forcetrue}

\ProcessOptions\relax

\RequirePackage{fancyvrb}
\RequirePackage{color}
\RequirePackage{ifthen}
%\RequirePackage[font=small,format=plain,labelfont=bf,up,textfont=it,up]{caption}
\RequirePackage{caption}
\RequirePackage{shellesc}
\RequirePackage{pgfkeys}
\RequirePackage{efbox}
\RequirePackage[framemethod=tikz]{mdframed}

%\DeclareCaptionType[within=chapter]{pygcode}[Listagem][Lista de listagens]
\DeclareCaptionType{pygcode}[Listagem][Lista de listagens]
\captionsetup[pygcode]{position=top}

% =========================================================
% Auxiliary:
%   finding the widest string in a comma
%   separated list of strings delimited by parenthesis
% =========================================================
% Arguments:
% #1) text: a comma separeted list of strings
% #2) formatter: a macro to format each string
% #3) dimension: will hold the result
% =========================================================

\ExplSyntaxOn
\cs_new_protected:Npn \pygmented_widest:nnN #1#2#3
  {
    \dim_zero:N #3
    \clist_map_inline:nn { #1 }
      {
        \hbox_set:Nn \l_tmpa_box { #2 { ##1 } }
        \dim_set:Nn #3 { \dim_max:nn { #3 } { \box_wd:N \l_tmpa_box } }
      }
  }

% Preserve the exact original calling signature: \widest(#1)#2#3
\cs_new_protected:Npn \pygmented_widest_wrapper:w (#1)#2#3
  {
    \pygmented_widest:nnN { #1 } { #2 } #3
  }

% Map the public \widest macro to our wrapper INSIDE the syntax block
\cs_new_eq:NN \widest \pygmented_widest_wrapper:w
\ExplSyntaxOff

% =========================================================
% fancyvrb new commands to append to a file
% =========================================================

% See http://tex.stackexchange.com/questions/47462/inputenc-error-with-unicode-chars-and-verbatim
\long\def\unexpanded@write#1#2{\write#1{\unexpanded{#2}}}

\def\VerbatimOutAppend{\FV@Environment{}{VerbatimOutAppend}}

\def\FVB@VerbatimOutAppend#1{%
  \@bsphack
  \begingroup
    \FV@UseKeyValues
    \FV@DefineWhiteSpace
    \def\FV@Space{\space}%
    \FV@DefineTabOut
    \def\FV@ProcessLine{\immediate\unexpanded@write#1}%
    \let\FV@FontScanPrep\relax
    \let\@noligs\relax
    \FV@Scan
}

\def\FVE@VerbatimOutAppend{%
  \endgroup
  \@esphack
}

\DefineVerbatimEnvironment{VerbatimOutAppend}{VerbatimOutAppend}{}

% =========================================================
% Main options
% =========================================================

\newif\ifpygmented@opt@texcomments
\newif\ifpygmented@opt@mathescape
\newif\ifpygmented@opt@linenos
\newif\ifpygmented@opt@autogobble
\newif\ifpygmented@left
\newif\ifpygmented@right

% some settings used by fancyvrb:
% * for line numbering:
%     numbers, numbersep, firstnumber, stepnumber, numberblanklines
% * for selection of lines to print:
%     firstline, lastline,

\pgfkeys{%
  /pygmented/.cd,
  % 
  boxing method/.store in = \pygmented@opt@boxing@method,
  inline method/.store in = \pygmented@opt@inline@method,
  % 
  lang/.store in          = \pygmented@opt@lang,
  sty/.store in           = \pygmented@opt@style,
  escapeinside/.store in  = \pygmented@opt@escapeinside,
  texcomments/.is if      = pygmented@opt@texcomments,
  mathescape/.is if       = pygmented@opt@mathescape,
  % 
  label/.store in         = \pygmented@opt@label,
  caption/.store in       = \pygmented@opt@caption,
  % 
  gobble/.store in        = \pygmented@opt@gobble,
  tabsize/.store in       = \pygmented@opt@tabsize,
  % 
  linenos/.is if          = pygmented@opt@linenos,
  autogobble/.is if       = pygmented@opt@autogobble,
  linenostart/.store in   = \pygmented@opt@linenostart,
  linenostep/.store in    = \pygmented@opt@linenostep,
  linenosep/.store in     = \pygmented@opt@linenosep,
  %
  colback/.store in       = \pygmented@opt@colback,
  font/.store in          = \pygmented@opt@font,
  % 
  force/.default          = false,
  texcomments/.default    = true,
  mathescape/.default     = true,
  linenos/.default        = true,
  autogobble/.default     = true,
}

\pgfqkeys{/pygmented}{
  boxing method = mdframed,
  inline method = efbox,
  sty           = default,
  linenos       = false,
  linenosep     = 2pt,
  font          = \ttfamily,
  tabsize       = 0,
}

% =========================================================
% pygmented commands and environments
% =========================================================

\newwrite\pygmented@outfile

\newcount\pygmented@counter

\newcommand\pygmented@process@options[1]{%
  \pgfkeys{%
    /pgf/key filters/defined/.install key filter,%
    /pgf/key filter handlers/append filtered to/.install key filter handler=\remainingglobaloptions
  }%
  \def\remainingglobaloptions{}%
  \pgfkeysalsofilteredfrom{\pygmented@global@options}%
  \pgfkeysalso{%
    /pgf/key filter handlers/append filtered to/.install key filter handler=\remaininguseroptions
  }%
  \def\remaininguseroptions{}%
  \pgfqkeysfiltered{/pygmented}{#1}%
  % %%%%%%% DEBUGING
  % \typeout{}%
  % \typeout{\string\pygmented@global@options:}\typeout{\meaning\pygmented@global@options}%
  % \typeout{\string\remainingglobaloptions:}\typeout{\meaning\remainingglobaloptions}%
  % \typeout{\string\remaininguseroptions:}\typeout{\meaning\remaininguseroptions}%
  % 
  \fvset{gobble=0,tabsize=0}%
}

\newcommand\pygmented@process@adicional@options[1]{%
  \pgfkeysalso{%
    /pgf/key filters/false/.install key filter,%
    /pgf/key filter handlers/append filtered to/.install key filter handler=\remainingoptions
  }%
  \def\remainingoptions{}%
  \pgfkeysalsofilteredfrom{\remainingglobaloptions}%
  \edef\pygmented@saved@{%
    \ifcsname pygmented@#1@additional@options\endcsname
      \csname pygmented@#1@additional@options\endcsname,%
    \fi
  }%
  \pgfkeysalsofilteredfrom{\pygmented@saved@}%
  \pgfkeysalsofilteredfrom{\remaininguseroptions}%
  % %%%%%%% DEBUGING
  % \typeout{}%
  % \typeout{\string\remainingoptions:}%
  % \typeout{\meaning\remainingoptions}%
}

\newcommand\inputpygmented[2][]{%
  \begingroup
    \pygmented@process@options{#1}%
    \immediate\write\pygmented@outfile{<@@pygmented@input@\the\pygmented@counter}%
    \immediate\write\pygmented@outfile{\detokenize\expandafter{\pygmented@global@options},\detokenize{#1}}%
    \immediate\write\pygmented@outfile{#2}%
    \immediate\write\pygmented@outfile{>@@pygmented@input@\the\pygmented@counter}%
    %
    \csname pygmented@snippet@\the\pygmented@counter\endcsname
    \global\advance\pygmented@counter by 1\relax
  \endgroup
}


% =========================================================
% Custom Environment Parser for `pygmented`
% ---------------------------------------------------------
% We cannot use standard \newenvironment because \@ifnextchar
% considers newlines as whitespace and consumes them. This
% merges the \begin line with the first line of source code,
% causing fancyvrb to crash with an "Extraneous input" error.
%
% We also cannot use fancyvrb's \DefineVerbatimEnvironment
% because it forces its own internal keyval parser, which
% crashes upon seeing our custom pgfkeys (like 'autogobble').
%
% THE FIX: We temporarily make the newline character (^^M)
% \active. This acts as a wall, tricking \@ifnextchar into
% stopping its search without eating the newline. We then
% safely parse our pgfkeys and revert the newline to its
% normal state (\catcode 5) before passing control to fancyvrb.
% =========================================================
\begingroup
\catcode`\^^M=\active
\gdef\pygmented@Environment{%
  \catcode`\^^M=\active
  \@ifnextchar[{\catcode`\^^M=5 \pygmented@StepTwo}{\catcode`\^^M=5 \pygmented@StepTwo[]}%
}
\endgroup

\def\pygmented{\pygmented@Environment}

\long\def\pygmented@StepTwo[#1]{%
  \pygmented@process@options{#1}%
  \def\FV@KeyValues{}% <-- Prevents fancyvrb from attempting to parse residual options
  \immediate\write\pygmented@outfile{<@@pygmented@display@\the\pygmented@counter}%
  \immediate\write\pygmented@outfile{\detokenize\expandafter{\pygmented@global@options},\detokenize{#1}}%
  \FVB@VerbatimOutAppend{\pygmented@outfile}%
}

\def\endpygmented{%
  \FVE@VerbatimOutAppend
  \immediate\write\pygmented@outfile{>@@pygmented@display@\the\pygmented@counter}%

  % =========================================================
  % INVISIBLE MARGIN WRAPPER
  % ---------------------------------------------------------
  % Because we bypass fancyvrb's standard environment setup,
  % the snippet loses LaTeX's normal list margin context.
  % Without this, snippets inside `itemize` or `quotation`
  % environments would snap flush to the left page margin.
  %
  % We wrap the rendered snippet in a custom \list to force it
  % to inherit \@totalleftmargin and \linewidth. We explicitly
  % set all vertical spacing (\topsep, \parskip, etc.) to 0pt
  % (\z@) so we don't interfere with the internal vertical
  % spacing engines of tcolorbox or mdframed.
  % =========================================================
  \list{}{\leftmargin\z@ \rightmargin\z@ \topsep\z@ \partopsep\z@ \parskip\z@ \parsep\z@ \itemsep\z@}%
  \item\relax
  \csname pygmented@snippet@\the\pygmented@counter\endcsname
  \endlist

  \global\advance\pygmented@counter by 1\relax
}


\newcommand\pyginline[2][]{%
  \begingroup
    \pygmented@process@options{#1}%
    \immediate\write\pygmented@outfile{<@@pygmented@inline@\the\pygmented@counter}%
    \immediate\write\pygmented@outfile{\detokenize\expandafter{\pygmented@global@options},\detokenize{#1}}%
    \DefineShortVerb{#2}%
    \SaveVerb
      [aftersave={%
       \UndefineShortVerb{#2}%
       \immediate\write\pygmented@outfile{\FV@SV@pygmented@verb}%
       \immediate\write\pygmented@outfile{>@@pygmented@inline@\the\pygmented@counter}%
       %
       \csname pygmented@snippet@\the\pygmented@counter\endcsname
       \global\advance\pygmented@counter by 1\relax
       \endgroup
      }]%
      {pygmented@verb}#2%
}


\newcommand\pygmented@snippet@inlined[1]{%
  \begingroup
  \csname PYstyle\pygmented@opt@style\endcsname
  \pygmented@opt@font
  \pygmented@process@adicional@options{\pygmented@opt@inline@method}%
  \expandafter\expandafter\csname \pygmented@opt@inline@method \endcsname\expandafter[\remainingoptions]{#1}%
  \endgroup
}

\newenvironment{pygmented@snippet@framed}{%
  \begingroup
  \pygmented@leftmargin\z@
  \ifpygmented@opt@linenos
    \expandafter\widest\pygmented@alllinenos{\FormatLineNumber}{\pygmented@leftmargin}%
    \advance\pygmented@leftmargin\pygmented@opt@linenosep
  \fi
  %
  \ifdefined\pygmented@opt@label
    \def\pygmented@title{%
      \captionof{pygcode}{\label{\pygmented@opt@label}\pygmented@opt@caption}%
      % \nopagebreak
      \vskip -0.7\baselineskip
    }%
  \else
    \ifdefined\pygmented@opt@caption
      \def\pygmented@title{%
        \captionof{pygcode}{\pygmented@opt@caption}%
        % \nopagebreak
        \vskip -0.7\baselineskip
      }%
    \fi
  \fi
  \ifdefined\pygmented@title
    % \nopagebreak[0]%
    \pygmented@title
    % \nopagebreak
  \fi
  %
  \pygmented@process@adicional@options{\pygmented@opt@boxing@method}%
  \expandafter\begin\expandafter{\expandafter\pygmented@opt@boxing@method\expandafter}\expandafter[%
    \remainingoptions
    ]%
  \csname PYstyle\pygmented@opt@style\endcsname
  \pygmented@opt@font
  % 
  \noindent
  }{%
  \end{\pygmented@opt@boxing@method}%
  \endgroup
}


\newcommand\pygmented@inlined[1]{%
  \expandafter\efbox\expandafter[\remainingoptions]{#1}%
}



\def\FormatLineNumber#1{{\rmfamily\tiny#1}}


\newdimen\pygmented@leftmargin
\newdimen\pygmented@linenosep

\def\pygmented@lineno@do#1{%
  \pygmented@linenosep 0pt%
  \csname pygmented@\pygmented@opt@boxing@method @margin\endcsname
  \advance \pygmented@linenosep \pygmented@opt@linenosep
  \makebox[0pt][r]{%
    \FormatLineNumber{#1}%
    \hspace*{\pygmented@linenosep}}%
}

\newcommand\pygmented@tcbox@additional@options{%
  parbox=true,%    Shield against global parbox=false
  nobeforeafter,%
  tcbox raise base,%
  left=0mm,%
  right=0mm,%
  top=0mm,%
  bottom=0mm,%
  boxsep=2pt,%
  arc=1pt,%
  boxrule=0pt,%
  \ifcsname pygmented@opt@colback\endcsname
    colback=\pygmented@opt@colback,%
  \fi
}

\newcommand\pygmented@efbox@additional@options{%
  \ifcsname pygmented@opt@colback\endcsname
    backgroundcolor=\pygmented@opt@colback,%
  \fi
}

\newcommand\pygmented@mdframed@additional@options{%
  leftmargin=\pygmented@leftmargin,%
  frametitlerule=true,%
  \ifcsname pygmented@opt@colback\endcsname
    backgroundcolor=\pygmented@opt@colback,%
  \fi
}

\newcommand\pygmented@tcolorbox@additional@options{%
  grow to left by=-\pygmented@leftmargin,%
  \ifcsname pygmented@opt@colback\endcsname
    colback=\pygmented@opt@colback,%
  \fi
}

\newcommand\pygmented@boite@additional@options{%
  leftmargin=\pygmented@leftmargin,%
  \ifcsname pygmented@opt@colback\endcsname
    colback=\pygmented@opt@colback,%
  \fi
}


\newcommand\pygmented@mdframed@margin{%
  \advance \pygmented@linenosep \mdflength{outerlinewidth}%
  \advance \pygmented@linenosep \mdflength{middlelinewidth}%
  \advance \pygmented@linenosep \mdflength{innerlinewidth}%
  \advance \pygmented@linenosep \mdflength{innerleftmargin}%
}

\newcommand\pygmented@tcolorbox@margin{%
  \advance \pygmented@linenosep \kvtcb@left@rule
  \advance \pygmented@linenosep \kvtcb@leftupper
  \advance \pygmented@linenosep \kvtcb@boxsep
}

\newcommand\pygmented@boite@margin{%
  \advance \pygmented@linenosep \boite@leftrule
  \advance \pygmented@linenosep \boite@boxsep
}

\def\pygmented@global@options{}

\newcommand\setpygmented[1]{%
  \def\pygmented@global@options{/pygmented/.cd,#1}%
}


% =========================================================
% final actions
% =========================================================

\AtEndOfPackage{%
  \IfFileExists{\jobname.pygmented}{%
    \input{\jobname.pygmented}%
  }{%
    \PackageWarning{pygmentex}{File `\jobname.pygmented' not found.}%
  }%
  \immediate\openout\pygmented@outfile\jobname.snippets%
}

\AtEndDocument{%
  \immediate\closeout\pygmented@outfile%
  \ifpygmented@opt@force
    \typeout{>>>> running pygmentex (option force=true) ...}%
    \ShellEscape{if [ -z "$TEXMF_OUTPUT_DIRECTORY" ]; then pygmentex \jobname.snippets ; else pygmentex "$TEXMF_OUTPUT_DIRECTORY"/\jobname.snippets; fi}%
    \typeout{>>>> ... done.}%
  \else
    \IfFileExists{\jobname.pygmented}%
      {\typeout{>>>> file \jobname.pygmented exists, not running pygmentex}}%
      {\typeout{>>>> no file \jobname.pygmented, running pygmentex ...}%
       \ShellEscape{if [ -z "$TEXMF_OUTPUT_DIRECTORY" ]; then pygmentex \jobname.snippets ; else pygmentex "$TEXMF_OUTPUT_DIRECTORY"/\jobname.snippets; fi}%
       \typeout{>>>> ... done.}}%
  \fi
}

\endinput
