% File: prettytok.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{prettytok}{2022/07/08}{0.0.1}{Pretty-print token list}
\RequirePackage{precattl}


% ======== check tl_analysis bug.
\msg_new:nnn {prettytok} {tl-analysis-bug}
	{
		Because~of~an~tl-analysis~bug~(see~https://github.com/latex3/latex3/issues/1073),~functions~will~not~be~usable~inside~tl-analysis~functions!~
		Upgrade~your~LaTeX~version,~or~
		define~macro~"prettyignoretlanalysisbug"~before~loading~the~package~to~ignore~this~error.
	}

\ifdefined \prettyignoretlanalysisbug
	\relax
\else
\begingroup
	\tl_set:Nn \__test {}
	\tl_analysis_map_inline:nn {ab} {
		\tl_analysis_map_inline:nn {cd} {
			\tl_put_right:Nx \__test {#1 ##1 .}
		}
	}
	\tl_if_eq:NnF \__test {ac.ad.bc.bd.} {
		\msg_error:nn {prettytok} {tl-analysis-bug}
	}
\endgroup
\fi

% ======== load Lua module.
\sys_if_engine_luatex:T{
	\directlua{require("prettytok")}
	\precattl_exec:n {
		\directlua{prettyprint_frozenrelaxtok=token.get_next().tok}\cFrozenRelax
	}
}

% ======== main code start.

\cs_generate_variant:Nn \exp_args:NNn {NNx}
\cs_generate_variant:Nn \iow_open:Nn {NV}
\cs_generate_variant:Nn \exp_args:NNn {NNV}
\cs_generate_variant:Nn \iow_now:Nn {Nx}
\cs_generate_variant:Nn \tl_if_eq:nnF {xoF}
\cs_generate_variant:Nn \tl_if_eq:nnTF {o}
\cs_generate_variant:Nn \use:N {c}
\cs_generate_variant:Nn \str_map_inline:nn {xn}
\cs_generate_variant:Nn \tl_build_put_right:Nn {Nx}
\cs_generate_variant:Nn \iow_now:Nn {NV}

\iow_new:N \_prettytok_file
\ior_new:N \_prettytok_input_template_file


\exp_args:NNx \providecommand \prettyfilename {pretty-\jobname.html}
\providecommand \prettyrefreshstrategy {4}
\providecommand \prettyrefreshduration {1000}

\msg_new:nnn {prettytok} {already-initialized} {Output~file~is~already~initialized!} 


% See also Lua function: local function check_file_opened()

\global\let \pretty_check_already_init: \relax

\precattl_exec:n {
	\cs_new_protected:Npn \_prettyinit_unchecked: {
		\global\protected\def \pretty_check_already_init: {\msg_error:nn {prettytok} {already-initialized}}

		\iow_open:NV \_prettytok_file \prettyfilename

		% write the template
		\begingroup
			\ior_open:Nn \_prettytok_input_template_file {prettytok_template.html}
			\ior_str_map_variable:NNn \_prettytok_input_template_file \_prettytok_line {
				\str_replace_all:Nnn \_prettytok_line {\cO\^^I} {~}  % workaround: XeTeX prints literal tab character as ^^I which obviously breaks JavaScript
				\iow_now:NV \_prettytok_file \_prettytok_line
			}
			\ior_close:N \_prettytok_input_template_file
		\endgroup

		% refresh strategy
		\iow_now:Nx \_prettytok_file {set_refresh_strategy(\prettyrefreshstrategy,\prettyrefreshduration)}
	}

	\cs_new_protected:Npn \prettyinit: {
		\pretty_check_already_init:
		\_prettyinit_unchecked:
	}
}

\cs_new_eq:NN \prettyinit \prettyinit:


\precattl_exec:n {


	\cs_new_protected:Npn \pretty:n #1 {

		% ======== first write out the preamble if it isn't written. Only do once.
		\ifx \pretty_check_already_init: \relax \prettyinit: \fi

		% ======== the actual content. Collect to one \write to put them on the same line (also maybe for efficiency???)
		\tl_build_begin:N \_prettytok_content
		\tl_build_put_right:Nn \_prettytok_content {print_tl(}
		\tl_analysis_map_inline:nn {#1} {
			% by documentation of \tl_analysis_map_inline:nn: #1=token, #2=char code, #3=catcode
			\int_compare:nNnTF {##2} = {-1} {
				% token is control sequence
				\tl_if_eq:onTF {##1} {\cFrozenRelax} {
					\tl_build_put_right:Nn \_prettytok_content {csfrozenrelax(),}
				} {
					\tl_build_put_right:Nn \_prettytok_content {cs(}
					\tl_if_eq:xoF {\use:c {}} {##1} {
						\str_map_inline:xn {\exp_after:wN \cs_to_str:N ##1} {  % side note, must use str here to preserve spaces
							\tl_build_put_right:Nx \_prettytok_content {\int_eval:n {`####1},}
						}
					}
					\tl_build_put_right:Nn \_prettytok_content {),}
				}
			}
			{
				% token is not control sequence
				\tl_build_put_right:Nn \_prettytok_content {token(##2, "##3"),}
			}
		}
		\tl_build_put_right:Nn \_prettytok_content {)//</script><script>}
		\tl_build_end:N \_prettytok_content
		\iow_now:NV \_prettytok_file \_prettytok_content
	}
}

% ======== Lua expandable variant.
\sys_if_engine_luatex:T {
	\cs_new:Npn \prettye:n #1 {
		\directlua{prettyprint(token.scan_toks())}{#1}
	}

	\cs_new:Npn \prettye:w {
		\directlua{prettyprintw()} {}
	}
	\cs_new:Npn \prettye:nw #1 {
		\directlua{prettyprintw()} {#1}
	}
}

\cs_new_protected:Npn \pretty:w #1 \prettystop {
	\pretty:n {#1} #1 \prettystop
}

\cs_new:Npn \prettystop {}

\cs_generate_variant:Nn \pretty:n {x, o, V}
\let \pretty:N \pretty:n

% ======== normal-catcode alias
\let\prettyN\pretty:n
\let\prettyX\pretty:x
\let\prettyO\pretty:o
\let\prettyV\pretty:V
\let\prettyeN\prettye:n
\let\prettyeW\prettye:w

\cs_generate_variant:Nn \pretty:N {c}


% ======== generate \pretty:nn, \pretty:nnn, etc.
\begingroup
	\def \__tmp:NN #1 #2 {   % #1: example \pretty:nn, #2: example \pretty:n (the latter has one more n than the former)
		\cs_new_protected:Npn #1 ##1 ##2 { #2 {##1 ##2} }
	}
	\cs_generate_variant:Nn \__tmp:NN {cc}

	\def \__tmp {pretty:n}
	\int_step_inline:nn {9} {
		\__tmp:cc {\__tmp n} {\__tmp}
		\tl_put_right:Nn \__tmp {n}
	}

	\sys_if_engine_luatex:T {
		\def \__tmp:NN #1 #2 {   % #1: example \prettye:nn, #2: example \prettye:n (the latter has one more n than the former)
			\cs_new:Npn #1 ##1 ##2 { #2 {##1 ##2} }
		}
		\def \__tmp {prettye:n}
		\int_step_inline:nn {9} {
			\__tmp:cc {\__tmp n} {\__tmp}
			\__tmp:cc {\__tmp nw} {\__tmp w}
			\tl_put_right:Nn \__tmp {n}
		}
	}
\endgroup


\cs_new_protected:Nn \prettyshow:N {
	\exp_args:No \pretty:n {\meaning #1}
}
\cs_generate_variant:Nn \prettyshow:N {c}
\let\prettyshowN\prettyshow:N
\let\prettyshowC\prettyshow:c


