Created
May 5, 2012 11:30
-
-
Save phi-gamma/2601712 to your computer and use it in GitHub Desktop.
Sort structure for tex.sx user Village (http://tex.stackexchange.com/questions/51967/how-to-alphabetize-all-of-the-parts-chapters-sections-and-content-of-an-entir)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\unprotect | |
\let\@EA\expandafter%% Precaution in case Hans removes it. | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% implementation | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
%%% We-F¢ll have a minimal setup macro as an interface. | |
\def\setupsortstructure{% | |
\dosingleempty\do_setup_sort_structure% | |
} | |
\def\do_setup_sort_structure[#1]{% | |
\doifassignmentelse{#1}{\getparameters[sortstructure][#1]}{}% | |
} | |
\def\register_structure_at_depth#1#2{% | |
\ctxlua{thirddata.sort_structure.register("#1", \number#2)}% | |
} | |
%%% Interception layer. This will define fake sectioning commands from | |
%%% the specification given in \setupsortstructure. | |
\def\intercept_structure{% | |
%%% The redefinition list is assumed to represent the actual nesting | |
%%% hierarchy. The counter \redefined_structure will be incremented | |
%%% for every item in the list, yielding the nesting depth. | |
\newcount\redefined_structures | |
%%% This macro will be used to iterate over the list of structure | |
%%% elements. | |
\def\redefine_structure_element##1{% | |
%%% Calculate current depth. | |
\advance\redefined_structures\plusone | |
\register_structure_at_depth{##1}{\the\redefined_structures}% | |
%%% We will keep the definition of the original sectioning macros | |
%%% intact, i.e. the optional first argument (a comma separated | |
%%% list) will be respected. To this end we define ordinary | |
%%% two-stage macros and store the arguments along with the actual | |
%%% heading text. | |
\@EA\def\csname##1\endcsname{% | |
\edef\current_depth{\the\redefined_structures}% | |
%%% 1stly define a wrapper so we can store the optional first arg. | |
\@EA\dosingleempty\csname do_##1\endcsname% | |
} | |
%%% The items we get will be passed through to the Lua end. | |
\@EA\def\csname do_##1\endcsname[####1]####2{ | |
\ctxlua{ | |
thirddata.sort_structure.collect( | |
\!!bs##1\!!es, %% type | |
\!!bs\detokenize{####2}\!!es, %% label | |
\!!bs####1\!!es %% args (optional) | |
) | |
} | |
}% | |
} | |
%%% Now the entry macro. Will be sorted as well but lacks both a | |
%%% definite depth and optional arguments (the latter could be | |
%%% implemented but that was not part of the question. | |
\def\define_entry_command{% | |
\def\entry####1{% | |
\ctxlua{ | |
thirddata.sort_structure.collect( | |
\!!bs entry\!!es, | |
\!!bs\detokenize{####1}\!!es | |
) | |
}% | |
}% | |
} | |
%%% Of course, at some point we¢ll have to revert the interception and | |
%%% reinstate the real commands. | |
\prependtoks | |
%%% End group with \stoptext. | |
\endgroup | |
%%% Now that we¢re done collecting, we can sort the items | |
%%% recursively and push the content according to the restructured | |
%%% order. | |
\ctxlua{thirddata.sort_structure.process_tree()} | |
\to \everystoptext% | |
%%% This will activat the interception layer. | |
\begingroup %%% The parallelism between TEX and Lua scoping is great. | |
\@EA\processcommalist\@EA[\sortstructuresections]% | |
\redefine_structure_element | |
} | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% Lua internals | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
\startluacode | |
--- Pro tip: uncomment the print statements to watch what¢s going on. | |
--- Namespacing adheres to the guidelines for modules. | |
thirddata = thirddata or { } | |
thirddata.sort_structure = { } | |
local sort = thirddata.sort_structure | |
sort.args = { } | |
sort.depth_to_id = { [0] = "root" } | |
sort.id_to_depth = { } | |
sort.labels = { } | |
--- Compare function. You will have to modify it to suit your needs. | |
local nodecmp = function (a, b) | |
return string.lower(a.label) < string.lower(b.label) | |
end | |
--- The tree will be sorted recursively. Once we arrive at max depth | |
--- we expect only nodes of type -Y´entry¡ to follow. | |
local do_process_tree do_process_tree = function (tree) | |
local depth, label, nodes = tree.depth, tree.label, tree.nodes | |
-- print(string.rep("*", depth)..">", "["..sort.depth_to_id[depth].."]", label) | |
if nodes then | |
table.sort(nodes, nodecmp) | |
for i=1, #nodes do | |
local node = nodes[i] | |
if node.id == "entry" then | |
-- print(string.format([=[\letterbackslash entry{%s}]=], node.label)) | |
context.sortstructureentrycommand(node.label) | |
else | |
context(string.format([=[\letterbackslash%s[%s]{%s}]=], | |
node.id, | |
node.args, | |
node.label)) | |
do_process_tree(node) | |
end | |
end | |
end | |
end | |
--- Wrapper for the sorting function. | |
sort.process_tree = function () | |
do_process_tree(sort.doctree) | |
end | |
--- This is called when the structure is defined initially. It maps | |
--- element names to their respective levels. | |
sort.register = function (id, depth) | |
-- print("Registering structure", id, "=>", depth) | |
sort.id_to_depth[id] = depth | |
sort.depth_to_id[depth] = id | |
end | |
--- Root node. Poor thing ain-F¢t got no parents. | |
sort.doctree = { | |
parent = false, | |
nodes = { }, | |
depth = 0, | |
label = "root", | |
} | |
local currentnode = sort.doctree | |
--- This function does the main work of the interception layer. It | |
--- ensures that entries are inserted in the correct depth. | |
sort.collect = function (id, label, args) | |
if id == "entry" then | |
local parent = currentnode.nodes[#currentnode.nodes] | |
-- print("Collecting entry", parent.id, parent.depth, label) | |
parent.nodes[#parent.nodes+1] = { | |
args = args, | |
depth = parent.depth + 1, | |
id = "entry", | |
label = label, | |
nodes = false, | |
} | |
return | |
end | |
local depth = sort.id_to_depth[id] | |
local indepth = sort.id_to_depth[id] - 1 | |
-- print("Collecting element", id, depth, label, args) | |
local newnode = { | |
args = args, --- <string> | |
depth = depth, --- <int> | |
id = id, --- <string> | |
label = label, --- <string> | |
nodes = { }, --- <node array> | |
} | |
if indepth > currentnode.depth then | |
local parent = currentnode.nodes[#currentnode.nodes] | |
newnode.parent = parent | |
parent.nodes[#parent.nodes+1] = newnode | |
currentnode = parent | |
elseif indepth == currentnode.depth then | |
local parent = currentnode | |
newnode.parent = parent | |
parent.nodes[#parent.nodes+1] = newnode | |
else -- less | |
local diff = currentnode.depth - indepth | |
for i=1, diff do | |
currentnode = currentnode.parent | |
end | |
local parent = currentnode | |
newnode.parent = parent | |
parent.nodes[#parent.nodes+1] = newnode | |
end | |
end | |
\stopluacode | |
%%% Insert the interception layer. The -Y´real¡ heading commands will be | |
%%% restored once we arrive at \stoptext. | |
\appendtoks | |
\intercept_structure | |
\define_entry_command | |
\to \everystarttext | |
\protect | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% setups | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
%%% Demo layout. | |
\setuppapersize[A6][A6] | |
\setuplayout [width=middle,height=middle,backspace=1cm,topspace=1cm,header=\zeropoint,footer=\zeropoint] | |
%%% Demo heading style. | |
\setuphead [part,chapter,section] [placehead=yes,page=no]%,number=no,align=middle] | |
\setuphead [part] [style=\WORD\tfa] | |
\setuphead [chapter] [style=\WORD\tf] | |
\setuphead [section] [style=\word\tf\sc] | |
%%% The macro for entries. | |
\definehighlight [entryhighlight] [style=italic] | |
\define[1]\entry{% | |
\leavevmode | |
\noindentation | |
\hbox to2em{\hfill-A·}\hskip.5em | |
\entryhighlight{#1}% | |
\blank[line]% | |
} | |
%%% Configuration. | |
\setupsortstructure[ | |
%%% The structure elements to intercept. | |
sections={part,chapter,section,subsection}, | |
%%% Hook your self-defined macro into this here. | |
entrycommand=\entry, | |
] | |
\starttext | |
%%% Here we are already using the fake commands ... | |
\part{Birds} | |
\chapter{North America} | |
\section{West Coast} | |
\entry{Bald Eagle} | |
\entry{Spotted Owl} | |
\section{East Coast} | |
\section{Alaska} | |
\chapter{South America} | |
\section{Peru} | |
\section{Brazil} | |
\part{Bears} | |
\chapter{Canada} | |
\chapter{Greenland} | |
%%% Only now will the actual typesetting take place. | |
\stoptext | |
\endinput |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment