% File: rescansync.sty
% Copyright 2022 user202729
%
% This work  may be  distributed and/or  modified under  the conditions  of the
% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
% (at your option) any later version.  The latest version of this license is in
% the file:
%
%   http://www.latex-project.org/lppl.txt
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is user202729.

\ProvidesExplPackage{rescansync}{2022/07/09}{0.0.0}{Re-scan tokens with synctex information}

\cs_generate_variant:Nn \regex_replace_all:nnN {nxN}
\cs_generate_variant:Nn \tl_replace_all:Nnn {Nxn}

\msg_new:nnn {rescansync} {currfile-package-not-loaded}
	{currfile~package~is~needed~for~this~functionality}

\msg_new:nnn {rescansync} {saveenv-package-not-loaded}
	{saveenv~package~is~needed~for~this~functionality}

\makeatletter

\sys_if_engine_luatex:TF {
	\let \_RS_optionaldirectlua \directlua
	\cs_new:Npn \rescansync_gettag: {
		\directlua{
			tex.sprint(tex.get_synctex_tag())
		}
	}
}
{
	\let \_RS_optionaldirectlua \@gobble
	\let \rescansync_gettag: \empty
}



%\let \rescansyncTMPfixsync \undefined

\tl_gset:Nn \_RS_count {0}
\edef \_RS_j_tl {\char_generate:nn {`\^^J} {12}}

\iow_new:N \_RS_file

% #1: the content (will be detokenized)
% #2: the number (line offset)
% #3: the file name
% #4: the synctex tag (get from \rescansync_gettag:)
%
% content is an expl3 string with ^^J represent line break -- same format as expected by iow_now
%
% NOTE: because filecontentsdef make character >= 128 in pdftex active character and
% they cannot be \write safely, we detokenize it then replace ^^M later.
\cs_new_protected:Npn \rescansync:nnnn #1 #2 #3 #4 {
	\tl_gset:Nx \_RS_count {\int_eval:n {\_RS_count+1}}
	\tl_set:Nx \_RS_filename {RS\_RS_count-#3}

	\begingroup  % will be closed by the active `~` later
		\iow_open:Nn \_RS_file {\_RS_filename}

		\iow_now:Nx \_RS_file {
			% print some initial padding newlines first
			\prg_replicate:nV {#2} \_RS_j_tl

			% prepend some content to the start of the line...
			\c_backslash_str a \c_backslash_str _RS_optionaldirectlua{tex.set_synctex_tag( #4 )} \c_tilde_str   % this one works
			% note that the directlua must be directly inside the file, not indirectly invoked through some macro

			% print the content
			\detokenize{#1}
		}

		\iow_close:N \_RS_file

		\int_step_inline:nnn {33} {125} {\catcode ##1=11~}  % set all to letter
		\catcode `\~ \active
		\catcode `\\ 0
		\catcode `\{ 1
		\catcode `\} 2~

		% just so that the content '\_RS_optionaldirectlua{tex.set_synctex_tag( ... )}~' is interpreted correctly...


		\char_set_active_eq:NN \~ \endgroup
		\exp_args:NNNo \expandafter \_RS_skip_initial_endlinechars \@@input {\_RS_filename}
}

\cs_new_protected:Npn \rescansync:nnn #1 #2 #3 {
	\rescansync:nnnn {#1} {#2} {#3} {\rescansync_gettag:}  % #4 will be expanded inside
}

\cs_new_protected:Npn \_RS_skip_initial_endlinechars #1 \a {}  % #1: tokens generated by endlinechar

\NewDocumentEnvironment {rescansyncSaveenvPacked} {m} {
	\edef \_RS_lineno {\the\inputlineno}
	\cs_if_free:NT \saveenv {
		\msg_error:nn {rescansync} {saveenv-package-not-loaded}
	}
	\cs_if_free:NT \currfilename {
		\msg_error:nn {rescansync} {currfile-package-not-loaded}
	}
	\saveenv \_RS_body
} {
	\endsaveenv
	\global\edef #1 { {\_RS_body} {\_RS_lineno} {\currfilename} }
	%\pretty:V #1
}

\NewDocumentEnvironment {rescansyncSaveenvghostPacked} {m} {
	\edef \_RS_lineno {\the\inputlineno}
	\cs_if_free:NT \saveenv {
		\msg_error:nn {rescansync} {saveenv-package-not-loaded}
	}
	\cs_if_free:NT \currfilename {
		\msg_error:nn {rescansync} {currfile-package-not-loaded}
	}
	\saveenvghost \_RS_body
} {
	\endsaveenvghost
	\global\edef #1 { {\_RS_body} {\_RS_lineno} {\currfilename} }
	%\pretty:V #1
}

\NewDocumentCommand \rescansyncPacked {m} {
	%\pretty:V {#1}
	\exp_last_unbraced:Nx \rescansync:nnn {#1}
}



\cs_generate_variant:Nn \iow_now:Nn {No}
\cs_generate_variant:Nn \prg_replicate:nn {nV}

% #1: the macro that contain the content, #2: the number (line offset)
\cs_new_protected:Npn \rescansync:nn #1 #2 {
	\cs_if_free:NT \currfilename {
		\msg_error:nn {rescansync} {currfile-package-not-loaded}
	}
	\rescansync:nnn {#1} {#2} {\currfilename}
}
