minted icon indicating copy to clipboard operation
minted copied to clipboard

Reference lines with less boilerplate

Open zax71 opened this issue 3 months ago • 11 comments

I have been annotating a lot of code in a LaTeX document recently using the method outlined in this Tex Stack Exchange post. But when working with this method it is a bit cumbersome to work with - adding the reference to a list of highlighted lines manually. Could I suggest some different syntax for achieving this?

\begin{document}
\begin{minted}[
    linenos=true, 
    escapeinside=!!,
    highlightlabels,
  ]{c++}
    i = i + 1 ;  
    j = j + 1 ; !\label{myline}!
    k = k + 1 ;
    l = l + 1 ; !\label{yourline}!
\end{minted}
The important lines are line~\ref{myline} and line\ref{yourline}.

The minted package should be able to figure out what lines need highlighting purely based off the lines inside the code block - without requiring them to be defined separately.

zax71 avatar Sep 19 '25 08:09 zax71

I have been annotating a lot of code in a LaTeX document recently using the method outlined in this Tex Stack Exchange post.

Being the author of the linked TeX-SX answer, I doubt my patch for highlightlines option still worked for minted v3.x.

Your example is not compilable. It has no preamble and no \end{document}, and lacks the definition of non-built-in highlightlabels option.

The minted package should be able to figure out what lines need highlighting purely based off the lines inside the code block - without requiring them to be defined separately.

Auto-labelling highlighted lines may be feasible with customized fvextra commands \FancyVerbFormatLine/\FancyVerbFormatText and \FancyVerbHighlightLine/\FancyVerbHighlightLine(First|Middle|Last|Single). (fvextra is one of the dependencies of minted.) But how would you like to name the labels?

muzimuzhi avatar Sep 19 '25 14:09 muzimuzhi

Being the author of the linked TeX-SX answer, I doubt my patch for highlightlines option still worked for minted v3.x.

Odd, I must be using an outdated version. I use \usepackage{minted} in my preamble without a version so that could be why.

Your example is not compilable. It has no preamble and no \end{document}, and lacks the definition of non-built-in highlightlabels option.

That's my bad, I thought the preamble would be self-evident. Regarding the highlightlabels option, I included that as "pseudocode" for an idea of how the syntax might work - keeping the option of labelling lines without them being highlighted open to users.

Auto-labelling highlighted lines may be feasible with customized fvextra commands \FancyVerbFormatLine/\FancyVerbFormatText and \FancyVerbHighlightLine/\FancyVerbHighlightLine(First|Middle|Last|Single). (fvextra is one of the dependencies of minted.) But how would you like to name the labels?

That sounds great that it could be feasible! I'm not sure what you mean by how to name the labels though, I was thinking that a user could type the names of the labels inline with the code as shown in my example and then minted can search the code for any instance of a \label{} and highlight it with the methods you mentioned.

zax71 avatar Sep 19 '25 18:09 zax71

Regarding the highlightlabels option, I included that as "pseudocode" for an idea of how the syntax might work - keeping the option of labelling lines without them being highlighted open to users.

Oh I didn't realize that it acted as "pseudocode".

Try the example below. It adds

  • three new commands \FVLabel{<label>}, \FVLabelBegin{<label>}, and \FVLabelEnd{<label>} which wrap \label and also marks a line or line range to be highlighted, in addition to those lines specified by the highlightlines option. A second compilation is needed.
  • a new option highlightlabels which makes \label the same as \FVLabel, when used in Verbatim-derived environments.

The patches mostly happen in the fvextra package, and the inheritance in minted are tested with both v2.x and v3.x.

\documentclass{article}
\usepackage{minted}
\usepackage{fvextra}
\usepackage{xcolor}
%\usepackage{hyperref}

% for the "tcblisting" env
\usepackage{tcolorbox}
\tcbuselibrary{minted}

\ExplSyntaxOn
\makeatletter

% count the number of \Verb and \begin{Verbatim}
\newcounter{FancyVerb}

\tl_new:N \l__fv_tmp_tl

\tl_new:N \g__fv_lines_labeled_begin_tl
\seq_new:N \g__fv_lines_labeled_seq

% \FVLabel, \FVLabelBegin, and \FVLabelEnd add extra highlighting lines,
% in addition to those specified by the "hightlightlines" option.
\NewDocumentCommand{\FVLabel}{}{
  \__fv_highlight_curr_line:
  \__fv_label:
}
\NewDocumentCommand{\FVLabelBegin}{}{
  \__fv_highlight_range_begin:
  \__fv_label:
}
\NewDocumentCommand{\FVLabelEnd}{}{
  \__fv_highlight_range_end:
  \__fv_label:
}

\cs_new_protected:Npn \__fv_highlight_curr_line:
  {
    \seq_gpush:Ne \g__fv_lines_labeled_seq { \arabic{FancyVerbLine} }
  }
\cs_new_protected:Npn \__fv_highlight_range_begin:
  {
    \tl_gset:Ne \g__fv_lines_labeled_begin_tl { \arabic{FancyVerbLine} }
  }
\cs_new_protected:Npn \__fv_highlight_range_end:
  {
    \tl_if_empty:NTF \g__fv_lines_labeled_begin_tl
      { \PackageError{fvextra}{Missing paired \string\FVLabelBegin}{} }
      {
        \seq_gpush:Ne \g__fv_lines_labeled_seq
          { \g__fv_lines_labeled_begin_tl - \arabic{FancyVerbLine} }
      }
    \tl_gclear:N \g__fv_lines_labeled_begin_tl
  }

\cs_new_protected:Npn \__fv_label: { \label }

% Init setting for labeled lines
% add labeled lines to 
\FV@AddToHook\FV@FormattingPrep@PreHook
  {
    \refstepcounter{FancyVerb}
    \tl_set_eq:Nc \l__fv_tmp_tl
      { FV@highlightlines@smuggle@ \arabic{FancyVerb} }
    \cs_if_exist:NT \l__fv_tmp_tl
      {
        \exp_args:Ne \fvset{highlightlines={ \l__fv_tmp_tl }}
        % at this point the env-level "highlightlines" is already processed,
        % so we trigger the processer again, manually.
        \FV@HighlightLinesPrep
      }
    % just in case
    \seq_gclear:N \g__fv_lines_labeled_seq
  }

% new hook \FV@Endlist@Hook
\def\FV@Endlist@Hook{}
\def\FV@EndList{%
  \FV@ListProcessLastLine
  \FV@EndListFrame
  \FV@Endlist@Hook
  \@endparenv
  \endgroup
  \@endpetrue}

\FV@AddToHook\FV@Endlist@Hook
  {
    \seq_if_empty:NF \g__fv_lines_labeled_seq
      {
        % example: write "\FV@highlightlines@smuggle{1}{4,2}" to .aux
        \immediate\write\@auxout
          {
            \string\FV@highlightlines@smuggle
              { \arabic{FancyVerb} }
              { \seq_use:Nn \g__fv_lines_labeled_seq {,} }
          }
      }
    \seq_gclear:N \g__fv_lines_labeled_seq
  }

% helper, used in .aux file
\cs_new_protected:Npn \FV@highlightlines@smuggle #1#2
  {
    \cs_gset:cpn { FV@highlightlines@smuggle@#1 } {#2}
  }

% new boolean option "highlightlabels", for both fvextra and minted
%
% It makes \label the same as \FVLabel inside Verbatim-derived envs.
\define@booleankey{FV}{highlightlabels}
  {\FV@AddToHook\FV@FormattingPrep@PreHook
    {\let\__fv_label:=\label \let\label=\FVLabel}}
  {}
% one-day newer than the date in minted2.sty
\IfPackageAtLeastTF{minted}{2024-05-10}{
  % for minted v3.x
  \mintedpgfkeyscreate{fv}{
    highlightlabels
  }
}{
  % for minted v2.x
  \minted@def@optfv@switch{highlightlabels}
}
\makeatother
\ExplSyntaxOff

\begin{document}

\fvset{numbersep=5pt}

\subsection*{\texttt{fvextra} example(s)}

\begin{tcblisting}{minted language=latex}
\begin{Verbatim}[linenos, gobble=4, commandchars=\\\{\}]
    i = i + 1 ;
    j = j + 1 ; \FVLabel{myline}
    k = k + 1 ;
    l = l + 1 ; \FVLabel{yourline}
\end{Verbatim}
\end{tcblisting}

The important lines are line~\ref{myline} and line~\ref{yourline}.

\begin{tcblisting}{minted language=latex}
\begin{Verbatim}[linenos, firstnumber=11, gobble=4, commandchars=\\\{\}]
    i = i + 1 ;  
    j = j + 1 ; \FVLabelBegin{myline2}
    k = k + 1 ;
    l = l + 1 ; \FVLabelEnd{yourline2}
\end{Verbatim}
\end{tcblisting}

The important lines are lines~\ref{myline2}--\ref{yourline2}.

\subsection*{\Verb|minted| example(s)}

\begin{tcblisting}{minted language=latex}
\begin{minted}[
    autogobble,
    linenos=true,
    firstnumber=21,
    escapeinside=!!,
    highlightlabels,
    highlightlines={27-29},
  ]{c++}
    i = i + 1 ;  
    j = j + 1 ; !\label{myline3}!
    k = k + 1 ;
    l = l + 1 ; !\label{yourline3}!

    i -= 1 ;
    j -= 1 ;
    k -= 1 ;
    l -= 1 ;
\end{minted}
\end{tcblisting}

The important lines are line~\ref{myline3}, line~\ref{yourline3}, and the
following highlighted range.

\end{document}
Image Image

muzimuzhi avatar Sep 20 '25 07:09 muzimuzhi

Thank you so much for taking the time to look into this! I'm not well versed in the world of writing LaTeX packages - how would I use this patch in my document? Do I make some kind of fvextra.tex file where I copy that preamble, or is it more involved than that?

zax71 avatar Sep 23 '25 16:09 zax71

@zax71 In general the patch (lines from \ExplSyntaxOn to \ExplSyntaxOff in https://github.com/gpoore/minted/issues/459#issuecomment-3314721381 example, inclusive) only needs to appear in your preamble (so the patch takes effect before any fvextra/minted commands are used), possibly in a separate file (which consists of various kinds of patches) \input by the main tex file.

muzimuzhi avatar Sep 26 '25 13:09 muzimuzhi

That seems to work perfectly! I guess this can be PR'd in now, or is this out of scope for the Minted package?

zax71 avatar Sep 26 '25 16:09 zax71

Hmm, it seems to work perfectly on my desktop that is running NixOS. Although, on my laptop that is running Debian with the texlive-full package it can't build the project. My GitHub Actions CI can also build it perfectly fine.

The build errors on \g__fv_lines_labeled_seq with the full error of:

./_minted-main/43EE9E6F2074F7B02419542E4824B6E41B35B9996CB1364E8424D6B13D48A010.pygtex:8: Undefined control sequence.
\__fv_highlight_curr_line: ->\seq_gpush:Ne 
                                           \g__fv_lines_labeled_seq {\arabic...
l.8 ...\PYG{+w}{ }\PYG{esc}{\label{calculateMove}}

I can't find any reference of this control sequence or error anywhere else on the internet so I'm stumped as to how to fix my TexLive install so that it builds.

zax71 avatar Sep 29 '25 10:09 zax71

Adding

\cs_generate_variant:Nn \seq_gpush:Nn { Ne }

to the very beginning of the patch (but after \ExplSyntaxOn) should do the trick. This line of code defines \seq_gpush:Ne as the variant of expl3 function \seq_gpush:Nn (see texdoc l3 for more info).

The \seq_gpush:Ne is added to the l3kernel since 2023-10-10, so your laptop may be using a TeX Live 2023.

The version of l3kernel can be read from the beginning of every log file, on the 7th or 8th line, which looks like

L3 programming layer <2025-09-02>

muzimuzhi avatar Sep 29 '25 23:09 muzimuzhi

I guess this can be PR'd in now, or is this out of scope for the Minted package?

Yes, as the highlightlines option in minted is inherited from fvextra package (https://github.com/gpoore/fvextra, created and maintained by the same author), extension to it should be contributed to fvextra too.

Also my patch uses expl3 functions (control sequences having _ and/or : in their names, and surrounded by \ExplSyntaxOn...\ExplSyntaxOff), which is not used by fvextra code base (yet). It's just a matter of style, or choices of utility functions.

muzimuzhi avatar Sep 29 '25 23:09 muzimuzhi

Adding

\cs_generate_variant:Nn \seq_gpush:Nn { Ne }

to the very beginning of the patch (but after \ExplSyntaxOn) should do the trick. This line of code defines \seq_gpush:Ne as the variant of expl3 function \seq_gpush:Nn (see texdoc l3 for more info).

Thanks! That seems to fix it.

I guess this can be PR'd in now, or is this out of scope for the Minted package?

Yes, as the highlightlines option in minted is inherited from fvextra package (https://github.com/gpoore/fvextra, created and maintained by the same author), extension to it should be contributed to fvextra too.

Also my patch uses expl3 functions (control sequences having _ and/or : in their names, and surrounded by \ExplSyntaxOn...\ExplSyntaxOff), which is not used by fvextra code base (yet). It's just a matter of style, or choices of utility functions.

Ah, should this issue be transitioned to the fvextra repo?

zax71 avatar Sep 30 '25 08:09 zax71

@muzimuzhi If you want to convert this into an fvextra pull request, I'd be happy to add highlightlabels. Using expl3 is fine. I may be slow in working through and accepting a pull request...I have several deadlines and a backlog of projects over the next month or two.

gpoore avatar Oct 08 '25 17:10 gpoore