gpoore / minted

minted is a LaTeX package that provides syntax highlighting using the Pygments library. Highlighted source code can be customized using fancyvrb.
1.75k stars 126 forks source link

minted doesn't seem to work with import using \subimport* #251

Open JeffFessler opened 4 years ago

JeffFessler commented 4 years ago

If I use the latex package import and its command \subimport to import a .tex file from another directory that in turn uses \inputminted to try to show some code local to that directory, then pygments returns Error: cannot read infile: [Errno 2] No such file or directory:

This issue was reported elsewhere too: https://tex.stackexchange.com/questions/506892/incompatibility-between-import-and-minted

It is a feature request to support \subimport with minted, or to document the incompatibility.

gpoore commented 4 years ago

There are known incompatibilities with import's \import: https://github.com/gpoore/minted/issues/172. There is an open issue to document that: https://github.com/gpoore/minted/issues/233. I don't think I've seen \subimport mentioned before, so I'll add that to what already needs documentation about incompatibilities.

minted has to do everything relative to the root document's directory. If someone can ever provide a mechanism for converting \input within a \subimport file into an \input using the complete relative path, the incompatibility could probably be fixed.

muzimuzhi commented 4 years ago

Fix code

\usepackage{xpatch}

\makeatletter
% fix for first kind
\xpatchcmd\inputminted
  {#3}
  {\import@path #3}
  {}{\fail}

% fix for second kind
\xpretocmd\minted@pygmentize
  {\restore@IfFileExists} % direct \let\IfFileExists\im@@IfFileExists works too
  {}{\fail}
\def\restore@IfFileExists{%
  \ifx\IfFileExists\@iffileonpath
    \let\IfFileExists\im@@IfFileExists
  \fi
}
\makeatother

Explanation

Packages minted and import are incompatible in two ways.

Assume we have the following directory structure, and the subfile.tex is imported by main.tex.

.
├── main.tex
└── subdir
    ├── python-code.py
    └── subfile.tex

First kind: Inside subfile.tex, \inputminted{python}{python-code.py} produces Missing Pygments output error, and the corresponding xxx.pygtex file is not created (under the minted cache directory.)

When user use \inputminted{<lang>}{<code file>} inside a subfile imported by e.g., \import{<path>}{<subfile>}, it is file <path><code file> (in my example, file subdir/python-code.py) that user wants to use. import package stores the <path> in macro \import@path and prepend it to \input@path (for commands like \input and \include to use), but \inputminted{<lang>}{<file>} directly calls \minted@pygmentize[<file>]{<lang>} and \minted@pygmentize uses the <file> directly, without aware of the <path> part. Use `\minted@pygmentize[\import@path <file>]{<lang>} inside \inputmited may fix this kind of incompatibility, also see the above code.

Second kind: If subfile.tex is star-imported in main.tex (say, \import*{subdir/}{subfile}), then minted environment used in subfile.tex produces Missing Pygments output error. This time, xxx.pygtex file is created correctly, the problem is that wrong file path is used when inputing *.pygstyle and xxx.pygtex files.

The second kind of problem occurs only when subfile is star-imported. Compared to the non-stared form, starred form of \import and \subimport changes only one thing: \IfFileExists is locally let to \@iffileonpath. \@iffileonpath{<file>}{<then>}{<else>} forces the use of path prefixes in \input@path, hence skips the step to try to input file <file>, without any path prefixes. Unfortunately, inputing *.pygstyle and *.pygtex files need no path prefixes. In my example, it is file _minted-main/xxx.pygtex, not file subdir/_minted-main/xxx.pygtex that should be input. Locally restore the definition of \IfFileExists inside \minted@pygmentize may fix this kind of problem, also see the second part of the above code.

Test

Here is a test file which includes usages of minted environment and \inputminted in a) the main file, b) the normally \input subfile, c) the imported subfile, d) the star-imported subfile, and e) the subsubfile imported by subfile. Welcome to test.

minted-with-import.zip

JeffFessler commented 4 years ago

@muzimuzhi thanks for the nice patch. It worked fine for me. It looks like something that could be incorporated into the package too.

muzimuzhi commented 4 years ago

To be incorporated into the package, there may need something like

\AtBeginDocument{%
  \@ifpackageloaded{import}{%
    % patch code to redefine minted macros
  }
}
ArchangeGabriel commented 1 year ago

The patch is still needed, but note that apparently starred version of {sub,}import have been deprecated and aliases to the non-starred version, so only taking care of the subpath is required.

ArchangeGabriel commented 1 month ago

The patch does not work anymore with minted3, it complains that way:

! Undefined control sequence.
<argument> \fail 

l.38 ...md\inputminted{#3}{\import@path #3}{}{\fail}

I guess this is expected since \inputminted definition changed a lot in 3.0, but I don’t have enough knowledge to fix it (and it is still required of course, I’ve tried without patching but it does not work).