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

sile-typesetter / sile / 11169993653

03 Oct 2024 09:26PM UTC coverage: 63.103% (+29.9%) from 33.23%
11169993653

push

github

web-flow
Merge pull request #2113 from Omikhleia/fix-ast-differences

46 of 57 new or added lines in 5 files covered. (80.7%)

88 existing lines in 10 files now uncovered.

11286 of 17885 relevant lines covered (63.1%)

3626.41 hits per line

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

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

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

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

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

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

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

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

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

76
function class:registerCommands ()
97✔
77
   SILE.classes.base.registerCommands(self)
97✔
78

79
   self:registerCommand("noindent", function (_, content)
194✔
80
      if #SILE.typesetter.state.nodes ~= 0 then
61✔
81
         SU.warn([[
2✔
82
            \noindent was called after paragraph content has already been processed
83

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

97
   self:registerCommand("neverindent", function (_, content)
194✔
98
      SILE.settings:set("current.parindent", SILE.types.node.glue())
64✔
99
      SILE.settings:set("document.parindent", SILE.types.node.glue())
64✔
100
      SILE.process(content)
32✔
101
   end, "Turn off all indentation")
129✔
102

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

108
   for k, _ in pairs(skips) do
388✔
109
      self:registerCommand(k .. "skip", function (_, _)
582✔
110
         SILE.typesetter:leaveHmode()
36✔
111
         SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
72✔
112
      end, "Skip vertically by a " .. k .. " amount")
618✔
113
   end
114

115
   self:registerCommand("hfill", function (_, _)
194✔
116
      SILE.typesetter:pushExplicitGlue(SILE.types.node.hfillglue())
24✔
117
   end, "Add a huge horizontal glue")
109✔
118

119
   self:registerCommand("vfill", function (_, _)
194✔
120
      SILE.typesetter:leaveHmode()
207✔
121
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vfillglue())
414✔
122
   end, "Add huge vertical glue")
304✔
123

124
   self:registerCommand("hss", function (_, _)
194✔
125
      SILE.typesetter:pushGlue(SILE.types.node.hssglue())
×
126
      table.insert(SILE.typesetter.state.nodes, SILE.types.node.zerohbox())
×
127
   end, "Add glue which stretches and shrinks horizontally (good for centering)")
97✔
128

129
   self:registerCommand("vss", function (_, _)
194✔
130
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vssglue())
×
131
   end, "Add glue which stretches and shrinks vertically")
97✔
132

133
   local _thinspacewidth = SILE.types.measurement(0.16667, "em")
97✔
134

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

139
   self:registerCommand("negthinspace", function (_, _)
194✔
140
      SILE.call("glue", { width = -_thinspacewidth })
×
141
   end)
142

143
   self:registerCommand("enspace", function (_, _)
194✔
144
      SILE.call("glue", { width = SILE.types.measurement(1, "en") })
×
145
   end)
146

147
   self:registerCommand("relax", function (_, _) end)
97✔
148

149
   self:registerCommand("enskip", function (_, _)
194✔
150
      SILE.call("enspace")
×
151
   end)
152

153
   local _quadwidth = SILE.types.measurement(1, "em")
97✔
154

155
   self:registerCommand("quad", function (_, _)
194✔
UNCOV
156
      SILE.call("glue", { width = _quadwidth })
×
157
   end)
158

159
   self:registerCommand("qquad", function (_, _)
194✔
160
      SILE.call("glue", { width = _quadwidth * 2 })
24✔
161
   end)
162

163
   self:registerCommand("slash", function (_, _)
194✔
164
      SILE.typesetter:typeset("/")
×
165
      SILE.call("penalty", { penalty = 50 })
×
166
   end)
167

168
   self:registerCommand("break", function (_, _)
194✔
169
      SILE.call("penalty", { penalty = -10000 })
14✔
170
   end, "Requests a frame break (if in vertical mode) or a line break (if in horizontal mode)")
111✔
171

172
   self:registerCommand("cr", function (_, _)
194✔
173
      SILE.call("hfill")
2✔
174
      SILE.call("break")
2✔
175
   end, "Fills a line with a stretchable glue and then requests a line break")
99✔
176

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

183
   self:registerCommand("framebreak", function (_, _)
194✔
184
      if not SILE.typesetter:vmode() then
8✔
185
         SU.warn([[
×
186
            \\framebreak was not intended to work in horizontal mode
187

188
            Behavior may change in future versions.
189
         ]])
×
190
      end
191
      SILE.call("penalty", { penalty = -10000, vertical = true })
4✔
192
   end, "Requests a frame break (switching to vertical mode if needed)")
101✔
193

194
   self:registerCommand("pagebreak", function (_, _)
194✔
195
      if not SILE.typesetter:vmode() then
8✔
196
         SU.warn([[
×
197
            \\pagebreak was not intended to work in horizontal mode
198

199
            Behavior may change in future versions.
200
         ]])
×
201
      end
202
      SILE.call("penalty", { penalty = -20000, vertical = true })
4✔
203
   end, "Requests a non-negotiable page break (switching to vertical mode if needed)")
101✔
204

205
   self:registerCommand("nobreak", function (_, _)
194✔
206
      SILE.call("penalty", { penalty = 10000 })
×
207
   end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
97✔
208

209
   self:registerCommand("novbreak", function (_, _)
194✔
210
      SILE.call("penalty", { penalty = 10000, vertical = true })
18✔
211
   end, "Inhibits a frame break (switching to vertical mode if needed)")
115✔
212

213
   self:registerCommand(
194✔
214
      "allowbreak",
97✔
215
      function (_, _)
216
         SILE.call("penalty", { penalty = 0 })
×
217
      end,
218
      "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"
219
   )
97✔
220

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

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

236
   self:registerCommand("eject", function (_, _)
194✔
237
      SILE.call("vfill")
3✔
238
      SILE.call("break")
3✔
239
   end, "Fills the page with stretchable vglue and then request a page break")
100✔
240

241
   self:registerCommand("supereject", function (_, _)
194✔
242
      SILE.call("vfill")
107✔
243
      SILE.call("penalty", { penalty = -20000 })
107✔
244
   end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
204✔
245

246
   self:registerCommand("em", function (_, content)
194✔
247
      local style = SILE.settings:get("font.style")
57✔
248
      local toggle = (style and style:lower() == "italic") and "Regular" or "Italic"
114✔
249
      SILE.call("font", { style = toggle }, content)
57✔
250
   end, "Emphasizes its contents by switching the font style to italic (or back to regular if already italic)")
154✔
251

252
   self:registerCommand("strong", function (_, content)
194✔
253
      SILE.call("font", { weight = 700 }, content)
×
254
   end, "Sets the font weight to bold (700)")
97✔
255

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

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

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

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

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

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

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

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

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

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

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

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

424
   self:registerCommand("sloppy", function (_, _)
194✔
425
      SILE.settings:set("linebreak.tolerance", 9999)
×
426
   end)
427

428
   self:registerCommand("awful", function (_, _)
194✔
429
      SILE.settings:set("linebreak.tolerance", 10000)
×
430
   end)
431

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

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

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

467
return class
97✔
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