2

This question led to a new package:
rescansync

Let's say I want to replace all a (in source code, not rendered output) in a section with b...

%! TEX program = lualatex
\documentclass{article}
\usepackage{filecontentsdef}
\begin{document}
\begin{filecontentsdefmacro}{\zzz}
abc

abc

abc
\end{filecontentsdefmacro}
\ExplSyntaxOn
\regex_replace_all:nnN {a} {b} \zzz
\ExplSyntaxOff
\filecontentsexec\zzz
\end{document}

It works, however the synctex data is lost (it points to the whole block, not individual paragraph/line)

How can I keep the synctex data?

LuaTeX-only solutions are okay.

5
  • 1
    I guess it's possible to use one of the set_synctex_* functions in LuaTeX, but the documentation is quite sparse. Or modify the .synctex file after compilation.
    – user202729
    Jan 29, 2022 at 5:00
  • 1
    it depends how you define "all". In luatex you could use a lua pattern replace in the process_input_buffer callback, but that would change \makebox to \mbkebox is that OK? Jan 29, 2022 at 11:03
  • @DavidCarlisle Yes that's expected... (for now assume that the capture, or somehow getting the whole content in advance is necessary, otherwise process input buffer can do indeed)
    – user202729
    Jan 29, 2022 at 11:08
  • [[Note]] I made this into a package github.com/user202729/TeXlib/blob/main/rescansync.sty (unfortunately currently there's no documentation&&not on CTAN)
    – user202729
    Apr 5, 2022 at 8:00
  • Note 2. I realize that from Lua it's possible to read the whole file at once... so as long as you don't need to change the number of lines you can just read the whole file, process, and return the correct line according to the line number. See tex.stackexchange.com/questions/641312/… for example.
    – user202729
    Apr 19, 2022 at 15:36

1 Answer 1

2

There's a method. For the documentation, see texdoc luatex and texdoc ltluatex (and the packages being imported).

Disadvantage: it's necessary to write to a real external file (not \scantokens)

Search for !! in the code for the important parts. (note that directlua instead of luacode* is used, so be careful with the catcode)

%! TEX program = lualatex
\documentclass{article}
\usepackage{currfile} % !! need this package for currfilename to be defined
\begin{document}

% write the content to a separate file
\directlua{magic_offset=1 inputlineno_offset=tex.inputlineno+magic_offset} % !!
\begin{filecontents*}[overwrite]{b.tex}
\typeout{1}
abc

abc

abc
\typeout{2}
\end{filecontents*}

% it's also possible to typeset something between the "write" part and the "rescan" part
first line

\directlua{ --[[ !! ]]
saved_synctex_tag=tex.get_synctex_tag()
function handler()
    if token.get_macro("currfilename")=="b.tex" and tex.get_synctex_tag()>0 then
        if not (saved_synctex_tag==nil) then
            tex.set_synctex_tag(saved_synctex_tag)
            saved_synctex_tag=nil
        end
        tex.set_synctex_line(tex.inputlineno+inputlineno_offset)
    end
end
tex.set_synctex_mode(2)
luatexbase.add_to_callback('process_input_buffer', handler, "synctex patch callback")
}\input{b.tex}\directlua{luatexbase.remove_from_callback('process_input_buffer', "synctex patch callback") tex.set_synctex_mode(0) tex.set_synctex_line(0) }

last line

last line
\end{document}

Note that for some weird reason, if [abspath] option is provided to currfile package the handler will be called several times with tex.get_synctex_tag()==0.

Thus the check is added so that it's only called once, but on the actual file.

(I guess that it's because the primitive expand-only the \input command or something)

Removing saved_synctex_tag=nil line; or the get_macro("currfilename")=="b.tex" check will make something worse because (I think, not tested) if b.tex includes e.g. c.tex, then the set_synctex_line should not be run for c.tex.

Alternative to callback, setting the tag within the input-ed file itself always work.

2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .