strc-reg.mkiv / last modification: 2020-01-30 14:16
%D \module
%D   [       file=strc-reg,
%D        version=2008.10.20,
%D          title=\CONTEXT\ Structure Macros,
%D       subtitle=Registers,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Structure Macros / Registers}

\registerctxluafile{strc-reg}{}

\unprotect

\startcontextdefinitioncode

% todo: tag:: becomes rendering
% todo: language, character, linked, location
% todo: fonts etc at sublevels (already defined)

% \starttext
% \placeregister[index]
% \chapter{a} \index{93} \index{456} \index{***} \index{*} \index{@}
% test \index{aa} test \startregister[index][x]{bb} test \page test \page test \page test \stopregister[index][x]
% test \index{aa} test \setregisterentry[index][label=x,entries=bb] test \page test \page test \page test \finishregisterentry[index][label=x]
% test \index{aa} test \setregisterentry[index][label=y] test \page test \page test \page test \finishregisterentry[index][label=y,entries=yy]
% \stoptext

%  \index                {entry}
%  \index[key]           {entry}
%  \index[pageclass::]   {entry}
%  \index[pageclass::key]{entry}
%  \index                {textclass::entry}
%  \index[key]           {textclass::entry}
%  \index[pageclass::]   {textclass::entry}
%  \index[pageclass::key]{textclass::entry}

% tzt variant with n entries, parameters and userdata (altnum)

\installcorenamespace{register}

\installcommandhandler\??register {register} \??register

\let\strc_registers_setup_saved\setupregister

\unexpanded\def\setupregister % maybe we should drop the plural form
  {\dotripleempty\strc_registers_setup}

\def\strc_registers_setup[#1][#2][#3]%
  {\ifthirdargument
     \def\strc_registers_setup_step##1{\strc_registers_setup_saved[#1:##1][#3]}%
     \processcommalist[#2]\strc_registers_setup_step
   \else\ifsecondargument
     \strc_registers_setup_saved[#1][#2]%
   \else
     \strc_registers_setup_saved[#1]%
   \fi\fi}

\unexpanded\def\setupregisters
  {\dosingleempty\strc_registers_setup_root}

\def\strc_registers_setup_root[#1]%
  {\strc_registers_setup_saved[#1]\relax}

\setupregister
  [\c!n=2,
   \c!balance=\v!yes,  % \v!no komt niet zo vaak voor
   \c!align=\v!flushleft,
   \c!tolerance=\v!stretch,
   \c!before=\blank,
  %\c!after=,
  %\c!symbol=,
   \c!compress=\v!no,
   \c!interaction=\v!pagenumber,
   \c!alternative=\v!a,
   \c!distance=\emwidth,
   \c!style=\v!bold,
   \c!pagestyle=\v!slanted,
   \c!indicator=\v!yes,
   \c!criterium=\v!all,
   \c!check=\v!yes, % check for weird see usage
  %\c!command=,
   \c!referencing=\v!on,
   \c!location=\v!middle,
  %\c!maxwidth=,
   \c!number=\v!no,
   \c!unknownreference=\v!empty,
   \c!prefix=\v!both,
  %\c!expansion=,
  %\c!xmlsetup=,
   \c!pagenumber=\v!yes,
   \c!pageprefixconnector=\endash,
   \c!pagesegments=2:2,
   \c!file=\jobname,
  %\c!deeptextcommand=, % undefined by default !
   \c!method=, % no default as we have them in the module, maybe some day in lang-*
   \c!numberorder=\v!numbers, % \v!characters
   \s!language=\currentmainlanguage]%

% yes or no shared ?

\setupregister
  [\c!label=,
   \c!entries=,
   \c!alternative=]

\definemixedcolumns
  [\v!register]
  [\c!n=\registerparameter\c!n,
   \c!balance=\registerparameter\c!balance,
   \c!align=\registerparameter\c!align,
   \c!tolerance=\registerparameter\c!tolerance]

%D \starttyping
%D \setupregister[index][1][textcolor=darkred]
%D \setupregister[index][2][textcolor=darkgreen,textstyle=bold]
%D
%D \placeregister[index][n=1] \blank[3*big]
%D
%D test \index{test+one} test \index{test+two} more \index{more}
%D \stoptyping

\newconditional\c_strc_registers_defining
\setnewconstant\c_strc_registers_maxlevel \plusfive

\ifdefined\Word \else \unexpanded\def\Word#1{#1} \fi

\appendtoks
    \ifconditional\c_strc_registers_defining \else % todo: dosingle ...
      \settrue\c_strc_registers_defining
      \definemixedcolumns[\currentregister][\v!register]% first as otherwise it overloads start/stop
      \clf_defineregister{\currentregister}{\registerparameter\c!referencemethod}%
      \normalexpanded{\presetheadtext[\currentregister=\Word{\currentregister}]}%
      \setuevalue{\currentregister}{\dodoubleempty\strc_registers_insert_entry[\currentregister]}%
      \setuevalue{\e!see\currentregister}{\dodoubleempty\strc_registers_insert_see[\currentregister]}%
      %setuevalue{\e!coupled\currentregister}{\dolinkedregister{\currentregister}}%
      % historic ballast
      \setuevalue{\e!place\currentregister}{\placeregister[\currentregister]}%
      \setuevalue{\e!complete\currentregister}{\completeregister[\currentregister]}%
      \setuevalue{\e!setup\currentregister\e!endsetup}{\setupregister[\currentregister]}%
      \dorecurse\c_strc_registers_maxlevel{% weird, expanded should not be needed
        \normalexpanded{\defineregister[\currentregister:\recurselevel][\currentregister]}%
       %\defineregister[\currentregister:\recurselevel][\currentregister]%
        \letregisterparameter{\c!entries:\recurselevel}\empty % needed as we use detokenize (ok, we can
        \letregisterparameter{\c!keys   :\recurselevel}\empty % avoid it, but it's faster too)
      }%
      %
      \setfalse\c_strc_registers_defining
    \fi
\to \everydefineregister

\appendtoks
    \clf_setregistermethod{\currentregister}{\registerparameter\c!referencemethod}%
\to \everysetupregister

%D Registering:

\glet\currentregistername  \empty
\glet\currentregisternumber\!!zerocount

\def\strc_registers_register_page_entry
  {\iftrialtypesetting
     \expandafter\gobblethreearguments
   \else
     \expandafter\strc_registers_register_page_entry_indeed
   \fi}

\def\strc_registers_register_page_expand_xml_entries
  {\xmlstartraw
     \xdef\currentregisterentriesa{\registerparameter{\c!entries:1}}%
     \xdef\currentregisterentriesb{\registerparameter{\c!entries:2}}%
     \xdef\currentregisterentriesc{\registerparameter{\c!entries:3}}%
   \xmlstopraw
   \glet\currentregistercoding\s!xml}

\def\strc_registers_register_page_expand_yes_entries
  {\xdef\currentregisterentriesa{\registerparameter{\c!entries:1}}%
   \xdef\currentregisterentriesb{\registerparameter{\c!entries:2}}%
   \xdef\currentregisterentriesc{\registerparameter{\c!entries:3}}%
   \glet\currentregistercoding\s!tex}

\def\strc_registers_register_page_expand_nop_entries
  {\xdef\currentregisterentriesa{\detokenizedregisterparameter{\c!entries:1}}%
   \xdef\currentregisterentriesb{\detokenizedregisterparameter{\c!entries:2}}%
   \xdef\currentregisterentriesc{\detokenizedregisterparameter{\c!entries:3}}%
   \glet\currentregistercoding\s!tex}

\def\strc_registers_register_page_expand_xml
  {\xmlstartraw
     \xdef\currentregisterentries{\registerparameter\c!entries}%
   \xmlstopraw
   \glet\currentregistercoding\s!xml}

\def\strc_registers_register_page_expand_yes
  {\xdef\currentregisterentries{\registerparameter\c!entries}%
   \glet\currentregistercoding\s!tex}

\def\strc_registers_register_page_expand_nop
  {\xdef\currentregisterentries{\detokenizedregisterparameter\c!entries}%
   \glet\currentregistercoding\s!tex}

\def\strc_registers_register_page_expand_xml_keys
  {\xmlstartraw
     \xdef\currentregisterkeysa{\registerparameter{\c!keys:1}}%
     \xdef\currentregisterkeysb{\registerparameter{\c!keys:2}}%
     \xdef\currentregisterkeysc{\registerparameter{\c!keys:3}}%
   \xmlstopraw}

\def\strc_registers_register_page_expand_yes_keys
  {\xdef\currentregisterkeysa{\registerparameter{\c!keys:1}}%
   \xdef\currentregisterkeysb{\registerparameter{\c!keys:2}}%
   \xdef\currentregisterkeysc{\registerparameter{\c!keys:3}}}

\def\strc_registers_register_page_entry_indeed#1#2#3% register data userdata
  {\begingroup
   \edef\currentregister{#1}%
   %\setupcurrentregister[\c!entries=,\c!label=,\c!keys=,\c!alternative=,#2]%
   \setupcurrentregister[#2]%
   \edef\currentregisterlabel    {\registerparameter\c!label}%
   \edef\currentregisterexpansion{\registerparameter\c!expansion}%
   \edef\currentregisterownnumber{\registerparameter\c!ownnumber}%
   \xdef\currentregisterkeys     {\registerparameter\c!keys}%
   \xdef\currentregisterentries  {\registerparameter\c!entries}%
   \xdef\currentregisterxmlsetup {\registerparameter\c!xmlsetup}%
   \ifx\currentregisterentries\empty
     \ifx\currentregisterexpansion\s!xml
       \strc_registers_register_page_expand_xml_entries
     \else\ifx\currentregisterexpansion\v!yes
       \strc_registers_register_page_expand_yes_entries
     \else
       \strc_registers_register_page_expand_nop_entries
     \fi\fi
   \else
     \ifx\currentregisterexpansion\s!xml
       \strc_registers_register_page_expand_xml
     \else\ifx\currentregisterexpansion\v!yes
       \strc_registers_register_page_expand_yes
     \else
       \strc_registers_register_page_expand_nop
     \fi\fi
   \fi
   \ifx\currentregisterkeys\empty
     \ifx\currentregistercoding\s!xml
       \strc_registers_register_page_expand_xml_keys
     \else
       \strc_registers_register_page_expand_yes_keys
     \fi
   \fi
   \setnextinternalreference
   % we could consider storing register entries in a list which we
   % could then sort
   \glet\currentregistername\currentregister
   \xdef\currentregisternumber{\clf_storeregister % 'own' should not be in metadata
        metadata {%
            name     {\currentregister}%
            coding   {\currentregistercoding}%
        \ifx\currentregisterownnumber\v!yes
            own      {\registerparameter\c!alternative}% can be used instead of pagenumber
        \fi
        \ifx\currentreferencecoding\s!xml
            xmlroot  {\xmldocument} % only useful when text
        \fi
        \ifx\currentregisterxmlsetup\empty \else
            xmlsetup {\currentregisterxmlsetup}%
        \fi
        }%
        references {%
        \ifx\currentregisterlabel\empty \else
            label    {\currentregisterlabel}%
        \fi
%             view     {\interactionparameter\c!focus}%
        }%
        entries {%
            % we need a special one for xml, this is just a single one
            \ifx\currentregisterentries\empty
                entries {
                    {\currentregisterentriesa}%
                    {\currentregisterentriesb}%
                    {\currentregisterentriesc}%
                }
            \else
                entry {\currentregisterentries}%
            \fi
            \ifx\currentregisterkeys\empty
                keys {
                    {\currentregisterkeysa}%
                    {\currentregisterkeysb}%
                    {\currentregisterkeysc}%
                }
            \else
                key {\currentregisterkeys}%
            \fi
        }%
        userdata {\detokenize\expandafter{\normalexpanded{#3}}}
   }%
   \clf_setinternalreference
      internal \locationcount
      view     {\interactionparameter\c!focus}%
   \relax % this will change
   \ifx\currentregisterownnumber\v!yes
     \glet\currentregistersynchronize\relax
   \else
     \xdef\currentregistersynchronize{\clf_deferredenhanceregister{\currentregister}\number\currentregisternumber}%
   \fi
   \currentregistersynchronize % here?
   % needs thinking ... bla\index{bla}. will break before the . but adding a
   % penalty is also no solution
   \dostarttagged\t!registerlocation\currentregister
   \attribute\destinationattribute\lastdestinationattribute \signalcharacter % no \strut as it will be removed during cleanup
   \dotagregisterlocation
   \dostoptagged
   \endgroup}

\unexpanded\def\dosetfastregisterentry#1#2#3#4#5% register entry key processor processor
  {\begingroup
   \edef\currentregister{#1}%
   \setnextinternalreference
   \glet\currentregistername\currentregister
   \xdef\currentregisternumber{\clf_storeregister
     {%
        metadata {%
          name {\currentregister}%
        }
        entries {%
          entry {#2}%
          key   {#3}%
        }%
        processors {%
          entry {#4}%
          page  {#5}%
        }%
     }%
   }%
   % overlap with the above
 % \clf_setinternalreference
 %    internal \locationcount
 %    view     {\interactionparameter\c!focus}%
   \relax % this will change
   \xdef\currentregistersynchronize{\clf_deferredenhanceregister{\currentregister}\number\currentregisternumber}%
   \currentregistersynchronize % here?
   \dostarttagged\t!registerlocation\currentregister
   \attribute\destinationattribute\lastdestinationattribute \signalcharacter % no \strut as it will be removed during cleanup
   \dotagregisterlocation
   \dostoptagged
   \endgroup}

\let\dotagregisterlocation\relax % experiment

\unexpanded\def\strc_registers_insert_entry[#1][#2]%
  {\def\currentregister{#1}%
   \edef\p_ownnumber{\registerparameter\c!ownnumber}%
   \ifx\p_ownnumber\v!yes
     \expandafter\strc_registers_insert_entry_yes
   \else
     \expandafter\strc_registers_insert_entry_nop
   \fi{#2}}

% \def\strc_registers_insert_entry_nop#1#2%
%   {\doflushatpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!entries={#2}}{}}}
%
% \def\strc_registers_insert_entry_yes#1#2#3%
%   {\doflushatpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!alternative=#2,\c!entries={#3}}{}}}
%
% less tokens passed (nicer for tracing) .. could become installable

\def\strc_registers_insert_entry_nop
  {\ifvmode
     \expandafter\strc_registers_insert_entry_nop_par
   \else
     \expandafter\strc_registers_insert_entry_nop_txt
   \fi}

\def\strc_registers_insert_entry_yes
  {\ifvmode
     \expandafter\strc_registers_insert_entry_yes_par
   \else
     \expandafter\strc_registers_insert_entry_yes_txt
   \fi}

\def\strc_registers_insert_entry_nop_par#1#2%
  {\flushatnextpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!entries={#2}}{}}}

\def\strc_registers_insert_entry_yes_par#1#2#3%
  {\flushatnextpar{\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!alternative=#2,\c!entries={#3}}{}}}

\def\strc_registers_insert_entry_nop_txt#1#2%
  {\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!entries={#2}}{}}

\def\strc_registers_insert_entry_yes_txt#1#2#3%
  {\strc_registers_register_page_entry\currentregister{\c!keys={#1},\c!alternative=#2,\c!entries={#3}}{}}

\unexpanded\def\startregister{\doquadrupleempty\strc_registers_start_entry}
\unexpanded\def\stopregister {\dodoubleargument\strc_registers_stop_entry}

% a synonym, so that we can nest with overlap without syntax check problems

\let\openregisterrange \startregister
\let\closeregisterrange\stopregister

\def\strc_registers_start_entry[#1][#2][#3][#4]#5%
  {\iffourthargument
     % #1=register #2=tag #3=own #4=sortkey #5=entry
     \doflushatpar{\strc_registers_register_page_entry{#1}{\c!label=#2,\c!alternative=#3,\c!keys={#4},\c!entries={#5}}{}}%
   \else
     % #1=register #2=tag #3=sortkey #5=entry
     \doflushatpar{\strc_registers_register_page_entry{#1}{\c!label=#2,\c!keys={#3},\c!entries={#5}}{}}%
   \fi}

\def\strc_registers_stop_entry[#1][#2]%
  {\normalexpanded{\ctxlatecommand{extendregister("#1","#2")}}}

\def\setregisterentry   {\dotripleempty\strc_registers_set_entry}
\def\finishregisterentry{\dotripleempty\strc_registers_finish_entry}

% not yet document, not sure if this will stay:

\def\strc_registers_set_entry   [#1][#2][#3]{\doflushatpar{\strc_registers_register_page_entry{#1}{#2}{#3}}}
\def\strc_registers_finish_entry[#1][#2][#3]{\strc_registers_finish_entry_indeed{#1}{#2}{#3}}

\def\strc_registers_finish_entry_indeed#1#2#3% register data userdata
  {\begingroup
   \edef\currentregister{#1}%
  %\setupcurrentregister[\c!entries=,\c!label=,\c!keys=,\c!alternative=,#2]% todo: fast setter
   \resetregisterparameter\c!entries
   \resetregisterparameter\c!label
   \resetregisterparameter\c!keys
   \resetregisterparameter\c!alternative
   \setupcurrentregister[#2]%
   \edef\currentregisterlabel    {\registerparameter\c!label}%
   \edef\currentregisterexpansion{\registerparameter\c!expansion}%
   \edef\currentregisterownnumber{\registerparameter\c!ownnumber}%
   \xdef\currentregisterkeys     {\registerparameter\c!keys}%
   \ifx\currentregisterexpansion\s!xml
     \xmlstartraw
       \xdef\currentregisterentries{\registerparameter\c!entries}%
     \xmlstopraw
     \glet\currentregistercoding\s!xml
   \else
     \ifx\currentregisterexpansion\v!yes
       \xdef\currentregisterentries{\registerparameter\c!entries}%
     \else
       \xdef\currentregisterentries{\detokenizedregisterparameter\c!entries}%
     \fi
     \glet\currentregistercoding\s!tex
   \fi
   % I hate this kind of mess ... but it's a user request.
   \ifx\currentregisterentries\empty
       \normalexpanded{\ctxcommand{extendregister("\currentregister","\currentregisterlabel", {
            metadata = {
            \ifx\currentregisterownnumber\v!yes
                own = "\registerparameter\c!alternative", % can be used instead of pagenumber
            \fi
            },
            userdata = \!!bs\detokenize{#3}\!!es
         })%
       }}%
   \else
       \normalexpanded{\ctxcommand{extendregister("\currentregister","\currentregisterlabel", {
            metadata = {
              % catcodes = \the\catcodetable,
                coding   = "\currentregistercoding",
            \ifx\currentregisterownnumber\v!yes
                own      = "\registerparameter\c!alternative", % can be used instead of pagenumber
            \fi
            },
            entries = {
                % we need a special one for xml, this is just a single one
                \!!bs\currentregisterentries\!!es,
                \!!bs\currentregisterkeys\!!es
            },
            userdata = \!!bs\detokenize{#3}\!!es
         })
       }}%
   \fi
   \endgroup}

% The following variants are meant for (for instance xml). There is some
% overlap with previously defined macros.
%
% \starttext
%     \setstructurepageregister[index][entries=alpha]a
%     \setstructurepageregister[index][entries=gamma]g
%     \setstructurepageregister[index][entries=beta]b
%     \setstructurepageregister[index][entries:1=alpha,keys:1=z]a
%     \setstructurepageregister[index][entries:1=gamma,keys:1=x]g
%     \setstructurepageregister[index][entries:1=beta, keys:1=y]b
%     \index{alpha}a
%     \index{gamma}g
%     \index{beta}b
%     \placeregister[index][n=1]
% \stoptext

% some overlap with previous

\unexpanded\def\setstructurepageregister
  {\dotripleempty\strc_registers_set}

\def\strc_registers_set[#1][#2][#3]% [register][settings][userdata]
  {\doflushatpar{\strc_registers_register_page_entry{#1}{#2}{#3}}}

\unexpanded\def\startstructurepageregister{\doquadrupleempty\strc_registers_start}
\unexpanded\def\stopstructurepageregister {\dodoubleargument\strc_registers_stop}

\let\openstructurepageregisterrange \startstructurepageregister
\let\closestructurepageregisterrange\stopstructurepageregister

\def\strc_registers_start[#1][#2][#3][#4]% [register][tag][settings][userdata]
  {\doflushatpar{\strc_registers_register_page_entry{#1}{\c!label=#2,#3}{#4}}}

\def\strc_registers_stop[#1][#2]%
  {\normalexpanded{\ctxlatecommand{structures.registers.extend("#1","#2")}}}

% So far.

\unexpanded\def\strc_registers_insert_see[#1][#2]#3#4%
  {\doflushatpar{\strc_registers_insert_see_indeed{#1}{#2}{#3}{#4}}}

\def\strc_registers_insert_see_indeed#1#2#3#4% register key entry seeword
  {\begingroup
   \edef\currentregister{#1}%
   \edef\currentregisterexpansion{\registerparameter\c!expansion}%
   \ifx\currentregisterexpansion\s!xml
     \xmlstartraw
       \xdef\currentregisterentries{\detokenize{#3}}% not ok yet
       \xdef\currentregisterseeword{\detokenize{#4}}% not ok yet
     \xmlstopraw
     \glet\currentregistercoding\s!xml
   \else
     \ifx\currentregisterexpansion\v!yes
       \xdef\currentregisterentries{#3}% not ok yet
       \xdef\currentregisterseeword{#4}% not ok yet
     \else
       \xdef\currentregisterentries{\detokenize{#3}}% not ok yet
       \xdef\currentregisterseeword{\detokenize{#4}}% not ok yet
     \fi
     \glet\currentregistercoding\s!tex
   \fi
   \setnextinternalreference
   % we could consider storing register entries in list
   \edef\temp{\clf_storeregister{% \temp grabs the nofentries
     metadata {%
         kind {see}%
         name {\currentregister}%
     }%
     references {%
%         view  {\interactionparameter\c!focus}%
     }%
     entries {%
         % we need a special one for xml, this is just a single one
         entry {\currentregisterentries}%
         key   {#2}%
     }%
     seeword {%
         text  {\currentregisterseeword}%
     }%
   }}%
   \clf_setinternalreference
      internal \locationcount
      view     {\interactionparameter\c!focus}%
   \relax % this will change
   \dostarttagged\t!registerlocation\currentregister
   \attribute\destinationattribute\lastdestinationattribute \signalcharacter % no \strut as it will be removed during cleanup
   \dotagregisterlocation
   \dostoptagged
   \endgroup}

%D Rendering:

% todo: c!language ipv s!language

\let\utilityregisterlength\!!zerocount

\def\determineregistercharacteristics
  {\dodoubleempty\strc_registers_determine_characteristics}

\def\strc_registers_determine_characteristics[#1][#2]%
  {\begingroup
   \setupregister[#1][#2]%
   \edef\currentregister{\firstinset{#1}}%
   \normalexpanded{\endgroup\noexpand\xdef\noexpand\utilityregisterlength{\clf_analyzeregister
     {\currentregister}%
     {%
        language    {\registerparameter\s!language}%
        method      {\registerparameter\c!method}%
        numberorder {\registerparameter\c!numberorder}%
        compress    {\registerparameter\c!compress}%
        criterium   {\registerparameter\c!criterium}%
        pagenumber  \ifx\registerpageseparatorsymbol\empty false\else true\fi
     }%
   }}%
   \ifcase\utilityregisterlength\relax
     \resetsystemmode\v!register
   \else
     \setsystemmode  \v!register
   \fi}

\newtoks\everyplaceregister

\appendtoks
    \dontcomplain
\to \everyplaceregister

\newconditional\c_strc_registers_text_interaction

\unexpanded\def\placeregister
  {\dodoubleempty\strc_registers_place}

\def\strc_registers_place[#1][#2]%
  {\iffirstargument
     \begingroup
    %\forgetall
     \setupregister[#1][#2]% can be a list
     \edef\currentregister{\firstinset{#1}}%
     \the\everyplaceregister
     \ifnum\namedmixedcolumnsparameter\currentregister\c!n>\plusone
       \startmixedcolumns[\currentregister]
         \strc_registers_place_indeed{#1}%
       \stopmixedcolumns
     \else
       \strc_registers_place_indeed{#1}%
     \fi
     \endgroup
   \fi}

\def\strc_registers_place_indeed#1%
  {\doifelse{\registerparameter\c!interaction}\v!text
     \settrue\setfalse\c_strc_registers_text_interaction
   \clf_processregister
     {#1}%
     {%
        language      {\registerparameter\s!language}%
        method        {\registerparameter\c!method}%
        numberorder   {\registerparameter\c!numberorder}%
        check         {\registerparameter\c!check}%
        compress      {\registerparameter\c!compress}%
        criterium     {\registerparameter\c!criterium}%
        pagemethod    {\registerparameter\c!pagemethod}%
        pagenumber    \ifx\registerpageseparatorsymbol\empty false\else true\fi
     }{%
        separatorset  {\registerparameter\c!pageprefixseparatorset}%
        conversionset {\registerparameter\c!pageprefixconversionset}%
        starter       {\registerparameter\c!pageprefixstarter}%
        stopper       {\registerparameter\c!pageprefixstopper}%
        set           {\registerparameter\c!pageprefixset}%
        segments      {\registerparameter\c!pageprefixsegments}%
        connector     {\registerparameter\c!pageprefixconnector}%
     }{%
        prefix        {\registerparameter\c!pageprefix}%
        separatorset  {\registerparameter\c!pageseparatorset}%
        conversionset {\registerparameter\c!pageconversionset}%
        starter       {\registerparameter\c!pagestarter}%
        stopper       {\registerparameter\c!pagestopper}%
        segments      {\registerparameter\c!pagesegments}%
     }%
   \relax}

\def\strc_registers_limited_entry#1%
  {\limitatetext{#1}\currentregistermaxwidth\unknown}%

\appendtoks
    \edef\currentregistermaxwidth{\registerparameter\c!maxwidth}%
    \ifx\currentregistermaxwidth\empty
      \let\limitedregisterentry\firstofoneargument
    \else
      \let\limitedregisterentry\strc_registers_limited_entry
    \fi
\to \everyplaceregister

\unexpanded\def\completeregister
  {\dodoubleempty\strc_registers_complete}

\def\strc_registers_complete[#1][#2]%
  {\iffirstargument
     \begingroup
     \edef\currentregister{\firstinset{#1}}%
     \normalexpanded{\startnamedsection[\v!chapter][\c!title={\headtext{\currentregister}},reference=\currentregister]}%
     \placeregister[#1][#2]%
     \page[\v!yes]%
     \stopnamedsection
     \endgroup
   \fi}

% test case for collapsing (experimental, for Steffen Wolfrum)
%
% \starttext
% \placeregister[index][compress=no]  \blank[2*big]
% \placeregister[index][compress=yes] \blank[2*big]
% \placeregister[index][compress=all] \page
% \dorecurse{10}{test 1:!\index{test} test \page}
% \dorecurse{5} {test 2:\recurselevel      \page}
% \dorecurse{10}{test 3:!\index{test} test \page}
% \dorecurse{5} {test 4:\recurselevel      \page}
% \dorecurse{1} {test 5:!\index{test} test \page}
% \dorecurse{5} {test 6:\recurselevel      \page}
% \dorecurse{10}{test 7:!\index{test} test \page}
% \dorecurse{5} {test 8:\recurselevel      \page}
% oeps \index{oeps}
% xxxx \index{xxxx}
% todo \index{todo}
% \stoptext

%D Character rendering (sections):

\installcorenamespace{registerindicator}

\def\defaultregistercharacter#1%
  {\edef\currentregistercharacter{#1}%
   \ifx\currentregistercharacter\empty
     % skip
   \else\ifx\currentregistercharacter\s!unknown
     % skip
   \else
     \edef\p_indicator{\registerparameter\c!indicator}%
     \ifx\p_indicator\v!yes
       \strc_registers_place_character_yes
     \else
       \strc_registers_place_character_nop
     \fi
   \fi\fi}

\def\strc_registers_place_character_yes
  {\expandnamespaceparameter\??registerindicator\registerparameter\c!alternative\v!a{\currentregistercharacter}}

\def\strc_registers_place_character_nop
  {\registerparameter\c!before
   \goodbreak}

% a = <before> <goodbreak> <character> <par> <after> <nobreak>

\def\strc_registers_indicator_a#1#2%
  {\registerparameter\c!before
   % bugged, why does leftskip gets set: \vskip\lineheight\goodbreak\vskip-\lineheight
   \typo_injectors_check_register
   \begingroup
   \useregisterstyleandcolor\c!style\c!color
   \dontleavehmode
   \typo_injectors_mark_register
   \strut
   \iflocation
     \dosetdirectpagereference{\currentregister:\v!section:#1}%
   \fi
   \registerparameter\c!command{#2}%
   \endgroup
   \blank[\v!samepage]%
   \registerparameter\c!after
   \par
   \nobreak}

% b = <goodbreak> <before> <character> <after> <nobreak>

\def\strc_registers_indicator_b#1#2%
  {\registerparameter\c!before
   \typo_injectors_check_register
   \begingroup
   \useregisterstyleandcolor\c!style\c!color
   \dontleavehmode
   \typo_injectors_mark_register
   \strut
   \iflocation
     \dosetdirectpagereference{\currentregister:\v!section:#1}%
   \fi
   \registerparameter\c!command{#2}%
   \endgroup
   \registerparameter\c!after
   \nobreak}

\setvalue{\??registerindicator a}#1{\strc_registers_indicator_a{#1}{#1}}
\setvalue{\??registerindicator A}#1{\strc_registers_indicator_a{#1}{\WORD{#1}}}
\setvalue{\??registerindicator b}#1{\strc_registers_indicator_b{#1}{#1}}
\setvalue{\??registerindicator B}#1{\strc_registers_indicator_b{#1}{\WORD{#1}}}

%D The following macros are the interface to the rendering. These are
%D generated by \LUA. This might change.

% \showinjector
% \setinjector[register][2][\column]
%
% \starttext
%     first  \index{first}
%     second \index{second}
%     third  \index{third}
%     fourth \index{fourth}
%     \placeregister[index]
% \stoptext

\doinstallinjector\s!register

%D Beware, we get funny side effects when a dangling \index precedes an
%D placeindex as then flushing takes place inside the index. Took me hours
%D to notice that.

\newconstant   \c_strc_registers_page_state % 0=nothing  1=page  2=see
\newdimen      \d_strc_registers_distance

\unexpanded\def\startregisteroutput
  {\endgraf
   \begingroup
   \d_strc_registers_distance\registerparameter\c!distance\relax
   \dostarttaggedchained\t!register\currentregister\??register
   \forgeteverypar
   \forgetparindent
   \forgetparskip}

\unexpanded\def\stopregisteroutput
  {\endgraf
   \dostoptagged
   \endgroup}

\newdimen\d_strc_registers_hangindent
\newcount\c_strc_registers_hangafter

\unexpanded\def\usenestedregisterstyleandcolor#1#2% will change
  {\useregisterstyleandcolor#1#2%
   % how about style
   \ifconditional\c_strc_registers_text_interaction
     \ifx\currentcolorparameter\empty \else
       \resetinteractionparameter\c!color
       \resetinteractionparameter\c!contrastcolor
     \fi
   \fi}

\unexpanded\def\startregisterentries#1% depth
  {\endgraf
   \begingroup
   \scratchcounter\ifnum#1>\c_strc_registers_maxlevel\c_strc_registers_maxlevel\else#1\fi\relax
   \dostarttagged\t!registerentries\empty
   \let\savedcurrentregister\currentregister
   \edef\currentregister{\currentregister:\number\scratchcounter}%
   \usenestedregisterstyleandcolor\c!textstyle\c!textcolor
   \ifnum\scratchcounter>\plusone
     \advance\leftskip\d_strc_registers_distance\relax
   \fi
   \d_strc_registers_hangindent\registerparameter\c!distance\relax
   \c_strc_registers_hangafter \plusone
   \blank[\v!samepage]%
   \let\currentregister\savedcurrentregister}

\unexpanded\def\stopregisterentries
  {\endgraf
   \dostoptagged
   \endgroup}

\unexpanded\def\startregisterentry#1% todo: level
  {\typo_injectors_check_register
   \begingroup
   \dostarttagged\t!registerentry\empty
   \global\setconstant\c_strc_registers_page_state\zerocount
   \hangindent\d_strc_registers_hangindent
   \hangafter \c_strc_registers_hangafter
   \typo_injectors_mark_register}

\unexpanded\def\stopregisterentry
  {\endgraf
   \global\setconstant\c_strc_registers_page_state\zerocount
   \dostoptagged
   \endgroup}

\unexpanded\def\startregistersection#1% title
  {\dostarttagged\t!registersection\empty
   \dostarttagged\t!registertag\empty
   \registercharacter{#1}\endgraf
   \dostoptagged}

\unexpanded\def\stopregistersection
  {\dostoptagged
   \endgraf}

\unexpanded\def\startregisterpages
  {\begingroup
   \dostarttagged\t!registerpages\empty
   \useregisterstyleandcolor\c!pagestyle\c!pagecolor
   \registerparameter\c!pageleft}

\unexpanded\def\stopregisterpages
  {\registerparameter\c!pageright
   \dostoptagged
   \endgroup}

\unexpanded\def\startregisterseewords
  {\begingroup
   \dostarttagged\t!registerpage\empty
   \useregisterstyleandcolor\c!pagestyle\c!pagecolor}

\unexpanded\def\stopregisterseewords
  {\dostoptagged
   \endgroup}

\unexpanded\def\registerpageseparator % todo: , configurable
  {\ifcase\c_strc_registers_page_state
     \hskip\d_strc_registers_distance\relax
   \or
     \dostarttagged\t!registerseparator\empty
     \registerpageseparatorsymbol % page
     \dostoptagged
   \or
     \dostarttagged\t!registerseparator\empty
     \registerpageseparatorsymbol % see
     \dostoptagged
   \fi}

\unexpanded\def\registeronepagerangeseparator
  {|\endash|} % todo use \prewordbreak

% \unexpanded\def\withregisterpagecommand#1#2#3#4%
%   {\def\currentregisterpageindex{#2}%
%    \iflocation
%      \goto{\applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}}[internal(#2)]%
%    \else
%      \applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}%
%    \fi}

\unexpanded\def\withregisterpagecommand#1#2#3#4%
  {\ifcase#3\relax
     {\tt [entry\space not\space flushed]}%
   \else
     \def\currentregisterpageindex{#2}%
     \iflocation
       \strc_references_goto_internal{\applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}}[internal(#2)]%
     \else
       \applyprocessor{#1}{\registerparameter\c!pagecommand{#4}}%
     \fi
   \fi}

\unexpanded\def\pushcurrentregister#1%
  {\let\m_current_register\currentregister
   \edef\currentregister{#1}}

\unexpanded\def\popcurrentregister
  {\let\currentregister\m_current_register}

\unexpanded\def\registeronepage#1#2#3#4#5% #1:class #2:processor content
  {\pushcurrentregister{#1}%
   \edef\p_pagenumber{\registerparameter\c!pagenumber}%
   \ifx\p_pagenumber\v!no\else
     \registerpageseparator
     \global\setconstant\c_strc_registers_page_state\plusone
     \dostarttagged\t!registerpage\empty
     \withregisterpagecommand{#2}{#3}{#4}{#5}%
     \dostoptagged
   \fi
   \popcurrentregister}

\newconditional\c_strc_registers_following

\appendtoks
    \edef\p_compress{\registerparameter\c!compress}%
    \ifx\p_compress\v!text
      \settrue\c_strc_registers_following
      \letregisterparameter\c!compress\v!yes
    \else
      \setfalse\c_strc_registers_following
    \fi
\to \everyplaceregister

\unexpanded\def\registerpagerange#1#2#3#4#5#6#7#8% #1:class #2:processor  content, content todo: -- configurable
  {\pushcurrentregister{#1}%
   \edef\p_pagenumber{\registerparameter\c!pagenumber}%
   \ifx\p_pagenumber\v!no\else
     \registerpageseparator
     \global\setconstant\c_strc_registers_page_state\plusone
     \dostarttagged\t!registerpagerange\empty
     \dostarttagged\t!registerfrompage\empty
     \withregisterpagecommand{#2}{#3}{#4}{#5}%
     \dostoptagged
     \ifconditional\c_strc_registers_following
       \ifnum#3=\numexpr#6-1\relax
         \labeltext{following:\s!singular}%
       \else
         \labeltext{following:\s!plural}%
       \fi
    \else
       \registeronepagerangeseparator
       \dostarttagged\t!registertopage\empty
       \withregisterpagecommand{#2}{#6}{#7}{#8}%
     \fi
     \dostoptagged
     \dostoptagged
   \fi
   \popcurrentregister}

\unexpanded\def\defaultregisterentry#1#2#3#4#5% #1:class #2:processor #3:internal #4:seeindex #5:word
  {\pushcurrentregister{#1}%
   \def\currentregisterpageindex{#3}%
   \iflocation
     \def\currentregisterseeindex{#4}%
     \ifconditional\c_strc_registers_text_interaction
       \strc_references_goto_internal{\setlocationcolor\doapplyregisterentrycommand{#2}{#5}}[internal(#3)]%
     \else
       \doapplyregisterentrycommand{#2}{#5}%
     \fi
   \else
     \let\currentregisterseeindex\empty
     \doapplyregisterentrycommand{#2}{#5}%
   \fi
   \popcurrentregister}

\unexpanded\def\doapplyregisterentrycommand#1#2% processor text
  {\dostarttagged\t!registercontent\empty
   \ifx\currentregisterseeindex\empty \else
     \dontleavehmode
     \dosetdirectpagereference{seeindex:\currentregisterseeindex}% maybe some day we will support an area
   \fi
   \applyprocessor{#1}{\registerparameter\c!textcommand{\limitedregisterentry{\registerparameter\c!deeptextcommand{#2}}}}%
   \dostoptagged}

\unexpanded\def\doapplyregisterseecommand#1#2%
  {\ifx\currentregisterseeindex\empty
     \applyprocessor{#1}{#2}%
   \else\iflocation
     \strc_references_goto_internal{\applyprocessor{#1}{#2}}[seeindex:\currentregisterseeindex]%
   \else
     \applyprocessor{#1}{#2}%
   \fi\fi}

\unexpanded\def\defaultregisterseeword#1#2#3#4#5#6#7% class i n #3:processor #4:internal #5:seeindex #6:word
  {\pushcurrentregister{#1}%
   \ifnum#2=\plusone
     \registerpageseparator
   \fi
   \global\setconstant\c_strc_registers_page_state\plustwo
   \def\currentregisterpageindex{#5}%
   \dostarttagged\t!registersee\empty
   \settrue\c_strc_registers_page_done
   \iflocation
     \def\currentregisterseeindex{#6}%
   \else
     \let\currentregisterseeindex\empty
   \fi
   \ifnum#2=\plusone
     \labeltexts\v!see{\doapplyregisterseecommand{#4}{#7}}%
   \else\ifnum#2=#3\relax
     \labeltexts\v!and{\doapplyregisterseecommand{#4}{#7}}%
   \else
     ,\space\doapplyregisterseecommand{#4}{#7}%
   \fi\fi
   \dostoptagged
   \popcurrentregister}

\unexpanded\def\doapplyregistersectioncommand#1#2%
  {\ifx\currentregistersectionindex\empty
     \applyprocessor{#1}{#2}%
   \else\iflocation
     \strc_references_goto_internal{\applyprocessor{#1}{#2}}[sectionindex:\currentregistersectionindex]%
   \else
     \applyprocessor{#1}{#2}%
   \fi\fi}

\unexpanded\def\defaultregistersection#1#2#3#4#5#6#7% class i n #4:processor #5:internal #6:sectionindex #7:word
  {\pushcurrentregister{#1}%
   \ifnum#2=\plusone
     \registerpageseparator
   \fi
   \global\setconstant\c_strc_registers_page_state\plustwo
   \def\currentregisterpageindex{#5}%
   \dostarttagged\t!registersection\empty
   \settrue\c_strc_registers_page_done
   \iflocation
     \def\currentregistersectionindex{#6}%
   \else
     \let\currentregistersectionindex\empty
   \fi
   \ifnum#2=\plusone\else
      ,\space
   \fi
   \doapplyregistersectioncommand{#4}{#7}%
   \dostoptagged
   \popcurrentregister}

\let\registersection  \defaultregistersection
\let\registerseeword  \defaultregisterseeword
\let\registerentry    \defaultregisterentry
\let\registercharacter\defaultregistercharacter

%D Experimental:
%D
%D \starttyping
%D \setupregister
%D   [index]
%D   [pagesegments=1:4,
%D    pagemethod=section]
%D
%D \starttext
%D
%D     \chapter {one} \section {alpha}
%D
%D     x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
%D     x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
%D
%D     \chapter {one} \section {alpha}
%D
%D     x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
%D     x\index {whatever 1}x\index {whatever 2}x\index {whatever 2}x \page
%D
%D     \placeindex[n=1]
%D
%D \stoptext
%D \stoptyping

%D A few specific rendering variants:

% \def\doregisterpagelocation#1#2%
%   {\nextregisterpage
%    \hbox to 1em{\hss\doregisterpagehowto{#1}{#2}\hss}}

% todo: \installregisterpagehandler

% \def\MyRegisterPageCommand#1%
%   {#1\currentregisterpageuserdata{whatever}}
%
% \starttext
%     \setregisterentry[index][entries=aaa][whatever=f.] test \index{bbb} test
%     \placeregister[index][n=1,pagecommand=\MyRegisterPageCommand]
% \stoptext

\def\registerpageuserdata       #1#2{\clf_registeruserdata#1{#2}}
\def\currentregisterpageuserdata    {\registerpageuserdata\currentregisterpageindex} % {#1}

% not yet ok : new internal handler names

\unexpanded\def\registerpagebuttonsymbol
  {\vrule\s!width\emwidth\s!height\exheight\s!depth\zeropoint\relax}

\installcorenamespace{registersymbol}

\setvalue{\??registersymbol n}%
  {\def\registerpageseparatorsymbol{,\space}}

\setvalue{\??registersymbol a}%
  {\def\registerpageseparatorsymbol{,\space}} % now done via conversion

\setvalue{\??registersymbol\v!none}%
  {\let\registerpageseparatorsymbol\empty
   \let\registeronepage  \gobblefivearguments
   \let\registerpagerange\gobbleeightarguments}

\setvalue{\??registersymbol 1}%
  {\let\registerpageseparatorsymbol\space
   \def\registeronepage  {\symbol[1]\gobblefivearguments}%
   \def\registerpagerange{\symbol[1]\gobbleeightarguments}}

\setvalue{\??registersymbol 2}%
  {\let\registerpageseparatorsymbol\space
   \def\registeronepage  {\registerpagebuttonsymbol\gobblefivearguments}%
   \def\registerpagerange{\registerpagebuttonsymbol\gobbleeightarguments}}

\unexpanded\def\setregisterpagerendering
  {\doifelse{\registerparameter\c!pagenumber}\v!no
     {\let \currentregisterpagesymbol\v!none}
     {\edef\currentregisterpagesymbol{\registerparameter\c!symbol}}%
   \ifx\currentregisterpagesymbol\empty
     \csname\??registersymbol n\endcsname
   \else\ifcsname\??registersymbol\currentregisterpagesymbol\endcsname
     \csname\??registersymbol\currentregisterpagesymbol\endcsname
   \else
     \let\registerpageseparatorsymbol\space
     \def\registeronepage  {\registerparameter\c!symbol\gobblefivearguments}%
     \def\registerpagerange{\registerparameter\c!symbol\gobbleeightarguments}%
   \fi\fi}

\appendtoks
     \setregisterpagerendering
\to \everyplaceregister

%D The linked register code will be reimplemented (not that hard) when it's needed
%D again and/or when I'm bored.

           \def\findregisterinternal#1#2#3{\clf_findregisterinternal{#1}{#2}#3\relax}
\unexpanded\def\pageofinternal          #1{\clf_pageofinternal#1\relax}

\unexpanded\def\linkedregisterentrylink#1#2#3#4% tag where before after
  {\iflocation
     \scratchcounter\findregisterinternal{#1}{#2}\currentregisternumber\relax\relax
     \ifcase\scratchcounter\else
       #3\relax
       \goto{\symbol[#2]}[internal(\the\scratchcounter)]%
       #4\relax
     \fi
   \else
   % \scratchcounter\findregisterinternal{#1}{#2}\currentregisternumber\relax\relax
   % \ifcase\scratchcounter\else
   %   #3\relax
   %   \pageofinternal\scratchcounter
   %   #4\relax
   % \fi
   \fi}

\unexpanded\def\linkedregisterentry#1%
  {\dontleavehmode
   \begingroup
   \setbox\scratchbox\hbox{#1}%
   \linkedregisterentrylink\currentregistername\v!previous\relax\nobreakspace
   \unhbox\scratchbox
   \linkedregisterentrylink\currentregistername\v!next\nobreakspace\relax
   \endgroup}

\unexpanded\def\registerpacked#1#2%
  {\iflocation
     \hskip\d_strc_registers_distance\relax
     \nobreak
     \ifnum#1=#2\relax
       \goto{\symbol[\v!somewhere]}[internal(#1)]%
     \else
       \goto{\symbol[\v!first]}[internal(#1)]%
       \nobreakspace
       \goto{\symbol[\v!last]}[internal(#2)]%
     \fi
   \fi}

%D Default index:

\defineregister
  [\v!index]
%  [\v!indices]

\stopcontextdefinitioncode

\protect \endinput