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

sile-typesetter / sile / 14844751993

05 May 2025 05:31PM UTC coverage: 59.865% (+27.8%) from 32.062%
14844751993

push

github

alerque
fix(packages): Fix package reload to not call for command reregistration unless asked

1 of 1 new or added line in 1 file covered. (100.0%)

88 existing lines in 14 files now uncovered.

12962 of 21652 relevant lines covered (59.87%)

4076.32 hits per line

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

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

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

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

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

31
local skips = {
153✔
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)
153✔
38
   base._init(self, options)
153✔
39
   self:loadPackage("bidi")
153✔
40
   self:loadPackage("folio")
153✔
41
end
42

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

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

63
function class:declareSettings ()
153✔
64
   for k, v in pairs(skips) do
612✔
65
      SILE.settings:declare({
918✔
66
         parameter = "plain." .. k .. "skipamount",
459✔
67
         type = "vglue",
68
         default = SILE.types.node.vglue(v),
918✔
69
         help = "The amount of a \\" .. k .. "skip",
459✔
70
      })
71
   end
72
end
73

74
function class:registerCommands ()
153✔
75
   self:registerCommand("noindent", function (_, content)
306✔
76
      if #SILE.typesetter.state.nodes ~= 0 then
93✔
77
         SU.warn([[
2✔
78
            \noindent was called after paragraph content has already been processed
79

80
            This will not result in avoiding the current paragraph being indented. This
81
            function must be called before any content belonging to the paragraph is
82
            processed. If the intent was to suppress indentation of a following paragraph,
83
            first explicitly close the current paragraph. From an input document this is
84
            typically done with an empty line between paragraphs, but calling the \par
85
            command explicitly or from Lua code running SILE.call("par") will end
86
            the current paragraph.
87
         ]])
1✔
88
      end
89
      SILE.settings:set("current.parindent", SILE.types.node.glue())
186✔
90
      SILE.process(content)
93✔
91
   end, "Do not add an indent to the start of this paragraph")
246✔
92

93
   self:registerCommand("neverindent", function (_, content)
306✔
94
      SILE.settings:set("current.parindent", SILE.types.node.glue())
90✔
95
      SILE.settings:set("document.parindent", SILE.types.node.glue())
90✔
96
      SILE.process(content)
45✔
97
   end, "Turn off all indentation")
198✔
98

99
   self:registerCommand("indent", function (_, content)
306✔
100
      SILE.settings:set("current.parindent", SILE.settings:get("document.parindent"))
8✔
101
      SILE.process(content)
4✔
102
   end, "Do add an indent to the start of this paragraph, even if previously told otherwise")
157✔
103

104
   for k, _ in pairs(skips) do
612✔
105
      self:registerCommand(k .. "skip", function (_, _)
918✔
106
         SILE.typesetter:leaveHmode()
38✔
107
         SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
76✔
108
      end, "Skip vertically by a " .. k .. " amount")
956✔
109
   end
110

111
   self:registerCommand("hfill", function (_, _)
306✔
112
      SILE.typesetter:pushExplicitGlue(SILE.types.node.hfillglue())
44✔
113
   end, "Add a huge horizontal glue")
175✔
114

115
   self:registerCommand("vfill", function (_, _)
306✔
116
      SILE.typesetter:leaveHmode()
322✔
117
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vfillglue())
644✔
118
   end, "Add huge vertical glue")
475✔
119

120
   self:registerCommand("hss", function (_, _)
306✔
121
      SILE.typesetter:pushGlue(SILE.types.node.hssglue())
×
122
      table.insert(SILE.typesetter.state.nodes, SILE.types.node.zerohbox())
×
123
   end, "Add glue which stretches and shrinks horizontally (good for centering)")
153✔
124

125
   self:registerCommand("vss", function (_, _)
306✔
126
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vssglue())
×
127
   end, "Add glue which stretches and shrinks vertically")
153✔
128

129
   local _thinspacewidth = SILE.types.measurement(0.16667, "em")
153✔
130

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

135
   self:registerCommand("negthinspace", function (_, _)
306✔
136
      SILE.call("glue", { width = -_thinspacewidth })
×
137
   end)
138

139
   self:registerCommand("enspace", function (_, _)
306✔
140
      SILE.call("glue", { width = SILE.types.measurement(1, "en") })
×
141
   end)
142

143
   self:registerCommand("relax", function (_, _) end)
153✔
144

145
   self:registerCommand("enskip", function (_, _)
306✔
146
      SILE.call("enspace")
×
147
   end)
148

149
   local _quadwidth = SILE.types.measurement(1, "em")
153✔
150

151
   self:registerCommand("quad", function (_, _)
306✔
152
      SILE.call("glue", { width = _quadwidth })
1✔
153
   end)
154

155
   self:registerCommand("qquad", function (_, _)
306✔
156
      SILE.call("glue", { width = _quadwidth * 2 })
88✔
157
   end)
158

159
   self:registerCommand("slash", function (_, _)
306✔
160
      SILE.typesetter:typeset("/")
×
161
      SILE.call("penalty", { penalty = 50 })
×
162
   end)
163

164
   self:registerCommand("break", function (_, _)
306✔
165
      SILE.call("penalty", { penalty = -10000 })
39✔
166
   end, "Requests a frame break (if in vertical mode) or a line break (if in horizontal mode)")
192✔
167

168
   self:registerCommand("cr", function (_, _)
306✔
169
      SILE.call("hfill")
2✔
170
      SILE.call("break")
2✔
171
   end, "Fills a line with a stretchable glue and then requests a line break")
155✔
172

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

179
   self:registerCommand("framebreak", function (_, _)
306✔
180
      if not SILE.typesetter:vmode() then
22✔
181
         SU.warn([[
×
182
            \\framebreak was not intended to work in horizontal mode
183

184
            Behavior may change in future versions.
185
         ]])
×
186
      end
187
      SILE.call("penalty", { penalty = -10000, vertical = true })
11✔
188
   end, "Requests a frame break (switching to vertical mode if needed)")
164✔
189

190
   self:registerCommand("pagebreak", function (_, _)
306✔
191
      if not SILE.typesetter:vmode() then
10✔
192
         SU.warn([[
×
193
            \\pagebreak was not intended to work in horizontal mode
194

195
            Behavior may change in future versions.
196
         ]])
×
197
      end
198
      SILE.call("penalty", { penalty = -20000, vertical = true })
5✔
199
   end, "Requests a non-negotiable page break (switching to vertical mode if needed)")
158✔
200

201
   self:registerCommand("nobreak", function (_, _)
306✔
202
      SILE.call("penalty", { penalty = 10000 })
×
203
   end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
153✔
204

205
   self:registerCommand("novbreak", function (_, _)
306✔
206
      SILE.call("penalty", { penalty = 10000, vertical = true })
9✔
207
   end, "Inhibits a frame break (switching to vertical mode if needed)")
162✔
208

209
   self:registerCommand(
306✔
210
      "allowbreak",
153✔
211
      function (_, _)
212
         SILE.call("penalty", { penalty = 0 })
×
213
      end,
214
      "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"
215
   )
153✔
216

217
   -- THIS SEEMS BROKEN BUT THE COMMAND NOT MENTIONED IN THE SILE MANUAL
218
   -- In TeX, "\filbreak" compensates the vertical fill if no break actually occurs
219
   -- (\def\filbreak{\par\vfil\penalty-200\vfilneg)
220
   self:registerCommand("filbreak", function (_, _)
306✔
221
      SILE.call("vfill")
×
222
      SILE.call("penalty", { penalty = -200 })
×
223
   end, "I HAVE THE SAME NAME AS A TEX COMMAND BUT DON'T SEEM TO BE THE SAME")
153✔
224

225
   -- NOTE: TeX's "\goodbreak" does a \par first, so always switches to vertical mode.
226
   -- SILE differs here, allowing it both within a paragraph (line breaking) and between
227
   -- paragraphs (page breaking).
228
   self:registerCommand("goodbreak", function (_, _)
306✔
229
      SILE.call("penalty", { penalty = -500 })
4✔
230
   end, "Indicates a good potential point to break a frame (if in vertical mode) or a line (if in horizontal mode")
157✔
231

232
   self:registerCommand("eject", function (_, _)
306✔
233
      SILE.call("vfill")
3✔
234
      SILE.call("break")
3✔
235
   end, "Fills the page with stretchable vglue and then request a page break")
156✔
236

237
   self:registerCommand("supereject", function (_, _)
306✔
238
      SILE.call("vfill")
163✔
239
      SILE.call("penalty", { penalty = -20000 })
163✔
240
   end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
316✔
241

242
   self:registerCommand("em", function (_, content)
306✔
243
      local style = SILE.settings:get("font.style")
63✔
244
      local toggle = (style and style:lower() == "italic") and "Regular" or "Italic"
126✔
245
      SILE.call("font", { style = toggle }, content)
63✔
246
   end, "Emphasizes its contents by switching the font style to italic (or back to regular if already italic)")
216✔
247

248
   self:registerCommand("strong", function (_, content)
306✔
249
      SILE.call("font", { weight = 700 }, content)
×
250
   end, "Sets the font weight to bold (700)")
153✔
251

252
   self:registerCommand("code", function (options, content)
306✔
253
      -- IMPLEMENTATION NOTE:
254
      -- The \code command came from the url package, though used in plenty of
255
      -- places. It was referring to the verbatim:font from the verbatim
256
      -- package, which _should_ be sort of unrelated.
257
      -- Trying to untangle the things here, by introducing the
258
      -- definition from the former, but it's of sub-quality...
259
      -- The ugly -3 size is a HACK of sorts.
260
      options.family = options.family or "Hack"
1✔
261
      if not options.size and not options.adjust then
1✔
262
         options.adjust = "ex-height"
1✔
263
      end
264
      SILE.call("font", options, content)
1✔
265
   end)
266

267
   self:registerCommand("nohyphenation", function (_, content)
306✔
268
      SILE.call("font", { language = "und" }, content)
×
269
   end)
270

271
   self:registerCommand("center", function (_, content)
306✔
272
      if #SILE.typesetter.state.nodes ~= 0 then
30✔
273
         SU.warn([[
×
274
            \\center environment started after other nodes in a paragraph
275

276
            Content may not be centered as expected.
277
         ]])
×
278
      end
279
      SILE.settings:temporarily(function ()
60✔
280
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
60✔
281
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
60✔
282
         SILE.settings:set("document.parindent", SILE.types.node.glue())
60✔
283
         SILE.settings:set("current.parindent", SILE.types.node.glue())
60✔
284
         SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
60✔
285
         SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
60✔
286
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
60✔
287
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
60✔
288
         SILE.process(content)
30✔
289
         SILE.call("par")
30✔
290
      end)
291
   end, "Typeset its contents in a centered block (keeping margins).")
183✔
292

293
   self:registerCommand("raggedright", function (_, content)
306✔
294
      SILE.settings:temporarily(function ()
8✔
295
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
8✔
296
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
8✔
297
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
8✔
298
         SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
8✔
299
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
8✔
300
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
8✔
301
         SILE.process(content)
4✔
302
         SILE.call("par")
4✔
303
      end)
304
   end, "Typeset its contents in a left aligned block (keeping margins).")
157✔
305

306
   self:registerCommand("raggedleft", function (_, content)
306✔
307
      SILE.settings:temporarily(function ()
6✔
308
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
6✔
309
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
6✔
310
         SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
6✔
311
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
6✔
312
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
6✔
313
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
6✔
314
         SILE.process(content)
3✔
315
         SILE.call("par")
3✔
316
      end)
317
   end, "Typeset its contents in a right aligned block (keeping margins).")
156✔
318

319
   self:registerCommand("justified", function (_, content)
306✔
UNCOV
320
      SILE.settings:temporarily(function ()
×
UNCOV
321
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
×
UNCOV
322
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
×
323
         -- Keep the fixed part of the margins for nesting but remove the stretchability.
UNCOV
324
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
×
UNCOV
325
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
×
326
         -- Reset parfillskip to its default value, in case the surrounding context
327
         -- is ragged and cancelled it.
UNCOV
328
         SILE.settings:set("typesetter.parfillskip", nil, false, true)
×
UNCOV
329
         SILE.settings:set("document.spaceskip", nil)
×
UNCOV
330
         SILE.process(content)
×
UNCOV
331
         SILE.call("par")
×
332
      end)
333
   end, "Typeset its contents in a justified block (keeping margins).")
153✔
334

335
   self:registerCommand("ragged", function (options, content)
306✔
336
      -- Fairly dubious command for compatibility
337
      local l = SU.boolean(options.left, false)
1✔
338
      local r = SU.boolean(options.right, false)
1✔
339
      if l and r then
1✔
340
         SILE.call("center", {}, content)
2✔
341
      elseif r then
×
342
         SILE.call("raggedleft", {}, content)
×
343
      elseif l then
×
344
         SILE.call("raggedright", {}, content)
×
345
      else
346
         SILE.call("justified", {}, content)
×
347
      end
348
   end)
349

350
   self:registerCommand("rightalign", function (_, content)
306✔
351
      SU.deprecated("\\rightalign", "\\raggedleft", "0.15.0", "0.17.0")
×
352
      SILE.call("raggedleft", {}, content)
×
353
   end)
354

355
   self:registerCommand("blockquote", function (_, content)
306✔
356
      SILE.call("smallskip")
1✔
357
      SILE.typesetter:leaveHmode()
1✔
358
      SILE.settings:temporarily(function ()
2✔
359
         local indent = SILE.types.measurement("2em"):absolute()
2✔
360
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
2✔
361
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
2✔
362
         -- We keep the stretcheability of the lskip and rskip: honoring text alignment
363
         -- from the parent context.
364
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width + indent))
3✔
365
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width + indent))
3✔
366
         SILE.settings:set("font.size", SILE.settings:get("font.size") * 0.95)
2✔
367
         SILE.process(content)
1✔
368
         SILE.typesetter:leaveHmode()
1✔
369
      end)
370
      SILE.call("smallskip")
1✔
371
   end, "A blockquote environment")
154✔
372

373
   self:registerCommand("quote", function (_, content)
306✔
374
      SU.deprecated(
×
375
         "\\quote",
376
         "\\pullquote or \\blockquote",
377
         "0.14.5",
378
         "0.16.0",
379
         [[
×
380
            The \quote command has *such* bad output it is being completely deprecated as
381
            unsuitable for general purpose use. The pullquote package
382
            (\use[module=packages.pullquote]) provides one alternative, and the blockquote
383
            environment provides another. But you can also copy and adapt the original
384
            source from the plain class if you need to maintain exact output past
385
            SILE v0.16.0.
386
         ]]
×
387
      )
388
      SILE.call("smallskip")
×
389
      SILE.call("par")
×
390
      local margin = SILE.types.measurement(2.5, "em")
×
391
      SILE.settings:set("document.lskip", margin)
×
392
      SILE.settings:set("document.lskip", margin)
×
393
      SILE.call("font", { size = SILE.types.measurement(0.8, "em") }, function ()
×
394
         SILE.call("noindent")
×
395
         SILE.process(content)
×
396
      end)
397
      SILE.call("par")
×
398
      SILE.settings:set("document.lskip", nil)
×
399
      SILE.settings:set("document.rskip", nil)
×
400
      SILE.call("smallskip")
×
401
   end)
402

403
   self:registerCommand("listitem", function (_, content)
306✔
404
      SU.deprecated(
×
405
         "\\listitem",
406
         "\\item",
407
         "0.14.6",
408
         "0.16.0",
409
         [[
×
410
            The new list package (\use[module=packages.lists) has much better typography
411
            for lists. If you want to maintain the exact output of listitem past
412
            SILE v0.16.0 copy the source of \listitem from the plain class into your
413
            project.
414
         ]]
×
415
      )
416
      SILE.call("medskip")
×
417
      SILE.typesetter:typeset("• ")
×
418
      SILE.process(content)
×
419
      SILE.call("medskip")
×
420
   end)
421

422
   self:registerCommand("sloppy", function (_, _)
306✔
423
      SILE.settings:set("linebreak.tolerance", 9999)
×
424
   end)
425

426
   self:registerCommand("awful", function (_, _)
306✔
427
      SILE.settings:set("linebreak.tolerance", 10000)
×
428
   end)
429

430
   self:registerCommand("hbox", function (_, content)
306✔
431
      local hbox, hlist = SILE.typesetter:makeHbox(content)
65✔
432
      SILE.typesetter:pushHbox(hbox)
65✔
433
      if #hlist > 0 then
65✔
434
         SU.warn([[
×
435
            \\hbox has migrating content
436

437
            Ignored for now, but likely to break in future versions.
438
         ]])
×
439
         -- Ugly shim:
440
         -- One day we ought to do SILE.typesetter:pushHlist(hlist) here, so as to push
441
         -- back the migrating contents from within the hbox'ed content.
442
         -- However, old Lua code assumed the hbox to be returned, and sometimes removed it
443
         -- from the typesetter queue (for measuring, etc.), assuming it was the last
444
         -- element in the queue...
445
      end
446
      return hbox
65✔
447
   end, "Compiles all the enclosed horizontal-mode material into a single hbox")
153✔
448

449
   self:registerCommand("vbox", function (options, content)
306✔
450
      local vbox
451
      SILE.settings:temporarily(function ()
88✔
452
         if options.width then
44✔
453
            SILE.settings:set("typesetter.breakwidth", SILE.types.length(options.width))
×
454
         end
455
         SILE.typesetter:pushState()
44✔
456
         SILE.process(content)
44✔
457
         SILE.typesetter:leaveHmode(1)
44✔
458
         vbox = SILE.pagebuilder:collateVboxes(SILE.typesetter.state.outputQueue)
88✔
459
         SILE.typesetter:popState()
44✔
460
      end)
461
      return vbox
44✔
462
   end, "Compiles all the enclosed material into a single vbox")
153✔
463
end
464

465
return class
153✔
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

© 2025 Coveralls, Inc