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

sile-typesetter / sile / 9428435077

08 Jun 2024 11:35AM UTC coverage: 64.56% (-9.9%) from 74.46%
9428435077

push

github

web-flow
Merge pull request #2047 from alerque/end-pars

23 of 46 new or added lines in 5 files covered. (50.0%)

1684 existing lines in 60 files now uncovered.

11145 of 17263 relevant lines covered (64.56%)

4562.45 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

103
   self:registerCommand("indent", function (_, content)
256✔
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")
132✔
107

108
   for k, _ in pairs(skips) do
512✔
109
      self:registerCommand(k .. "skip", function (_, _)
768✔
110
         SILE.typesetter:leaveHmode()
43✔
111
         SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
86✔
112
      end, "Skip vertically by a " .. k .. " amount")
811✔
113
   end
114

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

119
   self:registerCommand("vfill", function (_, _)
256✔
120
      SILE.typesetter:leaveHmode()
269✔
121
      SILE.typesetter:pushExplicitVglue(SILE.types.node.vfillglue())
538✔
122
   end, "Add huge vertical glue")
397✔
123

124
   self:registerCommand("hss", function (_, _)
256✔
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)")
128✔
128

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

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

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

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

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

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

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

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

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

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

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

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

172
   self:registerCommand("cr", function (_, _)
256✔
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")
130✔
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 (_, _)
256✔
184
      if not SILE.typesetter:vmode() then
22✔
185
         SU.warn("framebreak was not intended to work in horizontal mode. Behaviour may change in future versions")
×
186
      end
187
      SILE.call("penalty", { penalty = -10000, vertical = true })
11✔
188
   end, "Requests a frame break (switching to vertical mode if needed)")
139✔
189

190
   self:registerCommand("pagebreak", function (_, _)
256✔
191
      if not SILE.typesetter:vmode() then
10✔
192
         SU.warn("pagebreak was not intended to work in horizontal mode. Behaviour may change in future versions")
×
193
      end
194
      SILE.call("penalty", { penalty = -20000, vertical = true })
5✔
195
   end, "Requests a non-negotiable page break (switching to vertical mode if needed)")
133✔
196

197
   self:registerCommand("nobreak", function (_, _)
256✔
198
      SILE.call("penalty", { penalty = 10000 })
×
199
   end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
128✔
200

201
   self:registerCommand("novbreak", function (_, _)
256✔
202
      SILE.call("penalty", { penalty = 10000, vertical = true })
21✔
203
   end, "Inhibits a frame break (switching to vertical mode if needed)")
149✔
204

205
   self:registerCommand(
256✔
206
      "allowbreak",
128✔
207
      function (_, _)
208
         SILE.call("penalty", { penalty = 0 })
×
209
      end,
210
      "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"
211
   )
128✔
212

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

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

228
   self:registerCommand("eject", function (_, _)
256✔
229
      SILE.call("vfill")
3✔
230
      SILE.call("break")
3✔
231
   end, "Fills the page with stretchable vglue and then request a page break")
131✔
232

233
   self:registerCommand("supereject", function (_, _)
256✔
234
      SILE.call("vfill")
138✔
235
      SILE.call("penalty", { penalty = -20000 })
138✔
236
   end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
266✔
237

238
   self:registerCommand("em", function (_, content)
256✔
239
      local style = SILE.settings:get("font.style")
66✔
240
      local toggle = (style and style:lower() == "italic") and "Regular" or "Italic"
132✔
241
      SILE.call("font", { style = toggle }, content)
66✔
242
   end, "Emphasizes its contents by switching the font style to italic (or back to regular if already italic)")
194✔
243

244
   self:registerCommand("strong", function (_, content)
256✔
245
      SILE.call("font", { weight = 700 }, content)
×
246
   end, "Sets the font weight to bold (700)")
128✔
247

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

261
   self:registerCommand("nohyphenation", function (_, content)
256✔
262
      SILE.call("font", { language = "und" }, content)
×
263
   end)
264

265
   self:registerCommand("center", function (_, content)
256✔
266
      if #SILE.typesetter.state.nodes ~= 0 then
28✔
267
         SU.warn("\\center environment started after other nodes in a paragraph, may not center as expected")
×
268
      end
269
      SILE.settings:temporarily(function ()
56✔
270
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
56✔
271
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
56✔
272
         SILE.settings:set("document.parindent", SILE.types.node.glue())
56✔
273
         SILE.settings:set("current.parindent", SILE.types.node.glue())
56✔
274
         SILE.settings:set("document.lskip", SILE.types.node.hfillglue(lskip.width.length))
56✔
275
         SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
56✔
276
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
56✔
277
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
56✔
278
         SILE.process(content)
28✔
279
         SILE.call("par")
28✔
280
      end)
281
   end, "Typeset its contents in a centered block (keeping margins).")
156✔
282

283
   self:registerCommand("raggedright", function (_, content)
256✔
284
      SILE.settings:temporarily(function ()
10✔
285
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
10✔
286
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
10✔
287
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
10✔
288
         SILE.settings:set("document.rskip", SILE.types.node.hfillglue(rskip.width.length))
10✔
289
         SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
10✔
290
         SILE.settings:set("document.spaceskip", SILE.types.length("1spc", 0, 0))
10✔
291
         SILE.process(content)
5✔
292
         SILE.call("par")
5✔
293
      end)
294
   end, "Typeset its contents in a left aligned block (keeping margins).")
133✔
295

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

309
   self:registerCommand("justified", function (_, content)
256✔
310
      SILE.settings:temporarily(function ()
2✔
311
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
2✔
312
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
2✔
313
         -- Keep the fixed part of the margins for nesting but remove the stretchability.
314
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width.length))
2✔
315
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width.length))
2✔
316
         -- Reset parfillskip to its default value, in case the surrounding context
317
         -- is ragged and cancelled it.
318
         SILE.settings:set("typesetter.parfillskip", nil, false, true)
1✔
319
         SILE.settings:set("document.spaceskip", nil)
1✔
320
         SILE.process(content)
1✔
321
         SILE.call("par")
1✔
322
      end)
323
   end, "Typeset its contents in a justified block (keeping margins).")
129✔
324

325
   self:registerCommand("ragged", function (options, content)
256✔
326
      -- Fairly dubious command for compatibility
327
      local l = SU.boolean(options.left, false)
1✔
328
      local r = SU.boolean(options.right, false)
1✔
329
      if l and r then
1✔
330
         SILE.call("center", {}, content)
2✔
331
      elseif r then
×
332
         SILE.call("raggedleft", {}, content)
×
333
      elseif l then
×
334
         SILE.call("raggedright", {}, content)
×
335
      else
336
         SILE.call("justified", {}, content)
×
337
      end
338
   end)
339

340
   self:registerCommand("rightalign", function (_, content)
256✔
341
      SU.deprecated("\\rightalign", "\\raggedleft", "0.15.0", "0.17.0")
×
342
      SILE.call("raggedleft", {}, content)
×
343
   end)
344

345
   self:registerCommand("blockquote", function (_, content)
256✔
346
      SILE.call("smallskip")
1✔
347
      SILE.typesetter:leaveHmode()
1✔
348
      SILE.settings:temporarily(function ()
2✔
349
         local indent = SILE.types.measurement("2em"):absolute()
2✔
350
         local lskip = SILE.settings:get("document.lskip") or SILE.types.node.glue()
2✔
351
         local rskip = SILE.settings:get("document.rskip") or SILE.types.node.glue()
2✔
352
         -- We keep the stretcheability of the lskip and rskip: honoring text alignment
353
         -- from the parent context.
354
         SILE.settings:set("document.lskip", SILE.types.node.glue(lskip.width + indent))
3✔
355
         SILE.settings:set("document.rskip", SILE.types.node.glue(rskip.width + indent))
3✔
356
         SILE.settings:set("font.size", SILE.settings:get("font.size") * 0.95)
2✔
357
         SILE.process(content)
1✔
358
         SILE.typesetter:leaveHmode()
1✔
359
      end)
360
      SILE.call("smallskip")
1✔
361
   end, "A blockquote environment")
129✔
362

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

392
   self:registerCommand("listitem", function (_, content)
256✔
393
      SU.deprecated(
×
394
         "\\listitem",
395
         "\\item",
396
         "0.14.6",
397
         "0.16.0",
398
         [[
×
399
  The new list package (\use[module=packages.lists) has much better
400
  typography for lists. If you want to maintain the exact output of listitem
401
  past SILE v0.16.0 copy the source of \listitem from the plain class into
402
  your project.]]
×
403
      )
404
      SILE.call("medskip")
×
405
      SILE.typesetter:typeset("• ")
×
406
      SILE.process(content)
×
407
      SILE.call("medskip")
×
408
   end)
409

410
   self:registerCommand("sloppy", function (_, _)
256✔
411
      SILE.settings:set("linebreak.tolerance", 9999)
×
412
   end)
413

414
   self:registerCommand("awful", function (_, _)
256✔
415
      SILE.settings:set("linebreak.tolerance", 10000)
×
416
   end)
417

418
   self:registerCommand("hbox", function (_, content)
256✔
419
      local hbox, hlist = SILE.typesetter:makeHbox(content)
59✔
420
      SILE.typesetter:pushHbox(hbox)
59✔
421
      if #hlist > 0 then
59✔
422
         SU.warn("Hbox has migrating content (ignored for now, but likely to break in future versions)")
×
423
         -- Ugly shim:
424
         -- One day we ought to do SILE.typesetter:pushHlist(hlist) here, so as to push
425
         -- back the migrating contents from within the hbox'ed content.
426
         -- However, old Lua code assumed the hbox to be returned, and sometimes removed it
427
         -- from the typesetter queue (for measuring, etc.), assuming it was the last
428
         -- element in the queue...
429
      end
430
      return hbox
59✔
431
   end, "Compiles all the enclosed horizontal-mode material into a single hbox")
128✔
432

433
   self:registerCommand("vbox", function (options, content)
256✔
434
      local vbox
435
      SILE.settings:temporarily(function ()
44✔
436
         if options.width then
22✔
437
            SILE.settings:set("typesetter.breakwidth", SILE.types.length(options.width))
×
438
         end
439
         SILE.typesetter:pushState()
22✔
440
         SILE.process(content)
22✔
441
         SILE.typesetter:leaveHmode(1)
22✔
442
         vbox = SILE.pagebuilder:collateVboxes(SILE.typesetter.state.outputQueue)
44✔
443
         SILE.typesetter:popState()
22✔
444
      end)
445
      return vbox
22✔
446
   end, "Compiles all the enclosed material into a single vbox")
128✔
447
end
448

449
return class
128✔
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