summaryrefslogtreecommitdiff
path: root/tex/context/base/strc-bkm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/strc-bkm.lua')
-rw-r--r--tex/context/base/strc-bkm.lua427
1 files changed, 337 insertions, 90 deletions
diff --git a/tex/context/base/strc-bkm.lua b/tex/context/base/strc-bkm.lua
index c38ab3c2e..96b68b236 100644
--- a/tex/context/base/strc-bkm.lua
+++ b/tex/context/base/strc-bkm.lua
@@ -13,14 +13,15 @@ if not modules then modules = { } end modules ['strc-bkm'] = {
-- we should hook the placement into everystoptext ... needs checking
-local format, concat, gsub = string.format, table.concat, string.gsub
+-- todo: make an lpeg for stripped
+
+local next, type = next, type
+local gsub, lower = string.gsub, string.lower
+local concat = table.concat
local utfvalues = utf.values
local settings_to_hash = utilities.parsers.settings_to_hash
-local codeinjections = backends.codeinjections
-
-local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end)
-
+local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end)
local report_bookmarks = logs.reporter("structure","bookmarks")
local structures = structures
@@ -30,13 +31,17 @@ structures.bookmarks = structures.bookmarks or { }
local bookmarks = structures.bookmarks
local sections = structures.sections
local lists = structures.lists
-
local levelmap = sections.levelmap
local variables = interfaces.variables
+local implement = interfaces.implement
+local codeinjections = backends.codeinjections
-bookmarks.method = "internal" -- or "page"
+bookmarks.method = "internal" -- or "page"
-local names, opened, forced, numbered = { }, { }, { }, { }
+local names = { }
+local opened = { }
+local forced = { }
+local numbered = { }
function bookmarks.register(settings)
local force = settings.force == variables.yes
@@ -78,8 +83,13 @@ function bookmarks.overload(name,text)
end
end
if ls then
- ls.titledata.bookmark = text
+ local titledata = ls.titledata
+ if titledata then
+ titledata.bookmark = text
+ end
end
+ -- last resort
+ -- context.writetolist({name},text,"")
end
local function stripped(str) -- kind of generic
@@ -101,54 +111,6 @@ function bookmarks.setup(spec)
end
end
--- function bookmarks.place()
--- if next(names) then
--- local list = lists.filtercollected(names,"all",nil,lists.collected,forced)
--- if #list > 0 then
--- local levels, noflevels, lastlevel = { }, 0, 1
--- for i=1,#list do
--- local li = list[i]
--- local metadata = li.metadata
--- local name = metadata.name
--- if not metadata.nolist or forced[name] then -- and levelmap[name] then
--- local titledata = li.titledata
--- if titledata then
--- local structural = levelmap[name]
--- lastlevel = structural or lastlevel
--- local title = titledata.bookmark
--- if not title or title == "" then
--- -- We could typeset the title and then convert it.
--- if not structural then
--- -- placeholder, todo: bookmarklabel
--- title = name .. ": " .. (titledata.title or "?")
--- else
--- title = titledata.title or "?"
--- end
--- end
--- if numbered[name] then
--- local sectiondata = sections.collected[li.references.section]
--- local numberdata = li.numberdata
--- if sectiondata and numberdata and not numberdata.hidenumber then
--- -- we could typeset the number and convert it
--- title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title
--- end
--- end
--- noflevels = noflevels + 1
--- levels[noflevels] = {
--- lastlevel,
--- stripped(title), -- can be replaced by converter
--- li.references, -- has internal and realpage
--- allopen or opened[name]
--- }
--- end
--- end
--- end
--- bookmarks.finalize(levels)
--- end
--- function bookmarks.place() end -- prevent second run
--- end
--- end
-
function bookmarks.place()
if next(names) then
local levels = { }
@@ -157,26 +119,53 @@ function bookmarks.place()
local nofblocks = #lists.sectionblocks -- always >= 1
local showblocktitle = toboolean(numberspec.showblocktitle,true)
for i=1,nofblocks do
- local block = lists.sectionblocks[i]
+ local block = lists.sectionblocks[i]
local blockdone = nofblocks == 1
- local list = lists.filtercollected(names,block..":all",nil,lists.collected,forced)
+ local list = lists.filter {
+ names = names,
+ criterium = block .. ":all",
+ forced = forced,
+ }
for i=1,#list do
local li = list[i]
local metadata = li.metadata
local name = metadata.name
if not metadata.nolist or forced[name] then -- and levelmap[name] then
local titledata = li.titledata
+ --
+ if not titledata then
+ local userdata = li.userdata
+ if userdata then
+ local first = userdata.first
+ local second = userdata.second
+ if first then
+ if second then
+ titledata = { title = first .. " " .. second }
+ else
+ titledata = { title = first }
+ end
+ elseif second then
+ titledata = { title = second }
+ else
+ -- ignoring (command and so)
+ end
+ end
+ end
+ --
if titledata then
if not blockdone then
if showblocktitle then
-- add block entry
local blockdata = sections.sectionblockdata[block]
noflevels = noflevels + 1
+ local references = li.references
levels[noflevels] = {
- 1, -- toplevel
- stripped(blockdata.bookmark ~= "" and blockdata.bookmark or block),
- li.references,
- allopen or opened[name] -- same as first entry
+ level = 1, -- toplevel
+ title = stripped(blockdata.bookmark ~= "" and blockdata.bookmark or block),
+ reference = references,
+ opened = allopen or opened[name], -- same as first entry
+ realpage = references and references.realpage or 0, -- handy for later
+ usedpage = true,
}
end
blockdone = true
@@ -190,27 +179,36 @@ function bookmarks.place()
local title = titledata.bookmark
if not title or title == "" then
-- We could typeset the title and then convert it.
- if not structural then
- -- placeholder, todo: bookmarklabel
- title = name .. ": " .. (titledata.title or "?")
- else
+ -- if not structural then
+ -- title = titledata.title or "?")
+ -- else
title = titledata.title or "?"
- end
+ -- end
end
if numbered[name] then
local sectiondata = sections.collected[li.references.section]
local numberdata = li.numberdata
- if sectiondata and numberdata and not numberdata.hidenumber then
+ if sectiondata and numberdata then
+ if not numberdata.hidenumber then
-- we could typeset the number and convert it
- title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title
+ local number = sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)
+ if number and #number > 0 then
+ title = concat(number) .. " " .. title
+ end
+ end
end
end
noflevels = noflevels + 1
+ local references = li.references
levels[noflevels] = {
- lastlevel,
- stripped(title), -- can be replaced by converter
- li.references, -- has internal and realpage
- allopen or opened[name]
+ level = lastlevel,
+ title = stripped(title), -- can be replaced by converter
+ reference = references, -- has internal and realpage
+ opened = allopen or opened[name],
+ realpage = references and references.realpage or 0, -- handy for later
+ usedpage = true,
+ structural = structural,
+ name = name,
}
end
end
@@ -222,47 +220,296 @@ function bookmarks.place()
end
function bookmarks.flatten(levels)
+ if not levels then
+ -- a plugin messed up
+ return { }
+ end
-- This function promotes leading structurelements with a higher level
-- to the next lower level. Such situations are the result of lack of
-- structure: a subject preceding a chapter in a sectionblock. So, the
-- following code runs over section blocks as well. (bookmarks-007.tex)
local noflevels = #levels
if noflevels > 1 then
- local skip, start, one = false, 1, levels[1]
- local first, block = one[1], one[3].block
+ local function showthem()
+ for i=1,noflevels do
+ local level = levels[i]
+ -- if level.structural then
+ -- report_bookmarks("%i > %s > %s",level.level,level.reference.block,level.title)
+ -- else
+ report_bookmarks("%i > %s > %s > %s",level.level,level.reference.block,level.name,level.title)
+ -- end
+ end
+ end
+ if trace_bookmarks then
+ report_bookmarks("checking structure")
+ showthem()
+ end
+ local skip = false
+ local done = 0
+ local start = 1
+ local one = levels[1]
+ local first = one.level
+ local block = one.reference.block
for i=2,noflevels do
- local li = levels[i]
- local new, newblock = li[1], li[3].block
+ local current = levels[i]
+ local new = current.level
+ local reference = current.reference
+ local newblock = type(reference) == "table" and current.reference.block or block
if newblock ~= block then
- first, block, start, skip = new, newblock, i, false
+ first = new
+ block = newblock
+ start = i
+ skip = false
elseif skip then
-- go on
elseif new > first then
skip = true
elseif new < first then
for j=start,i-1 do
- local lj = levels[j]
- local old = lj[1]
- lj[1] = new
+ local previous = levels[j]
+ local old = previous.level
+ previous.level = new
if trace_bookmarks then
- report_bookmarks("promoting entry %a from level %a to %a: %s",j,old,new,lj[2])
+ report_bookmarks("promoting entry %a from level %a to %a: %s",j,old,new,previous.title)
end
+ done = done + 1
end
skip = true
end
end
+ if trace_bookmarks then
+ if done > 0 then
+ report_bookmarks("%a entries promoted")
+ showthem()
+ else
+ report_bookmarks("nothing promoted")
+ end
+ end
+ end
+ return levels
+end
+
+local extras = { }
+local lists = { }
+local names = { }
+
+bookmarks.extras = extras
+
+local function cleanname(name)
+ return lower(file.basename(name))
+end
+
+function extras.register(name,levels)
+ if name and levels then
+ name = cleanname(name)
+ local found = names[name]
+ if found then
+ lists[found].levels = levels
+ else
+ lists[#lists+1] = {
+ name = name,
+ levels = levels,
+ }
+ names[name] = #lists
+ end
+ end
+end
+
+function extras.get(name)
+ if name then
+ local found = names[cleanname(name)]
+ if found then
+ return lists[found].levels
+ end
+ else
+ return lists
+ end
+end
+
+function extras.reset(name)
+ local l, n = { }, { }
+ if name then
+ name = cleanname(name)
+ for i=1,#lists do
+ local li = lists[i]
+ local ln = li.name
+ if name == ln then
+ -- skip
+ else
+ local m = #l + 1
+ l[m] = li
+ n[ln] = m
+ end
+ end
+ end
+ lists, names = l, n
+end
+
+local function checklists()
+ for i=1,#lists do
+ local levels = lists[i].levels
+ for j=1,#levels do
+ local entry = levels[j]
+ local pageindex = entry.pageindex
+ if pageindex then
+ entry.reference = figures.getrealpage(pageindex)
+ entry.pageindex = nil
+ end
+ end
end
end
+function extras.tosections(levels)
+ local sections = { }
+ local noflists = #lists
+ for i=1,noflists do
+ local levels = lists[i].levels
+ local data = { }
+ sections[i] = data
+ for j=1,#levels do
+ local entry = levels[j]
+ if entry.usedpage then
+ local section = entry.section
+ local d = data[section]
+ if d then
+ d[#d+1] = entry
+ else
+ data[section] = { entry }
+ end
+ end
+ end
+ end
+ return sections
+end
+
+function extras.mergesections(levels,sections)
+ if not sections or #sections == 0 then
+ return levels
+ elseif not levels then
+ return { }
+ else
+ local merge = { }
+ local noflists = #lists
+ if #levels == 0 then
+ local level = 0
+ local section = 0
+ for i=1,noflists do
+ local entries = sections[i][0]
+ if entries then
+ for i=1,#entries do
+ local entry = entries[i]
+ merge[#merge+1] = entry
+ entry.level = entry.level + level
+ end
+ end
+ end
+ else
+ for j=1,#levels do
+ local entry = levels[j]
+ merge[#merge+1] = entry
+ local section = entry.reference.section
+ local level = entry.level
+ entry.section = section -- for tracing
+ for i=1,noflists do
+ local entries = sections[i][section]
+ if entries then
+ for i=1,#entries do
+ local entry = entries[i]
+ merge[#merge+1] = entry
+ entry.level = entry.level + level
+ end
+ end
+ end
+ end
+ end
+ return merge
+ end
+end
+
+function bookmarks.merge(levels,mode)
+ return extras.mergesections(levels,extras.tosections())
+end
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+local bookmarkactions = sequencers.new {
+ arguments = "levels,method",
+ returnvalues = "levels",
+ results = "levels",
+}
+
+appendgroup(bookmarkactions,"before") -- user
+appendgroup(bookmarkactions,"system") -- private
+appendgroup(bookmarkactions,"after" ) -- user
+
+appendaction(bookmarkactions,"system",bookmarks.flatten)
+appendaction(bookmarkactions,"system",bookmarks.merge)
+
function bookmarks.finalize(levels)
- -- This function can be overloaded by an optional converter
- -- that uses nodes.toutf on a typeset stream. This is something
- -- that we will support when the main loop has become a coroutine.
- codeinjections.addbookmarks(levels,bookmarks.method)
+ local method = bookmarks.method or "internal"
+ checklists() -- so that plugins have the adapted page number
+ levels = bookmarkactions.runner(levels,method)
+ if levels and #levels > 0 then
+ -- normally this is not needed
+ local purged = { }
+ for i=1,#levels do
+ local l = levels[i]
+ if l.usedpage ~= false then
+ purged[#purged+1] = l
+ end
+ end
+ --
+ codeinjections.addbookmarks(purged,method)
+ else
+ -- maybe a plugin messed up
+ end
+end
+
+function bookmarks.installhandler(what,where,func)
+ if not func then
+ where, func = "after", where
+ end
+ if where == "before" or where == "after" then
+ sequencers.appendaction(bookmarkactions,where,func)
+ else
+ report_tex("installing bookmark %a handlers in %a is not possible",what,tostring(where))
+ end
end
-- interface
-commands.overloadbookmark = bookmarks.overload
-commands.registerbookmark = bookmarks.register
-commands.setupbookmarks = bookmarks.setup
+implement {
+ name = "setupbookmarks",
+ actions = bookmarks.setup,
+ arguments = {
+ {
+ { "separatorset" },
+ { "conversionset" },
+ { "starter" },
+ { "stopper" },
+ { "segments" },
+ { "showblocktitle" },
+ }
+ }
+}
+
+implement {
+ name = "registerbookmark",
+ actions = bookmarks.register,
+ arguments = {
+ {
+ { "names" },
+ { "opened" },
+ { "force" },
+ { "number" },
+ }
+ }
+}
+
+implement {
+ name = "overloadbookmark",
+ actions = bookmarks.overload,
+ arguments = { "string", "string" }
+}