if not modules then modules = { } end modules ['typo-chr'] = { version = 1.001, comment = "companion to typo-bld.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- A historic intermediate version can be found in the mkiv variant. The code -- can probably be improved but performance etc is not really an issue here. local insert, remove = table.insert, table.remove local context = context local ctx_doifelse = commands.doifelse local nodecodes = nodes.nodecodes local boundarycodes = nodes.boundarycodes local subtypes = nodes.subtypes local glyph_code = nodecodes.glyph local par_code = nodecodes.par local boundary_code = nodecodes.boundary local wordboundary_code = boundarycodes.word local texgetnest = tex.getnest -- to be used local texsetcount = tex.setcount local flushnode = nodes.flushnode local flushlist = nodes.flushlist local settexattribute = tex.setattribute local ispunctuation = characters.is_punctuation local variables = interfaces.variables local v_all = variables.all local v_reset = variables.reset local stack = { } local a_marked = attributes.numbers['marked'] local lastmarked = 0 local marked = { [v_all] = 1, [""] = 1, [v_reset] = attributes.unsetvalue, } local function pickup() local list = texgetnest() if list then local tail = list.tail if tail and tail.id == glyph_code and ispunctuation[tail.char] then local prev = tail.prev list.tail = prev if prev then prev.next = nil end list.tail = prev tail.prev = nil return tail end end end local actions = { remove = function(specification) local n = pickup() if n then flushnode(n) end end, push = function(specification) local n = pickup() if n then insert(stack,n or false) end end, pop = function(specification) local n = remove(stack) if n then context(n) end end, } local function pickuppunctuation(specification) local action = actions[specification.action or "remove"] if action then action(specification) end end -- I played with nested marked content but it makes no sense and gives -- complex code. Also, it's never needed so why bother. local function pickup(head,tail,str) local attr = marked[str] local last = tail if last[a_marked] == attr then local first = last while true do local prev = first.prev if prev and prev[a_marked] == attr then if prev.id == par_code then -- and startofpar(prev) break else first = prev end else break end end return first, last end end local function found(str) local list = texgetnest() if list then local tail = list.tail return tail and tail[a_marked] == marked[str] end end local actions = { remove = function(specification) local list = texgetnest() if list then local head = list.head local tail = list.tail local first, last = pickup(head,tail,specification.mark) if first then if first == head then list.head = nil list.tail = nil else local prev = first.prev list.tail = prev prev.next = nil end flushlist(first) end end end, } local function pickupmarkedcontent(specification) local action = actions[specification.action or "remove"] if action then action(specification) end end local function markcontent(str) local currentmarked = marked[str or v_all] if not currentmarked then lastmarked = lastmarked + 1 currentmarked = lastmarked marked[str] = currentmarked end settexattribute(a_marked,currentmarked) end interfaces.implement { name = "pickuppunctuation", actions = pickuppunctuation, arguments = { { { "action" } } } } interfaces.implement { name = "pickupmarkedcontent", actions = pickupmarkedcontent, arguments = { { { "action" }, { "mark" } } } } interfaces.implement { name = "markcontent", actions = markcontent, arguments = "string", } interfaces.implement { name = "doifelsemarkedcontent", actions = function(str) ctx_doifelse(found(str)) end, arguments = "string", } -- We just put these here. interfaces.implement { name = "lastnodeidstring", public = true, actions = function() local list = texgetnest() -- "top" local okay = false if list then local tail = list.tail if tail then okay = nodecodes[tail.id] end end context(okay or "") end, } -- local t_lastnodeid = token.create("c_syst_last_node_id") -- -- interfaces.implement { -- name = "lastnodeid", -- public = true, -- actions = function() -- ... -- tex.setcount("c_syst_last_node_id",okay) -- context.sprint(t_lastnodeid) -- end, -- } -- not needed in lmtx ... local c_syst_last_node_id = tex.iscount("c_syst_last_node_id") interfaces.implement { name = "lastnodeid", actions = function() local list = texgetnest() -- "top" local okay = -1 if list then local tail = list.tail if tail then okay = tail.id end end texsetcount(c_syst_last_node_id,okay) end, } interfaces.implement { name = "lastnodesubtypestring", public = true, actions = function() local list = texgetnest() -- "top" local okay = false if list then local tail = list.tail if head then okay = subtypes[tail.id][tail.subtype] end end context(okay or "") end, } local function lastnodeequals(id,subtype) local list = texgetnest() -- "top" local okay = false if list then local tail = list.tail if tail then local i = tail.id okay = i == id or i == nodecodes[id] if subtype then local s = tail.subtype okay = s == subtype or s == subtypes[i][subtype] end end end ctx_doifelse(okay) end interfaces.implement { name = "lastnodeequals", arguments = "2 strings", actions = lastnodeequals, } interfaces.implement { name = "atwordboundary", actions = function() lastnodeequals(boundary_code,wordboundary_code) end, }