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

sile-typesetter / sile / 4551497499

pending completion
4551497499

push

github

GitHub
Merge bbf07093e into 71e91a38f

122 of 122 new or added lines in 20 files covered. (100.0%)

11619 of 15603 relevant lines covered (74.47%)

6844.75 hits per line

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

85.39
/classes/plain.lua
1
local base = require("classes.base")
167✔
2

3
local class = pl.class(base)
167✔
4
class._name = "plain"
167✔
5

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

28
local skips = {
167✔
29
  small = "3pt plus 1pt minus 1pt",
30
  med = "6pt plus 2pt minus 2pt",
31
  big = "12pt plus 4pt minus 4pt"
×
32
}
33

34
function class:_init (options)
167✔
35
  base._init(self, options)
167✔
36
  self:loadPackage("bidi")
167✔
37
  self:loadPackage("folio")
167✔
38
end
39

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

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

61
function class:declareSettings ()
167✔
62
  base.declareSettings(self)
167✔
63
  for k, v in pairs(skips) do
668✔
64
    SILE.settings:declare({
1,002✔
65
        parameter = "plain." .. k .. "skipamount",
501✔
66
        type = "vglue",
67
        default = SILE.nodefactory.vglue(v),
1,002✔
68
        help = "The amount of a \\" .. k .. "skip"
501✔
69
      })
70
  end
71
end
72

73
function class:registerCommands ()
167✔
74

75
  SILE.classes.base.registerCommands(self)
167✔
76

77
  self:registerCommand("noindent", function (_, content)
334✔
78
    if #SILE.typesetter.state.nodes ~= 0 then
94✔
79
      SU.warn("\\noindent called after nodes already recieved in a paragraph, the setting will have no effect because the parindent (if any) has already been output")
14✔
80
    end
81
    SILE.settings:set("current.parindent", SILE.nodefactory.glue())
188✔
82
    SILE.process(content)
94✔
83
  end, "Do not add an indent to the start of this paragraph")
261✔
84

85
  self:registerCommand("neverindent", function (_, content)
334✔
86
    SILE.settings:set("current.parindent", SILE.nodefactory.glue())
96✔
87
    SILE.settings:set("document.parindent", SILE.nodefactory.glue())
96✔
88
    SILE.process(content)
48✔
89
  end, "Turn off all indentation")
215✔
90

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

96
  for k, _ in pairs(skips) do
668✔
97
    self:registerCommand(k .. "skip", function (_, _)
1,002✔
98
      SILE.typesetter:leaveHmode()
53✔
99
      SILE.typesetter:pushExplicitVglue(SILE.settings:get("plain." .. k .. "skipamount"))
106✔
100
    end, "Skip vertically by a " .. k .. " amount")
1,055✔
101
  end
102

103
  self:registerCommand("hfill", function (_, _)
334✔
104
    SILE.typesetter:pushExplicitGlue(SILE.nodefactory.hfillglue())
50✔
105
  end, "Add a huge horizontal glue")
192✔
106

107
  self:registerCommand("vfill", function (_, _)
334✔
108
    SILE.typesetter:leaveHmode()
350✔
109
    SILE.typesetter:pushExplicitVglue(SILE.nodefactory.vfillglue())
700✔
110
  end, "Add huge vertical glue")
517✔
111

112
  self:registerCommand("hss", function (_, _)
334✔
113
    SILE.typesetter:initline()
×
114
    SILE.typesetter:pushGlue(SILE.nodefactory.hssglue())
×
115
    table.insert(SILE.typesetter.state.nodes, SILE.nodefactory.zerohbox())
×
116
  end, "Add glue which stretches and shrinks horizontally (good for centering)")
167✔
117

118
  self:registerCommand("vss", function (_, _)
334✔
119
    SILE.typesetter:pushExplicitVglue(SILE.nodefactory.vssglue())
×
120
  end, "Add glue which stretches and shrinks vertically")
167✔
121

122
  local _thinspacewidth = SILE.measurement(0.16667, "em")
167✔
123

124
  self:registerCommand("thinspace", function (_, _)
334✔
125
    SILE.call("glue", { width = _thinspacewidth })
×
126
  end)
127

128
  self:registerCommand("negthinspace", function (_, _)
334✔
129
    SILE.call("glue", { width = -_thinspacewidth })
×
130
  end)
131

132
  self:registerCommand("enspace", function (_, _)
334✔
133
    SILE.call("glue", { width = SILE.measurement(1, "en") })
×
134
  end)
135

136
  self:registerCommand("relax", function (_, _)
334✔
137
  end)
138

139
  self:registerCommand("enskip", function (_, _)
334✔
140
    SILE.call("enspace")
×
141
  end)
142

143
  local _quadwidth = SILE.measurement(1, "em")
167✔
144

145
  self:registerCommand("quad", function (_, _)
334✔
146
    SILE.call("glue", { width = _quadwidth })
1✔
147
  end)
148

149
  self:registerCommand("qquad", function (_, _)
334✔
150
    SILE.call("glue", { width = _quadwidth * 2 })
92✔
151
  end)
152

153
  self:registerCommand("slash", function (_, _)
334✔
154
    SILE.typesetter:typeset("/")
×
155
    SILE.call("penalty", { penalty = 50 })
×
156
  end)
157

158
  self:registerCommand("break", function (_, _)
334✔
159
    SILE.call("penalty", { penalty = -10000 })
35✔
160
  end, "Requests a frame break (if in vertical mode) or a line break (if in horizontal mode)")
202✔
161

162
  self:registerCommand("cr", function (_, _)
334✔
163
    SILE.call("hfill")
3✔
164
    SILE.call("break")
3✔
165
  end, "Fills a line with a stretchable glue and then requests a line break")
170✔
166

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

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

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

187
  self:registerCommand("nobreak", function (_, _)
334✔
188
    SILE.call("penalty", { penalty = 10000 })
×
189
  end, "Inhibits a frame break (if in vertical mode) or a line break (if in horizontal mode)")
167✔
190

191
  self:registerCommand("novbreak", function (_, _)
334✔
192
    SILE.call("penalty", { penalty = 10000, vertical = true })
7✔
193
  end, "Inhibits a frame break (switching to vertical mode if needed)")
174✔
194

195
  self:registerCommand("allowbreak", function (_, _)
334✔
196
    SILE.call("penalty", { penalty = 0 })
×
197
  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")
167✔
198

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

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

214
  self:registerCommand("eject", function (_, _)
334✔
215
    SILE.call("vfill")
3✔
216
    SILE.call("break")
3✔
217
  end, "Fills the page with stretchable vglue and then request a page break")
170✔
218

219
  self:registerCommand("supereject", function (_, _)
334✔
220
    SILE.call("vfill")
177✔
221
    SILE.call("penalty", { penalty = -20000 })
177✔
222
  end, "Fills the page with stretchable vglue and then requests a non-negotiable page break")
344✔
223

224
  self:registerCommand("justified", function (_, content)
334✔
225
    SILE.settings:set("document.rskip", nil)
1✔
226
    SILE.settings:set("document.spaceskip", nil)
1✔
227
    SILE.process(content)
1✔
228
    SILE.call("par")
1✔
229
  end)
230

231
  self:registerCommand("rightalign", function (_, content)
334✔
232
    SILE.call("raggedleft", {}, function ()
8✔
233
      SILE.process(content)
4✔
234
      SILE.call("par")
4✔
235
    end)
236
  end)
237

238
  self:registerCommand("em", function (_, content)
334✔
239
    SILE.call("font", { style = "Italic" }, content)
37✔
240
  end)
241

242
  self:registerCommand("strong", function (_, content)
334✔
243
    SILE.call("font", { weight = 700 }, content)
×
244
  end)
245

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

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

263
  self:registerCommand("raggedright", function (_, content)
334✔
264
    SILE.call("ragged", { right = true }, content)
7✔
265
  end)
266

267
  self:registerCommand("raggedleft", function (_, content)
334✔
268
    SILE.call("ragged", { left = true }, content)
6✔
269
  end)
270

271
  self:registerCommand("quote", function (_, content)
334✔
272
    SU.deprecated("\\quote", "\\pullquote", "0.14.5", "0.16.0", [[
2✔
273
  The \quote command has *such* bad output it is being completely
274
  deprecated as unsuitable for general purpose use. The pullquote
275
  package (\use[module=packages.pullquote]) provides one alternative,
276
  but you can also copy and adapt the original source from the plain
277
  class if you need to maintain exact output past SILE v0.16.0.]])
1✔
278
    SILE.call("smallskip")
1✔
279
    SILE.call("par")
1✔
280
    local margin = SILE.measurement(2.5, "em")
1✔
281
    SILE.settings:set("document.lskip", margin)
1✔
282
    SILE.settings:set("document.lskip", margin)
1✔
283
    SILE.call("font", { size = SILE.measurement(0.8, "em") }, function ()
3✔
284
      SILE.call("noindent")
1✔
285
      SILE.process(content)
1✔
286
    end)
287
    SILE.call("par")
1✔
288
    SILE.settings:set("document.lskip", nil)
1✔
289
    SILE.settings:set("document.rskip", nil)
1✔
290
    SILE.call("smallskip")
1✔
291
  end)
292

293
  self:registerCommand("listitem", function (_, content)
334✔
294
    SU.deprecated("\\listitem", "\\item", "0.14.6", "0.16.0", [[
×
295
  The new list package (\use[module=packages.lists) has much better
296
  typography for lists. If you want to maintain the exact output of listitem
297
  past SILE v0.16.0 copy the source of \listitem from the plain class into
298
  your project.]])
×
299
    SILE.call("medskip")
×
300
    SILE.typesetter:typeset("• ")
×
301
    SILE.process(content)
×
302
    SILE.call("medskip")
×
303
  end)
304

305
  self:registerCommand("sloppy", function (_, _)
334✔
306
    SILE.settings:set("linebreak.tolerance", 9999)
×
307
  end)
308

309
  self:registerCommand("awful", function (_, _)
334✔
310
    SILE.settings:set("linebreak.tolerance", 10000)
×
311
  end)
312

313
  self:registerCommand("center", function (_, content)
334✔
314
    if #SILE.typesetter.state.nodes ~= 0 then
124✔
315
      SU.warn("\\center environment started after other nodes in a paragraph, may not center as expected")
×
316
    end
317
    SILE.settings:temporarily(function()
248✔
318
      SILE.settings:set("current.parindent", 0)
124✔
319
      SILE.settings:set("document.parindent", 0)
124✔
320
      SILE.call("ragged", { left = true, right = true }, content)
124✔
321
    end)
322
  end)
323

324
  self:registerCommand("ragged", function (options, content)
334✔
325
    SILE.settings:temporarily(function ()
276✔
326
      if SU.boolean(options.left, false) then SILE.settings:set("document.lskip", SILE.nodefactory.hfillglue()) end
407✔
327
      if SU.boolean(options.right, false) then SILE.settings:set("document.rskip", SILE.nodefactory.hfillglue()) end
408✔
328
      SILE.settings:set("typesetter.parfillskip", SILE.nodefactory.glue())
276✔
329
      SILE.settings:set("document.parindent", SILE.nodefactory.glue())
276✔
330
      SILE.settings:set("document.spaceskip", SILE.length("1spc", 0, 0))
276✔
331
      SILE.process(content)
138✔
332
      SILE.call("par")
138✔
333
    end)
334
  end)
335

336
  self:registerCommand("hbox", function (_, content)
334✔
337
    local hbox, hlist = SILE.typesetter:makeHbox(content)
65✔
338
    -- HACK
339
    -- Direct insertion in the typesetter node queue comes from
340
    -- the original implementation.
341
    -- It would likely be clearer to use: SILE.typesetter:pushHbox(hbox)
342
    -- but the latter adds a zerohbox sometimes (on initline), so it will
343
    -- break some non-regression test and possibly have some effect at
344
    -- places... For now, therefore, keep that unchanged, but it should
345
    -- be investigated.
346
    table.insert(SILE.typesetter.state.nodes, hbox)
65✔
347

348
    if #hlist > 0 then
65✔
349
      SU.warn("Hbox has migrating content (ignored for now, but likely to break in future versions)")
×
350
      -- Ugly shim:
351
      -- One day we ought to do SILE.typesetter:pushHlist(hlist) here, so as to push
352
      -- back the migrating contents from within the hbox'ed content.
353
      -- However, old Lua code assumed the hbox to be returned, and sometimes removed it
354
      -- from the typesetter queue (for measuring, etc.), assuming it was the last
355
      -- element in the queue...
356
    end
357
    return hbox
65✔
358
  end, "Compiles all the enclosed horizontal-mode material into a single hbox")
167✔
359

360
  self:registerCommand("vbox", function (options, content)
334✔
361
    local vbox
362
    SILE.settings:temporarily(function ()
92✔
363
      if options.width then SILE.settings:set("typesetter.breakwidth", SILE.length(options.width)) end
46✔
364
      SILE.typesetter:pushState()
46✔
365
      SILE.process(content)
46✔
366
      SILE.typesetter:leaveHmode(1)
46✔
367
      vbox = SILE.pagebuilder:collateVboxes(SILE.typesetter.state.outputQueue)
92✔
368
      SILE.typesetter:popState()
46✔
369
    end)
370
    return vbox
46✔
371
  end, "Compiles all the enclosed material into a single vbox")
167✔
372

373
end
374

375
return class
167✔
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