• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sile-typesetter / sile / 8288578143

14 Mar 2024 09:39PM UTC coverage: 64.155% (-10.6%) from 74.718%
8288578143

Pull #1904

github

alerque
chore(core): Fixup ec6ed657 which didn't shim old pack styles properly
Pull Request #1904: Merge develop into master (commit to next release being breaking)

1648 of 2421 new or added lines in 107 files covered. (68.07%)

1843 existing lines in 77 files now uncovered.

10515 of 16390 relevant lines covered (64.15%)

3306.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.58
/classes/plain.lua
1
--- plain document class.
2
-- @use classes.plain
3

4
local base = require("classes.base")
182✔
5

6
local class = pl.class(base)
182✔
7
class._name = "plain"
182✔
8

9
class.defaultFrameset = {
182✔
10
  content = {
182✔
11
    left = "5%pw",
12
    right = "95%pw",
13
    top = "5%ph",
14
    bottom = "top(footnotes)"
×
15
  },
182✔
16
  folio = {
182✔
17
    left = "left(content)",
18
    right = "right(content)",
19
    top = "bottom(footnotes)+2%ph",
20
    bottom = "97%ph"
×
21
  },
182✔
22
  footnotes = {
182✔
23
    left = "left(content)",
24
    right = "right(content)",
25
    height = "0",
26
    bottom = "90%ph"
×
27
  }
182✔
28
}
182✔
29
class.firstContentFrame = "content"
182✔
30

31
local skips = {
182✔
32
  small = "3pt plus 1pt minus 1pt",
33
  med = "6pt plus 2pt minus 2pt",
34
  big = "12pt plus 4pt minus 4pt"
×
35
}
36

37
function class:_init (options)
182✔
38
  base._init(self, options)
182✔
39
  self:loadPackage("bidi")
182✔
40
  self:loadPackage("folio")
182✔
41
end
42

43
function class:declareOptions ()
182✔
44
  base.declareOptions(self)
182✔
45
  self:declareOption("direction", function (_, value)
364✔
46
    if value then
6✔
47
      SILE.documentState.direction = value
6✔
48
      SILE.settings:set("font.direction", value, true)
6✔
49
      for _, frame in pairs(self.defaultFrameset) do
24✔
50
        if not frame.direction then
18✔
51
          frame.direction = value
18✔
52
        end
53
      end
54
    end
55
    return SILE.documentState.direction
6✔
56
  end)
57
end
58

59
function class:setOptions (options)
182✔
60
  -- TODO: set a default direction here?
61
  base.setOptions(self, options)
182✔
62
end
63

64
function class:declareSettings ()
182✔
65
  base.declareSettings(self)
182✔
66
  for k, v in pairs(skips) do
728✔
67
    SILE.settings:declare({
1,092✔
68
        parameter = "plain." .. k .. "skipamount",
546✔
69
        type = "vglue",
70
        default = SILE.types.node.vglue(v),
1,092✔
71
        help = "The amount of a \\" .. k .. "skip"
546✔
72
      })
73
  end
74
end
75

76
function class:registerCommands ()
182✔
77

78
  SILE.classes.base.registerCommands(self)
182✔
79

80
  self:registerCommand("noindent", function (_, content)
364✔
81
    if #SILE.typesetter.state.nodes ~= 0 then
110✔
82
      SU.warn("\\noindent called after nodes already received in a paragraph, the setting will have no effect because the parindent (if any) has already been output")
1✔
83
    end
84
    SILE.settings:set("current.parindent", SILE.types.node.glue())
220✔
85
    SILE.process(content)
110✔
86
  end, "Do not add an indent to the start of this paragraph")
292✔
87

88
  self:registerCommand("neverindent", function (_, content)
364✔
89
    SILE.settings:set("current.parindent", SILE.types.node.glue())
126✔
90
    SILE.settings:set("document.parindent", SILE.types.node.glue())
126✔
91
    SILE.process(content)
63✔
92
  end, "Turn off all indentation")
245✔
93

94
  self:registerCommand("indent", function (_, content)
364✔
95
    SILE.settings:set("current.parindent", SILE.settings:get("document.parindent"))
8✔
96
    SILE.process(content)
4✔
97
  end, "Do add an indent to the start of this paragraph, even if previously told otherwise")
186✔
98

99
  for k, _ in pairs(skips) do
728✔
100
    self:registerCommand(k .. "skip", function (_, _)
1,092✔
101
      SILE.typesetter:leaveHmode()
53✔
102
      SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
106✔
103
    end, "Skip vertically by a " .. k .. " amount")
1,145✔
104
  end
105

106
  self:registerCommand("hfill", function (_, _)
364✔
107
    SILE.typesetter:pushExplicitGlue(SILE.types.node.hfillglue())
50✔
108
  end, "Add a huge horizontal glue")
207✔
109

110
  self:registerCommand("vfill", function (_, _)
364✔
111
    SILE.typesetter:leaveHmode()
381✔
112
    SILE.typesetter:pushExplicitVglue(SILE.types.node.vfillglue())
762✔
113
  end, "Add huge vertical glue")
563✔
114

115
  self:registerCommand("hss", function (_, _)
364✔
116
    SILE.typesetter:initline()
×
NEW
117
    SILE.typesetter:pushGlue(SILE.types.node.hssglue())
×
NEW
118
    table.insert(SILE.typesetter.state.nodes, SILE.types.node.zerohbox())
×
119
  end, "Add glue which stretches and shrinks horizontally (good for centering)")
182✔
120

121
  self:registerCommand("vss", function (_, _)
364✔
NEW
122
    SILE.typesetter:pushExplicitVglue(SILE.types.node.vssglue())
×
123
  end, "Add glue which stretches and shrinks vertically")
182✔
124

125
  local _thinspacewidth = SILE.types.measurement(0.16667, "em")
182✔
126

127
  self:registerCommand("thinspace", function (_, _)
364✔
128
    SILE.call("glue", { width = _thinspacewidth })
×
129
  end)
130

131
  self:registerCommand("negthinspace", function (_, _)
364✔
132
    SILE.call("glue", { width = -_thinspacewidth })
×
133
  end)
134

135
  self:registerCommand("enspace", function (_, _)
364✔
NEW
136
    SILE.call("glue", { width = SILE.types.measurement(1, "en") })
×
137
  end)
138

139
  self:registerCommand("relax", function (_, _)
364✔
140
  end)
141

142
  self:registerCommand("enskip", function (_, _)
364✔
143
    SILE.call("enspace")
×
144
  end)
145

146
  local _quadwidth = SILE.types.measurement(1, "em")
182✔
147

148
  self:registerCommand("quad", function (_, _)
364✔
149
    SILE.call("glue", { width = _quadwidth })
1✔
150
  end)
151

152
  self:registerCommand("qquad", function (_, _)
364✔
153
    SILE.call("glue", { width = _quadwidth * 2 })
90✔
154
  end)
155

156
  self:registerCommand("slash", function (_, _)
364✔
157
    SILE.typesetter:typeset("/")
×
158
    SILE.call("penalty", { penalty = 50 })
×
159
  end)
160

161
  self:registerCommand("break", function (_, _)
364✔
162
    SILE.call("penalty", { penalty = -10000 })
45✔
163
  end, "Requests a frame break (if in vertical mode) or a line break (if in horizontal mode)")
227✔
164

165
  self:registerCommand("cr", function (_, _)
364✔
166
    SILE.call("hfill")
3✔
167
    SILE.call("break")
3✔
168
  end, "Fills a line with a stretchable glue and then requests a line break")
185✔
169

170
  -- Despite their name, in older versions, \framebreak and \pagebreak worked badly in horizontal
171
  -- mode. The former was a linebreak, and the latter did nothing. That was surely not intended.
172
  -- There are many ways, though to assume what's wrong or what the user's intent ought to be.
173
  -- We now warn, and terminate the paragraph, but to all extents this might be a wrong approach to
174
  -- reconsider at some point.
175

176
  self:registerCommand("framebreak", function (_, _)
364✔
177
    if not SILE.typesetter:vmode() then
22✔
178
      SU.warn("framebreak was not intended to work in horizontal mode. Behaviour may change in future versions")
×
179
    end
180
    SILE.call("penalty", { penalty = -10000, vertical = true })
11✔
181
  end, "Requests a frame break (switching to vertical mode if needed)")
193✔
182

183
  self:registerCommand("pagebreak", function (_, _)
364✔
184
    if not SILE.typesetter:vmode() then
8✔
185
      SU.warn("pagebreak was not intended to work in horizontal mode. Behaviour may change in future versions")
×
186
    end
187
    SILE.call("penalty", { penalty = -20000, vertical = true })
4✔
188
  end, "Requests a non-negotiable page break (switching to vertical mode if needed)")
186✔
189

190
  self:registerCommand("nobreak", function (_, _)
364✔
191
    SILE.call("penalty", { penalty = 10000 })
×
192
  end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
182✔
193

194
  self:registerCommand("novbreak", function (_, _)
364✔
195
    SILE.call("penalty", { penalty = 10000, vertical = true })
7✔
196
  end, "Inhibits a frame break (switching to vertical mode if needed)")
189✔
197

198
  self:registerCommand("allowbreak", function (_, _)
364✔
199
    SILE.call("penalty", { penalty = 0 })
×
200
  end, "Allows a page break (if in vertical mode) or a line break (if in horizontal mode) at a point would not be considered as suitable for breaking")
182✔
201

202
  -- THIS SEEMS BROKEN BUT THE COMMAND NOT MENTIONED IN THE SILE MANUAL
203
  -- In TeX, "\filbreak" compensates the vertical fill if no break actually occurs
204
  -- (\def\filbreak{\par\vfil\penalty-200\vfilneg)
205
  self:registerCommand("filbreak", function (_, _)
364✔
206
    SILE.call("vfill")
×
207
    SILE.call("penalty", { penalty = -200 })
×
208
  end, "I HAVE THE SAME NAME AS A TEX COMMAND BUT DON'T SEEM TO BE THE SAME")
182✔
209

210
  -- NOTE: TeX's "\goodbreak" does a \par first, so always switches to vertical mode.
211
  -- SILE differs here, allowing it both within a paragraph (line breaking) and between
212
  -- paragraphs (page breaking).
213
  self:registerCommand("goodbreak", function (_, _)
364✔
214
    SILE.call("penalty", { penalty = -500 })
4✔
215
  end, "Indicates a good potential point to break a frame (if in vertical mode) or a line (if in horizontal mode")
186✔
216

217
  self:registerCommand("eject", function (_, _)
364✔
218
    SILE.call("vfill")
3✔
219
    SILE.call("break")
3✔
220
  end, "Fills the page with stretchable vglue and then request a page break")
185✔
221

222
  self:registerCommand("supereject", function (_, _)
364✔
223
    SILE.call("vfill")
192✔
224
    SILE.call("penalty", { penalty = -20000 })
192✔
225
  end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
374✔
226

227
  self:registerCommand("em", function (_, content)
364✔
228
    local style = SILE.settings:get("font.style")
69✔
229
    local toggle = (style and style:lower() == "italic") and "Regular" or "Italic"
138✔
230
    SILE.call("font", { style = toggle }, content)
69✔
231
  end, "Emphasizes its contents by switching the font style to italic (or back to regular if already italic)")
251✔
232

233
  self:registerCommand("strong", function (_, content)
364✔
234
    SILE.call("font", { weight = 700 }, content)
×
235
  end, "Sets the font weight to bold (700)")
182✔
236

237
  self:registerCommand("code", function (options, content)
364✔
238
    -- IMPLEMENTATION NOTE:
239
    -- The \code command came from the url package, though used in plenty of
240
    -- places. It was referring to the verbatim:font from the verbatim
241
    -- package, which _should_ be sort of unrelated.
242
    -- Trying to untangle the things here, by introducing the
243
    -- definition from the former, but it's of sub-quality...
244
    -- The ugly -3 size is a HACK of sorts.
245
    options.family = options.family or "Hack"
1✔
246
    options.size = options.size or SILE.settings:get("font.size") - 3
2✔
247
    SILE.call("font", options, content)
1✔
248
  end)
249

250
  self:registerCommand("nohyphenation", function (_, content)
364✔
251
    SILE.call("font", { language = "und" }, content)
×
252
  end)
253

254
  self:registerCommand("center", function (_, content)
364✔
255
    if #SILE.typesetter.state.nodes ~= 0 then
30✔
NEW
256
      SU.warn("\\center environment started after other nodes in a paragraph, may not center as expected")
×
257
    end
258
    SILE.settings:temporarily(function ()
60✔
259
      local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
60✔
260
      local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
60✔
261
      SILE.settings:set("document.parindent", SILE.types.node.glue())
60✔
262
      SILE.settings:set("current.parindent", SILE.types.node.glue())
60✔
263
      SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
60✔
264
      SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
60✔
265
      SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
60✔
266
      SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
60✔
267
      SILE.process(content)
30✔
268
      SILE.call("par")
30✔
269
    end)
270
  end, "Typeset its contents in a centered block (keeping margins).")
212✔
271

272
  self:registerCommand("raggedright", function (_, content)
364✔
273
    SILE.settings:temporarily(function ()
10✔
274
      local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
10✔
275
      local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
10✔
276
      SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
10✔
277
      SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
10✔
278
      SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
10✔
279
      SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
10✔
280
      SILE.process(content)
5✔
281
      SILE.call("par")
5✔
282
    end)
283
  end, "Typeset its contents in a left aligned block (keeping margins).")
187✔
284

285
  self:registerCommand("raggedleft", function (_, content)
364✔
286
    SILE.settings:temporarily(function ()
6✔
287
      local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
6✔
288
      local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
6✔
289
      SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
6✔
290
      SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
6✔
291
      SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
6✔
292
      SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
6✔
293
      SILE.process(content)
3✔
294
      SILE.call("par")
3✔
295
    end)
296
  end, "Typeset its contents in a right aligned block (keeping margins).")
185✔
297

298
  self:registerCommand("justified", function (_, content)
364✔
299
    SILE.settings:temporarily(function ()
2✔
300
      local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
2✔
301
      local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
2✔
302
      -- Keep the fixed part of the margins for nesting but remove the stretchability.
303
      SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
2✔
304
      SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
2✔
305
      -- Reset parfillskip to its default value, in case the surrounding context
306
      -- is ragged and cancelled it.
307
      SILE.settings:set("typesetter.parfillskip", nil, false, true)
1✔
308
      SILE.settings:set("document.spaceskip", nil)
1✔
309
      SILE.process(content)
1✔
310
      SILE.call("par")
1✔
311
    end)
312
   end, "Typeset its contents in a justified block (keeping margins).")
183✔
313

314
  self:registerCommand("ragged", function (options, content)
364✔
315
    -- Fairly dubious command for compatibility
316
    local l = SU.boolean(options.left, false)
1✔
317
    local r = SU.boolean(options.right, false)
1✔
318
    if l and r then
1✔
319
      SILE.call("center", {}, content)
2✔
NEW
320
    elseif r then
×
NEW
321
      SILE.call("raggedleft", {}, content)
×
NEW
322
    elseif l then
×
NEW
323
      SILE.call("raggedright", {}, content)
×
324
    else
NEW
325
      SILE.call("justified", {}, content)
×
326
    end
327
  end)
328

329
  self:registerCommand("rightalign", function (_, content)
364✔
330
    SU.deprecated("\\rightalign", "\\raggedleft", "0.15.0", "0.17.0")
4✔
331
    SILE.call("raggedleft", {}, content)
4✔
332
  end)
333

334
  self:registerCommand("blockquote", function (_, content)
364✔
NEW
335
    SILE.call("smallskip")
×
NEW
336
    SILE.typesetter:leaveHmode()
×
NEW
337
    SILE.settings:temporarily(function ()
×
NEW
338
      local indent = SILE.types.measurement("2em"):absolute()
×
NEW
339
      local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
×
NEW
340
      local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
×
341
      -- We keep the stretcheability of the lskip and rskip: honoring text alignment
342
      -- from the parent context.
NEW
343
      SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width + indent))
×
NEW
344
      SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width + indent))
×
NEW
345
      SILE.settings:set("font.size", SILE.settings:get("font.size") * 0.95)
×
NEW
346
      SILE.process(content)
×
NEW
347
      SILE.typesetter:leaveHmode()
×
348
    end)
NEW
349
    SILE.call("smallskip")
×
350
  end, "A blockquote environment")
182✔
351

352
  self:registerCommand("quote", function (_, content)
364✔
NEW
353
    SU.deprecated("\\quote", "\\pullquote or \\blockquote", "0.14.5", "0.16.0", [[
×
354
  The \quote command has *such* bad output it is being completely
355
  deprecated as unsuitable for general purpose use.
356
  The pullquote package (\use[module=packages.pullquote]) provides one
357
  alternative, and the blockquote environment provides another.
358
  But you can also copy and adapt the original source from the plain
359
  class if you need to maintain exact output past SILE v0.16.0.]])
×
360
    SILE.call("smallskip")
×
361
    SILE.call("par")
×
NEW
362
    local margin = SILE.types.measurement(2.5, "em")
×
363
    SILE.settings:set("document.lskip", margin)
×
364
    SILE.settings:set("document.lskip", margin)
×
NEW
365
    SILE.call("font", { size = SILE.types.measurement(0.8, "em") }, function ()
×
366
      SILE.call("noindent")
×
367
      SILE.process(content)
×
368
    end)
369
    SILE.call("par")
×
370
    SILE.settings:set("document.lskip", nil)
×
371
    SILE.settings:set("document.rskip", nil)
×
372
    SILE.call("smallskip")
×
373
  end)
374

375
  self:registerCommand("listitem", function (_, content)
364✔
376
    SU.deprecated("\\listitem", "\\item", "0.14.6", "0.16.0", [[
×
377
  The new list package (\use[module=packages.lists) has much better
378
  typography for lists. If you want to maintain the exact output of listitem
379
  past SILE v0.16.0 copy the source of \listitem from the plain class into
380
  your project.]])
×
381
    SILE.call("medskip")
×
382
    SILE.typesetter:typeset("• ")
×
383
    SILE.process(content)
×
384
    SILE.call("medskip")
×
385
  end)
386

387
  self:registerCommand("sloppy", function (_, _)
364✔
388
    SILE.settings:set("linebreak.tolerance", 9999)
×
389
  end)
390

391
  self:registerCommand("awful", function (_, _)
364✔
392
    SILE.settings:set("linebreak.tolerance", 10000)
×
393
  end)
394

395
  self:registerCommand("hbox", function (_, content)
364✔
396
    local hbox, hlist = SILE.typesetter:makeHbox(content)
60✔
397
    SILE.typesetter:pushHbox(hbox)
60✔
398
    if #hlist > 0 then
60✔
399
      SU.warn("Hbox has migrating content (ignored for now, but likely to break in future versions)")
×
400
      -- Ugly shim:
401
      -- One day we ought to do SILE.typesetter:pushHlist(hlist) here, so as to push
402
      -- back the migrating contents from within the hbox'ed content.
403
      -- However, old Lua code assumed the hbox to be returned, and sometimes removed it
404
      -- from the typesetter queue (for measuring, etc.), assuming it was the last
405
      -- element in the queue...
406
    end
407
    return hbox
60✔
408
  end, "Compiles all the enclosed horizontal-mode material into a single hbox")
182✔
409

410
  self:registerCommand("vbox", function (options, content)
364✔
411
    local vbox
412
    SILE.settings:temporarily(function ()
90✔
413
      if options.width then SILE.settings:set("typesetter.breakwidth", SILE.types.length(options.width)) end
45✔
414
      SILE.typesetter:pushState()
45✔
415
      SILE.process(content)
45✔
416
      SILE.typesetter:leaveHmode(1)
45✔
417
      vbox = SILE.pagebuilder:collateVboxes(SILE.typesetter.state.outputQueue)
90✔
418
      SILE.typesetter:popState()
45✔
419
    end)
420
    return vbox
45✔
421
  end, "Compiles all the enclosed material into a single vbox")
182✔
422

423
end
424

425
return class
182✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc