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"
}

-- local nodecodes      = nodes.nodecodes
-- local whatsitcodes   = nodes.whatsitcodes
-- local glyph_code     = nodecodes.glyph
-- local whatsit_code   = nodecodes.whatsit
-- local user_code      = whatsitcodes.userdefined
--
-- local stringusernode = nodes.pool.userstring
--
-- local nuts           = nodes.nuts
-- local pool           = nuts.pool
--
-- local tonut          = nuts.tonut
-- local tonode         = nuts.tonode
-- local getid          = nuts.getid
-- local getprev        = nuts.getprev
-- local getsubtype     = nuts.getsubtype
-- local getchar        = nuts.getchar
-- local getfield       = nuts.getfield
--
-- local remove_node    = nuts.remove
-- local traverse_by_id = nuts.traverse_id
--
-- local signal         = pool.userids.signal
--
-- local is_punctuation = characters.is_punctuation
--
-- local actions = {
--     removepunctuation = function(head,n)
--         local prev = getprev(n)
--         if prev then
--             if getid(prev) == glyph_code then
--                 if is_punctuation[getchar(prev)] then
--                     head = remove_node(head,prev,true)
--                 end
--             end
--         end
--         return head
--     end
-- }
--
-- -- we can also use properties .. todo (saves pass)
--
-- typesetters.signals = { }
--
-- function typesetters.signals.handler(head)
--     local h = tonut(head)
--     local done = false
--     for n in traverse_by_id(whatsit_code,h) do
--         if getsubtype(n) == user_code and getfield(n,"user_id") == signal and getfield(n,"type") == 115 then
--             local action = actions[getfield(n,"value")]
--             if action then
--                 h = action(h,n)
--             end
--             h = remove_node(h,n,true)
--             done = true
--         end
--     end
--     if done then
--         return tonode(h), true
--     else
--         return head
--     end
-- end
--
-- local enabled = false
--
-- local function signal(what)
--     if not enabled then
--         nodes.tasks.prependaction("processors","normalizers", "typesetters.signals.handler")
--         enabled = true
--     end
--     context(stringusernode(signal,what))
-- end
--
-- interfaces.implement {
--     name      = "signal",
--     actions   = signal,
--     arguments = "string",
-- }

local insert, remove = table.insert, table.remove

local context         = context

local nodecodes       = nodes.nodecodes
local glyph_code      = nodecodes.glyph
local localpar_code   = nodecodes.localpar

local texnest         = tex.nest
local flush_node      = node.flush_node
local flush_list      = node.flush_list

local settexattribute = tex.setattribute
local punctuation     = characters.is_punctuation

local variables       = interfaces.variables
local v_all           = variables.all
local v_reset         = variables.reset

local a_marked        = attributes.numbers['marked']
local lastmarked      = 0
local marked          = {
    [v_all]   = 1,
    [""]      = 1,
    [v_reset] = attributes.unsetvalue,
}

local stack           = { }

local function pickup()
    local list = texnest[texnest.ptr]
    if list then
        local tail = list.tail
        if tail and tail.id == glyph_code and punctuation[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
            flush_node(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
                local id = prev.id
                if id == localpar_code then
                    break
                else
                    first = prev
                end
            else
                break
            end
        end
        return first, last
    end
end

local actions = {
    remove = function(specification)
        local list = texnest[texnest.ptr]
        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
                flush_list(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",
}
