From f628db9e7ee27c0595ef53e53a8681e4f4a393fc Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Mon, 10 Jul 2023 02:22:17 +0200 Subject: [PATCH] refactor: Styles should not invoke functions but use AST constructs For case and sub/superscript to work better recursing the AST. That's quite a change in coding paradigm... See also https://github.com/Omikhleia/markdown.sile/pull/80 --- classes/resilient/book.lua | 54 ++----- classes/resilient/resume.lua | 133 +++++++--------- packages/resilient/abbr/init.lua | 12 +- packages/resilient/epigraph/init.lua | 2 +- packages/resilient/poetry/init.lua | 4 +- packages/resilient/sectioning/init.lua | 167 +++++++++++--------- packages/resilient/styles/init.lua | 98 ++++++------ packages/resilient/tableofcontents/init.lua | 92 ++++++----- 8 files changed, 277 insertions(+), 285 deletions(-) diff --git a/classes/resilient/book.lua b/classes/resilient/book.lua index 6f3568c..7bafc01 100644 --- a/classes/resilient/book.lua +++ b/classes/resilient/book.lua @@ -65,16 +65,16 @@ function class:_init (options) -- Package "folio" is loaded by the plain class. self:registerCommand("foliostyle", function (_, content) local styleName = SILE.documentState.documentClass:oddPage() and "folio-odd" or "folio-even" - SILE.call("style:apply:paragraph", { name = styleName }, function () + SILE.call("style:apply:paragraph", { name = styleName }, { -- Ensure proper baseline alignment with a strut rule. -- The baseline placement depends on the line output algorithm, and we cannot -- trust it if it just uses the line ascenders. -- Typically, if folios use "old-style" numbers, 16 and 17 facing pages shall have -- aligned folios, but the 1 is smaller than the 6 and 7, the former ascends above, -- and the latter descends below the baseline). - SILE.call("strut", { method = "rule"}) - SILE.process(content) - end) + utils.createCommand("strut", { method = "rule"}), + utils.subTreeContent(content) + }) end) -- Override the standard urlstyle hook to rely on styles @@ -374,24 +374,6 @@ function class:declareSettings () }) end -function class:runningHeaderSectionReference (options, content) - if SU.boolean(options.numbering, true) then - local sty = self:resolveStyle(options.style) - local numsty = sty.sectioning and sty.sectioning.numberstyle - and sty.sectioning.numberstyle.header - if numsty and sty.sectioning.counter.id then - local number = self.packages.counters:formatMultilevelCounter( - self:getMultilevelCounter(sty.sectioning.counter.id), { - noleadingzeros = true, - level = sty.sectioning.counter.level -- up to the sectioning level - } - ) - SILE.call("style:apply:number", { name = numsty, text = number }) - end - end - SILE.process(content) -end - function class:registerCommands () base.registerCommands(self) @@ -401,10 +383,10 @@ function class:registerCommands () local closure = SILE.settings:wrap() SILE.scratch.headers.even = function () closure(function () - SILE.call("style:apply:paragraph", { name = "header-even" }, function () - SILE.call("strut", { method = "rule"}) - SILE.process(content) - end) + SILE.call("style:apply:paragraph", { name = "header-even" }, { + utils.createCommand("strut", { method = "rule"}), + utils.subTreeContent(content) + }) end) end end, "Text to appear on the top of the even page(s).") @@ -413,10 +395,10 @@ function class:registerCommands () local closure = SILE.settings:wrap() SILE.scratch.headers.odd = function () closure(function () - SILE.call("style:apply:paragraph", { name = "header-odd" }, function () - SILE.call("strut", { method = "rule"}) - SILE.process(content) - end) + SILE.call("style:apply:paragraph", { name = "header-odd" }, { + utils.createCommand("strut", { method = "rule"}), + utils.subTreeContent(content) + }) end) end end, "Text to appear on the top of the odd page(s).") @@ -435,23 +417,19 @@ function class:registerCommands () SILE.call("set-multilevel-counter", { id = "sections", level = 1, value = 0 }) end, "Apply part hooks (counter resets, footers and headers, etc.)") - self:registerCommand("sectioning:chapter:hook", function (options, content) + self:registerCommand("sectioning:chapter:hook", function (_, content) -- Chapters re-enable folios, have no header, and reset the footnote counter. SILE.call("noheaderthispage") SILE.call("folios") SILE.call("set-counter", { id = "footnote", value = 1 }) -- Chapters, here, go in the even header. - SILE.call("even-running-header", {}, function () - self:runningHeaderSectionReference(options, content) - end) + SILE.call("even-running-header", {}, content) end, "Apply chapter hooks (counter resets, footers and headers, etc.)") - self:registerCommand("sectioning:section:hook", function (options, content) + self:registerCommand("sectioning:section:hook", function (_, content) -- Sections, here, go in the odd header. - SILE.call("odd-running-header", {}, function () - self:runningHeaderSectionReference(options, content) - end) + SILE.call("odd-running-header", {}, content) end, "Applies section hooks (footers and headers, etc.)") self:registerCommand("part", function (options, content) diff --git a/classes/resilient/resume.lua b/classes/resilient/resume.lua index b179523..3e5cc4d 100644 --- a/classes/resilient/resume.lua +++ b/classes/resilient/resume.lua @@ -10,6 +10,7 @@ local class = pl.class(base) class._name = "resilient.resume" local utils = require("resilient.utils") +local C = utils.createStructuredCommand SILE.scratch.resilient = SILE.scratch.resilient or {} SILE.scratch.resilient.resume = SILE.scratch.resilient.resume or {} @@ -117,12 +118,12 @@ function class:_init (options) self:registerCommand("foliostyle", function (_, content) SILE.call("hbox", {}, {}) -- for vfill to be effective SILE.call("vfill") - SILE.call("rightalign", {}, function() - SILE.process(content) - SILE.typesetter:typeset("/") - SILE.call("pageref", { marker = "resilient.resume:end" }) - end) - SILE.call("eject") -- for vfill to be effective + SILE.call("rightalign", {}, { + content, + "/", + C("pageref", { marker = "resilient.resume:end" }) + }) + SILE.call("eject") -- for vfill to be effective end) -- override default document.parindent, we do not want it. @@ -230,27 +231,23 @@ end -- RESUME PROCESSING -local C = utils.createStructuredCommand - local function doEntry (rows, _, content) local topic = utils.extractFromTree(content, "topic") local description = utils.extractFromTree(content, "description") - local titleRow = C("row", { }, { - C("cell", { valign = "top", padding = "4pt 4pt 0 4pt" }, { function () - SILE.call("style:apply:paragraph", { name = "resume-topic" }, function () - -- We are typesetting in a different style but want proper alignment - -- With the other style, so strut tweaking: - SILE.call("style:apply", { name = "resume-description" }, function () - SILE.call("strut") - end) - -- Then go ahead. - SILE.process(topic) - end) - end + local titleRow = C("row", {}, { + C("cell", { valign = "top", padding = "4pt 4pt 0 4pt" }, { + -- We are typesetting in a different style but want proper alignment + -- With the other style, so strut tweaking: + C("style:apply:paragraph", { name = "resume-topic" }, { + C("style:apply", { name = "resume-description" }, { + C("strut"), + }), + -- Then go ahead. + utils.subTreeContent(topic) + }) }), - C("cell", { valign = "top", span = 2, padding = "4pt 4pt 0.33cm 0" }, { function () - SILE.call("style:apply", { name = "resume-description" }, description) - end + C("cell", { valign = "top", span = 2, padding = "4pt 4pt 0.33cm 0" }, { + C("style:apply", { name = "resume-description" }, description) }) }) for i = 0, #content do @@ -263,16 +260,14 @@ end local doSection = function (rows, _, content) local title = utils.extractFromTree(content, "title") - local titleRow = C("row", { }, { - C("cell", { valign = "bottom", padding = "4pt 4pt 0 4pt" }, { function () - SILE.call("style:apply", { name = "resume-section" }, function () - SILE.call("hrule", { width = "100%fw", height= "1ex" }) - end) - end + local titleRow = C("row", {}, { + C("cell", { valign = "bottom", padding = "4pt 4pt 0 4pt" }, { + C("style:apply", { name = "resume-section" }, { + C("hrule", { width = "100%fw", height= "1ex" }) + }) }), - C("cell", { span = 2, padding = "4pt 4pt 0.33cm 0" }, { function () - SILE.call("style:apply", { name = "resume-section" }, title) - end + C("cell", { span = 2, padding = "4pt 4pt 0.33cm 0" }, { + C("style:apply", { name = "resume-section" }, title) }) }) table.insert(rows, titleRow) @@ -304,16 +299,14 @@ function class:registerCommands () local jobtitle = utils.extractFromTree(content, "jobtitle") or SU.error("jobtitle is mandatory") local headline = utils.extractFromTree(content, "headline") -- can be omitted - SILE.call("cv-footer", {}, function() - SILE.process({ contact }) - end) - SILE.call("cv-header", {}, function () - SILE.call("style:apply:paragraph", { name = "resume-header" }, function () - SILE.call("style:apply", { name = "resume-firstname" }, firstname) - SILE.typesetter:typeset(" ") - SILE.call("style:apply", { name = "resume-lastname" }, lastname) - end) - end) + SILE.call("cv-footer", {}, { contact }) + SILE.call("cv-header", {}, { + C("style:apply:paragraph", { name = "resume-header" }, { + C("style:apply", { name = "resume-firstname" }, { utils.subTreeContent(firstname) }), + " ", + C("style:apply", { name = "resume-lastname" }, { utils.subTreeContent(lastname) }) + }) + }) local rows = {} @@ -325,28 +318,26 @@ function class:registerCommands () end) end }), - C("cell", { span = 2, border = "0 1pt 0 0", padding = "4pt 2pt 4pt 0", valign = "bottom" }, { function () - SILE.call("style:apply:paragraph", { name = "resume-fullname" }, function () - SILE.call("style:apply", { name = "resume-firstname" }, firstname) - SILE.typesetter:typeset(" ") - SILE.call("style:apply", { name = "resume-lastname" }, lastname) - end) - end + C("cell", { span = 2, border = "0 1pt 0 0", padding = "4pt 2pt 4pt 0", valign = "bottom" }, { + C("style:apply:paragraph", { name = "resume-fullname" }, { + C("style:apply", { name = "resume-firstname" }, { utils.subTreeContent(firstname) }), + " ", + C("style:apply", { name = "resume-lastname" }, { utils.subTreeContent(lastname) }) + }) }) }) table.insert(rows, fullnameAndPictureRow) - local jobtitleRow = C("row", { }, { - C("cell", { span = 3 }, { function () - SILE.call("style:apply:paragraph", { name = "resume-jobtitle" }, jobtitle) - end + local jobtitleRow = C("row", {}, { + C("cell", { span = 3 }, { + C("style:apply:paragraph", { name = "resume-jobtitle" }, jobtitle) }) }) table.insert(rows, jobtitleRow) -- NOTE: if headline is absent, no problem. We still insert a row, just for -- vertical spacing. - local headlineRow = C("row", { }, { + local headlineRow = C("row", {}, { C("cell", { span = 3 }, { function () SILE.call("center", {}, function () SILE.call("parbox", { width = "80%fw" }, function() @@ -390,16 +381,12 @@ function class:registerCommands () self:registerCommand("ranking", function (options, _) local value = SU.cast("integer", options.value or 0) local scale = SU.cast("integer", options.scale or 5) - SILE.call("style:apply", { name = "resume-dingbats" }, function () - for _ = 1, value do - SILE.typesetter:typeset(charFromUnicode("U+25CF")) - SILE.call("kern", { width = "0.1em" }) - end - for _ = value + 1, scale do - SILE.typesetter:typeset(charFromUnicode("U+25CB")) - SILE.call("kern", { width = "0.1em" }) - end - end) + local rank = {} + for i = 1, scale do + rank[#rank + 1] = i <= value and charFromUnicode("U+25CF") or charFromUnicode("U+25CB") + rank[#rank + 1] = utils.createCommand("kern", { width = "0.1em" }) + end + SILE.call("style:apply", { name = "resume-dingbats" }, rank) end) self:registerCommand("cv-bullet", function (_, _) @@ -419,15 +406,15 @@ function class:registerCommands () local phone = SILE.inputter:findInTree(content, "phone") or SU.error("phone is mandatory") local email = SILE.inputter:findInTree(content, "email") or SU.error("email is mandatory") - SILE.call("style:apply:paragraph", { name = "resume-contact" }, function () - SILE.call("cv-icon-text", { symbol="U+1F4CD" }, street) - SILE.call("cv-bullet") - SILE.process(city) - SILE.call("par") - SILE.process({ phone }) - SILE.call("cv-bullet") - SILE.process({ email }) - end) + SILE.call("style:apply:paragraph", { name = "resume-contact" }, { + C("cv-icon-text", { symbol="U+1F4CD" }, { utils.subTreeContent(street) }), + C("cv-bullet"), + utils.subTreeContent(city), + C("par"), + phone, + C("cv-bullet"), + email + }) end) self:registerCommand("cv-icon-text", function (options, content) diff --git a/packages/resilient/abbr/init.lua b/packages/resilient/abbr/init.lua index c37d997..9375c91 100644 --- a/packages/resilient/abbr/init.lua +++ b/packages/resilient/abbr/init.lua @@ -82,14 +82,10 @@ function package:registerCommands () SILE.process(content) if SU.boolean(options.sq) then -- Latin sequiturque ("and next page") - SILE.call("font", { style = "italic", language = "und" }, function () - SILE.typesetter:typeset(" sq.") - end) + SILE.call("font", { style = "italic", language = "und" }, { " sq." }) elseif SU.boolean(options.sqq) then -- Latin sequiturque, plural ("and following pages") - SILE.call("font", { style = "italic", language = "und" }, function () - SILE.typesetter:typeset(" sqq.") - end) + SILE.call("font", { style = "italic", language = "und" }, { " sqq." }) elseif SU.boolean(options.suiv) then -- French ("et suivant") for those finding the latin sequiturque pedantic -- as Lacroux in his Orthotypographie book.. @@ -108,9 +104,7 @@ function package:registerCommands () elseif century:match("^[ivx]+$") == nil then SU.error("Not a valid century '"..century.. "' in abbr:siecle") end - SILE.call("font", { features = "+smcp" }, function () - SILE.typesetter:typeset(century) - end) + SILE.call("font", { features = "+smcp" }, { century }) if century == "i" then SILE.call("textsuperscript", {}, { "er" }) else diff --git a/packages/resilient/epigraph/init.lua b/packages/resilient/epigraph/init.lua index f7fc471..e48ab82 100644 --- a/packages/resilient/epigraph/init.lua +++ b/packages/resilient/epigraph/init.lua @@ -87,7 +87,7 @@ function package:registerCommands () -- HACK. Oh my. When left-aligned, the rule is seen as longer than The -- line and a new line is inserted. Tweaking it by 0.05pt seems to avoid -- it. Rounding issue somewhere? I feel tired. - SILE.call("hrule", {width = epigraphw - 0.05, height = rule }) + SILE.call("hrule", { width = epigraphw - 0.05, height = rule }) end) end if source then diff --git a/packages/resilient/poetry/init.lua b/packages/resilient/poetry/init.lua index f0a5d6b..d95d199 100644 --- a/packages/resilient/poetry/init.lua +++ b/packages/resilient/poetry/init.lua @@ -122,9 +122,10 @@ function package:registerCommands () self:registerCommand("resilient.poetry:prosody", function (options, content) local prosodyBox - local vadjust + local vadjust -- FIXME whhy in style without absolute? SILE.call("style:apply", { name = "poetry-prosody" }, function () + -- FIXME bad style design vadjust = options.lower or SILE.measurement() prosodyBox = SILE.call("hbox", {}, function() SILE.typesetter:typeset(options.name) @@ -235,6 +236,7 @@ function package:registerCommands () local setback = SILE.length("1.75em"):absolute() SILE.settings:temporarily(function () SILE.call("style:apply", { name = "poetry-verseno"}, function () + -- FIXME: bad style and hbox design local w = SILE.length("6em"):absolute() local h = SILE.call("hbox", {}, { mark }) diff --git a/packages/resilient/sectioning/init.lua b/packages/resilient/sectioning/init.lua index 3fdb94d..acbc745 100644 --- a/packages/resilient/sectioning/init.lua +++ b/packages/resilient/sectioning/init.lua @@ -38,28 +38,28 @@ function package:registerCommands () local resolveSectionStyleDef = function (name) local styledef = self:resolveStyle(name) if styledef.sectioning then - -- Apply counter defaults - styledef.sectioning.counter = styledef.sectioning.counter or {} - styledef.sectioning.counter.id = styledef.sectioning.counter.id - or SU.error("Sectioning style '"..name.."' must have a counter") - styledef.sectioning.counter.level = styledef.sectioning.counter.level or 1 - - -- Apply settings defaults - styledef.sectioning.settings = styledef.sectioning.settings or {} - -- styledef.sectioning.settings.open: if nil = do not open a page - styledef.sectioning.settings.toclevel = styledef.sectioning.settings.toclevel - and SU.cast("integer", styledef.sectioning.settings.toclevel) - styledef.sectioning.settings.goodbreak = SU.boolean(styledef.sectioning.settings.goodbreak, true) - styledef.sectioning.settings.bookmark = SU.boolean(styledef.sectioning.settings.bookmark, true) - - -- Apply numberstyle defaults - styledef.sectioning.numberstyle = styledef.sectioning.numberstyle or {} - -- styledef.sectioning.numberstyle.main: no default, won't display if absent - -- styledef.sectioning.numberstyle.header: no default, won't display if absent - -- styledef.sectioning.numberstyle.reference: no default, won't display if absent - - -- styledef.sectioning.hook may be absent (no hook) - return styledef + -- Apply counter defaults + styledef.sectioning.counter = styledef.sectioning.counter or {} + styledef.sectioning.counter.id = styledef.sectioning.counter.id + or SU.error("Sectioning style '"..name.."' must have a counter") + styledef.sectioning.counter.level = styledef.sectioning.counter.level or 1 + + -- Apply settings defaults + styledef.sectioning.settings = styledef.sectioning.settings or {} + -- styledef.sectioning.settings.open: if nil = do not open a page + styledef.sectioning.settings.toclevel = styledef.sectioning.settings.toclevel + and SU.cast("integer", styledef.sectioning.settings.toclevel) + styledef.sectioning.settings.goodbreak = SU.boolean(styledef.sectioning.settings.goodbreak, true) + styledef.sectioning.settings.bookmark = SU.boolean(styledef.sectioning.settings.bookmark, true) + + -- Apply numberstyle defaults + styledef.sectioning.numberstyle = styledef.sectioning.numberstyle or {} + -- styledef.sectioning.numberstyle.main: no default, won't display if absent + -- styledef.sectioning.numberstyle.header: no default, won't display if absent + -- styledef.sectioning.numberstyle.reference: no default, won't display if absent + + -- styledef.sectioning.hook may be absent (no hook) + return styledef end SU.error("Style '"..name.."' is not a sectioning style") end @@ -73,8 +73,8 @@ function package:registerCommands () local sty = resolveSectionStyleDef(name) local secStyle = sty.sectioning - -- 1. Handle the page-break: opening page: "unset", "odd" or "any" - -- (Would "even" be useful? I do not think is has any actual use) + -- Handle the page-break: opening page: "unset", "odd" or "any" + -- (Would "even" be useful? I do not think is has any actual use) if secStyle.settings.open and secStyle.settings.open ~= "unset" then -- Sectioning style that causes a page-break. if secStyle.settings.open == "odd" then @@ -95,66 +95,83 @@ function package:registerCommands () -- consecutive styles (e.g. a subsection directly preceded by a section -- shouldn't trigger a goodbreak in-between). - -- 2. Handle the style hook if specified. - -- (Pass the user-defined options + the counter and level, - -- so it has the means to compute it, if needed) + -- Process the section (title) content + local numSty = secStyle.numberstyle.main and self:resolveStyle(secStyle.numberstyle.main) + local numDisplay = numSty and numSty.numbering and numSty.numbering.display or "arabic" + + -- Counter for numbered sections + local number + if numbering then + SILE.call("increment-multilevel-counter", { + id = secStyle.counter.id, + level = secStyle.counter.level, + display = numDisplay + }) + number = self.class.packages.counters:formatMultilevelCounter( + self.class:getMultilevelCounter(secStyle.counter.id), { noleadingzeros = true } + ) + end + + -- Handle the style hook if specified. + -- Pass the user-defined options, the counter and level, so it has them, if needed. + -- Also pass the styled header content (possibly with the number). if secStyle.hook then local hookOptions = pl.tablex.copy(options) hookOptions.counter = secStyle.counter.id hookOptions.level = secStyle.counter.level - SILE.call(secStyle.hook, hookOptions, content) - end - -- 3. Process the section content - local numSty = secStyle.numberstyle.main and self:resolveStyle(secStyle.numberstyle.main) - local numDisplay = numSty and numSty.numbering and numSty.numbering.display or "arabic" - -- FIXME We could refactor more here: some bits do not have to be in the paragraph style. - SILE.call("style:apply:paragraph", { name = name }, { - function () - -- 3A. Counter for numbered sections - local number - if numbering then - SILE.call("increment-multilevel-counter", { - id = secStyle.counter.id, - level = secStyle.counter.level, - display = numDisplay - }) - number = self.class.packages.counters:formatMultilevelCounter( - self.class:getMultilevelCounter(secStyle.counter.id), { noleadingzeros = true } - ) - end + local titleHookContent + local numsty = sty.sectioning and sty.sectioning.numberstyle + and sty.sectioning.numberstyle.header + if numbering and numsty then + titleHookContent = { + utils.createCommand("style:apply:number", { name = numsty, text = number }), + utils.subTreeContent(content) + } + else + titleHookContent = content + end + SILE.call(secStyle.hook, hookOptions, titleHookContent) + end - -- 3B. TOC entry - local toclevel = secStyle.settings.toclevel - local bookmark = secStyle.settings.bookmark - if toclevel and toc then - SILE.call("tocentry", { level = toclevel, number = number, bookmark = bookmark }, SU.subContent(content)) - end + local titleContent = {} + -- TOC entry + -- We pass it added to the content, so the paragraph style is applied around it + -- (and the TOC info node is located in the right place, notwithstanding breaks, skips, etc.). + -- BUT that will be a problem later if the paragraph style includes input filters or needs + -- to tweaks (typically, text casing is in that situation). + -- We could have done things slightly differently (splitting how paragraph style is applied), + -- but I went another quick and dirty route in the styles package... + local toclevel = secStyle.settings.toclevel + local bookmark = secStyle.settings.bookmark + if toclevel and toc then + titleContent[#titleContent + 1] = + utils.createStructuredCommand("tocentry", { level = toclevel, number = number, bookmark = bookmark }, utils.subTreeContent(content)) + end - -- 3C. Show section number (if numbering is true AND a main style is defined) - if numbering then - if secStyle.numberstyle.main then - SILE.call("style:apply:number", { name = secStyle.numberstyle.main, text = number }) - if SU.boolean(numSty.numbering and numSty.numbering.standalone, false) then - SILE.call("break") -- HACK. Pretty weak unless the parent paragraph style is ragged. - end - end + -- Show section number (if numbering is true AND a main style is defined) + if numbering then + if secStyle.numberstyle.main then + titleContent[#titleContent + 1] = + utils.createCommand("style:apply:number", { name = secStyle.numberstyle.main, text = number }) + if SU.boolean(numSty.numbering and numSty.numbering.standalone, false) then + titleContent[#titleContent + 1] = + utils.createCommand("break") -- HACK. Pretty weak unless the parent paragraph style is ragged. end - end, - -- 3D. Section (title) content - utils.subTreeContent(content), - -- 3E. Cross-reference label - function () - -- If the \label command is defined, assume a cross-reference package - -- is loaded and allow specifying a label marker. This makes it less clumsy - -- than having to put it in the section title content, or just after the section - -- (with the risk of impacting indent/noindent and novbreak decisions here) - if marker and SILE.Commands["label"] then SILE.call("label", { marker = marker }) end end - }) - -- Was present in the original book class for section and subsection - -- But seems to behave weird = cancelled for now. - -- SILE.typesetter:inhibitLeading() + end + -- Section (title) content + titleContent[#titleContent + 1] = utils.subTreeContent(content) + + -- Cross-reference label + -- If the \label command is defined, assume a cross-reference package + -- is loaded and allow specifying a label marker. This makes it less clumsy + -- than having to put it in the section title content, or just after the section + -- (with the risk of impacting indent/noindent and novbreak decisions here) + if marker and SILE.Commands["label"] then + titleContent[#titleContent + 1] = utils.createCommand("label", { marker = marker }) + end + SILE.call("style:apply:paragraph", { name = name }, titleContent) end, "Apply sectioning") self:registerCommand("open-on-odd-page", function (_, _) diff --git a/packages/resilient/styles/init.lua b/packages/resilient/styles/init.lua index cbac99f..5ce1e4e 100644 --- a/packages/resilient/styles/init.lua +++ b/packages/resilient/styles/init.lua @@ -264,31 +264,30 @@ function package:registerCommands () end, "Applies a font, with additional support for relative sizes.") -- Very naive cascading... - local characterStyle = function (style, content, options) + local function characterStyle (style, content, options) options = options or {} - local dump = false if style.properties then - for k, v in ipairs(content) do - -- FIXME STYLES functions are annoyinng here... - if type(v) ~= "function" then - if style.properties.position and style.properties.position ~= "normal" then - local positionCommand = SILE.scratch.styles.positions[style.properties.position] - if not positionCommand then - SU.error("Invalid style position '"..style.properties.position.."'") - end - content[k] = utils.createCommand(positionCommand, {}, content[k]) - end - if style.properties.case then - if style.properties.case and style.properties.case ~= "normal" then - local caseCommand = SILE.scratch.styles.cases[style.properties.case] - if not caseCommand then - SU.error("Invalid style case '"..style.properties.case.."'") - end - content[k] = utils.createCommand(caseCommand, {}, content[k]) - end - end - end + local tocentry = utils.extractFromTree(content, "tocentry") -- HACK for sectioning styles + if style.properties.position and style.properties.position ~= "normal" then + local positionCommand = SILE.scratch.styles.positions[style.properties.position] + if not positionCommand then + SU.error("Invalid style position '"..style.properties.position.."'") end + content = utils.createCommand(positionCommand, {}, content) + end + if style.properties.case and style.properties.case ~= "normal" then + local caseCommand = SILE.scratch.styles.cases[style.properties.case] + if not caseCommand then + SU.error("Invalid style case '"..style.properties.case.."'") + end + content = utils.createCommand(caseCommand, {}, content) + end + if tocentry then -- HACK for sectioning styles + -- We don't want character styles from a paragraph level to be applied + -- to the TOC entry content, esp. text casing. + SU.debug("resilient.styles", "TOC entry reinserted due to character styling") + content = { tocentry, content } + end end if style.color then content = utils.createCommand("color", { color = style.color }, content) @@ -296,14 +295,31 @@ function package:registerCommands () if style.font and SU.boolean(options.font, true) then content = utils.createCommand("style:font", style.font, content) end - if dump then SU.dump(content) end return content end - local characterStyleNoFont = function (style, content) + local function characterStyleNoFont (style, content) return characterStyle(style, content, { font = false }) end + local function hackSubContent(content, name) + if type(content) == "table" then + if content.command or content.id then + -- We want to skip the calling content key values (id, command, etc.) + return utils.subTreeContent(content) + end + return content + end + if name ~= "footnote" then -- HACK: Could not avoid function call in resilient.footnotes... + SU.warn("Invocation of style '" .. name .. "'' with unexpected content (" + .. type(content) ..")" .. [[ + For styles to apply correctly, the content should be an AST table. + Some constructs may fail or generate errors later (text case, position, etc.) +]]) + end + return content + end + local characterStyleFontOnly = function (style, content) if style.font then content = utils.createCommand("style:font", style.font, content) @@ -312,17 +328,6 @@ function package:registerCommands () end local styleForAlignment = function (style, content, breakafter) - -- FIXME STYLES This messy, to extract the subContent and skip id nodes - local toProcess - if type(content) == "table" and content.command then - toProcess = {} - for _, v in ipairs(content) do - toProcess[#toProcess+1] = v - end - else - toProcess = content - end - if style.paragraph and style.paragraph.align then if style.paragraph.align then local alignCommand = SILE.scratch.styles.alignments[style.paragraph.align] @@ -336,7 +341,7 @@ function package:registerCommands () -- must be applied last, no to cause havoc with the noindent/indent and -- centering etc. environments local recontent = utils.createCommand(alignCommand, {}, { - characterStyleNoFont(style, toProcess), + characterStyleNoFont(style, content), not breakafter and utils.createCommand("novbreak") or nil }) if style.font then @@ -344,14 +349,14 @@ function package:registerCommands () end SILE.process({ recontent }) else - SILE.process({ characterStyle(style, toProcess) }) + SILE.process({ characterStyle(style, content) }) if not breakafter then SILE.call("novbreak") end -- NOTE: SILE.call("par") would cause a parskip to be inserted. -- Not really sure whether we expect this here or not. SILE.typesetter:leaveHmode() end else - SILE.process({ characterStyle(style, toProcess) }) + SILE.process({ characterStyle(style, content) }) end end @@ -361,19 +366,10 @@ function package:registerCommands () local name = SU.required(options, "name", "style:apply") local styledef = self:resolveStyle(name, options.discardable) - -- FIXME STYLES This messy, to extract the subContent and skip id nodes - local toProcess - if type(content) == "table" and (content.command or content.id) then - toProcess = {} - for _, v in ipairs(content) do - toProcess[#toProcess+1] = v - end - else - toProcess = content - end + content = hackSubContent(content, name) -- HACK: see above - toProcess = characterStyle(styledef, toProcess) - SILE.process({ toProcess }) + content = characterStyle(styledef, content) + SILE.process({ content }) end, "Applies a named character style to the content.") -- APPLY A PARAGRAPH STYLE @@ -493,6 +489,8 @@ function package:registerCommands () local styledef = self:resolveParagraphStyle(name, options.discardable) local parSty = styledef.paragraph + content = hackSubContent(content, name) -- HACK: see above + local bb = SU.boolean(parSty.before.vbreak, true) if #SILE.typesetter.state.nodes then if not bb then diff --git a/packages/resilient/tableofcontents/init.lua b/packages/resilient/tableofcontents/init.lua index 8a1e9b4..0b15092 100644 --- a/packages/resilient/tableofcontents/init.lua +++ b/packages/resilient/tableofcontents/init.lua @@ -5,6 +5,7 @@ -- License: MIT -- local base = require("packages.resilient.base") +local utils = require("resilient.utils") local package = pl.class(base) package._name = "resilient.tableofcontents" @@ -151,19 +152,19 @@ function package:registerCommands () local oldLbl = SILE.Commands["label"] SILE.Commands["label"] = function () end - SILE.call("style:apply:paragraph", { name = "toc" }, function () - for i = 1, #toc do - local item = toc[i] - if item.level >= start and item.level <= start + depth then - SILE.call("tableofcontents:item", { - level = item.level, - pageno = item.pageno, - number = item.number, - link = linking and item.link - }, item.label) - end + local tocItems = {} + for i = 1, #toc do + local item = toc[i] + if item.level >= start and item.level <= start + depth then + tocItems[#tocItems + 1] = utils.createCommand("tableofcontents:item", { + level = item.level, + pageno = item.pageno, + number = item.number, + link = linking and item.link + }, utils.subTreeContent(item.label)) end - end) + end + SILE.call("style:apply:paragraph", { name = "toc" }, tocItems) SILE.Commands["footnote"] = oldFt SILE.Commands["label"] = oldLbl @@ -194,7 +195,7 @@ function package:registerCommands () -- (e.g. \raise), we cannot take a general decision, as it is a versatile -- object (e.g. \rebox) and its outputYourself could moreover have been -- redefine to do fancy things. Better warn and skip. - SU.warn("Some content could not be converted to text: "..node) + SU.warn("Some content could not be converted to text: " .. node) end end -- Trim leading and trailing spaces, and simplify internal spaces. @@ -223,14 +224,18 @@ function package:registerCommands () local title = nodesToText(SILE.typesetter.state.nodes) SILE.typesetter:popState() - SILE.call("pdf:bookmark", { title = title, dest = dest, level = options.level }) + SILE.call("pdf:bookmark", { + title = title, + dest = dest, + level = options.level + }) end dc = dc + 1 end SILE.call("info", { category = "toc", value = { - label = SU.stripContentPos(content), + label = utils.subTreeContent(SU.stripContentPos(content)), level = (options.level or 1), number = options.number, link = dest @@ -238,23 +243,24 @@ function package:registerCommands () }) end, "Register an entry in the current TOC - low-level command.") - local linkWrapper = function (dest, func) + local linkWrapper = function (dest, content) if dest and SILE.Commands["pdf:link"] then - return function() - SILE.call("pdf:link", { dest = dest }, func) - end - else - return func + return { + utils.createStructuredCommand("pdf:link", { dest = dest }, content) + } end + return content end self:registerCommand("tableofcontents:item", function (options, content) local level = SU.cast("integer", SU.required(options, "level", "tableofcontents:levelitem")) - if level < 0 or level > #tocStyles - 1 then SU.error("Invalid TOC level "..level) end + if level < 0 or level > #tocStyles - 1 then + SU.error("Invalid TOC level " .. level) + end local hasFiller = true local hasPageno = true - local tocSty = self:resolveStyle("toc-level"..level) + local tocSty = self:resolveStyle("toc-level" .. level) if tocSty.toc then hasPageno = SU.boolean(tocSty.toc.pageno, true) hasFiller = hasPageno and SU.boolean(tocSty.toc.dotfill, true) @@ -262,29 +268,39 @@ function package:registerCommands () SILE.settings:temporarily(function () SILE.settings:set("typesetter.parfillskip", SILE.nodefactory.glue()) - SILE.call("style:apply:paragraph", { name = "toc-level"..level }, - linkWrapper(options.link, function () - if options.number then - SILE.call("tableofcontents:levelnumber", { level = level, text = options.number }) - end - SILE.process(content) - SILE.call(hasFiller and "dotfill" or "hfill") - if hasPageno then - SILE.call("style:apply", { name = "toc-pageno"}, { options.pageno }) - end - end) - ) + local itemContent = {} + if options.number then + itemContent[#itemContent + 1] = utils.createCommand("tableofcontents:levelnumber", { + level = level, + text = options.number + }) + end + itemContent[#itemContent + 1] = utils.subTreeContent(content) + itemContent[#itemContent + 1] = utils.createCommand(hasFiller and "dotfill" or "hfill") + if hasPageno then + itemContent[#itemContent + 1] = utils.createCommand("style:apply", { + name = "toc-pageno" + }, {options.pageno}) + end + SILE.call("style:apply:paragraph", { + name = "toc-level" .. level + }, linkWrapper(options.link, itemContent)) end) end, "Typeset a TOC entry - internal.") self:registerCommand("tableofcontents:levelnumber", function (options, _) local text = SU.required(options, "text", "tableofcontents:levelnumber") local level = SU.cast("integer", SU.required(options, "level", "tableofcontents:levelnumber")) - if level < 0 or level > #tocStyles - 1 then SU.error("Invalid TOC level "..level) end + if level < 0 or level > #tocStyles - 1 then + SU.error("Invalid TOC level " .. level) + end - local tocSty = self:resolveStyle("toc-level"..level) + local tocSty = self:resolveStyle("toc-level" .. level) if tocSty.toc and SU.boolean(tocSty.toc.numbered, false) then - SILE.call("style:apply:number", { name = "toc-number-level"..level, text = text }) + SILE.call("style:apply:number", { + name = "toc-number-level" .. level, + text = text + }) end end, "Typeset the (section) number in a TOC entry - internal.") end