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

sile-typesetter / sile / 11534409649

26 Oct 2024 07:27PM UTC coverage: 33.196% (-28.7%) from 61.897%
11534409649

push

github

alerque
chore(tooling): Update editor-config key for stylua as accepted upstream

Our setting addition is still not in a tagged release, but the PR was
accepted into the default branch of stylua. This means you no longer
need to run my fork of Stylua to get this project's style, you just nead
any build from the main development branch. However the config key was
renamed as part of the acceptance, so this is the relevant adjustment.

5810 of 17502 relevant lines covered (33.2%)

1300.57 hits per line

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

73.28
/types/node.lua
1
--- SILE node type.
2
-- @types node
3

4
local nodetypes = {}
17✔
5

6
-- This infinity needs to be smaller than an actual infinity but bigger than the infinite stretch
7
-- added by the typesetter. See https://github.com/sile-typesetter/sile/issues/227
8
local infinity = SILE.types.measurement(1e13)
34✔
9

10
local function _maxnode (nodes, dim)
11
   local dims = SU.map(function (node)
6,098✔
12
      -- TODO there is a bug here because we shouldn't need to cast to lengths,
13
      -- somebody is setting a height as a number value (test in Lua 5.1)
14
      -- return node[dim]
15
      return SU.cast("length", node[dim])
6,890✔
16
   end, nodes)
3,049✔
17
   return SU.max(SILE.types.length(0), pl.utils.unpack(dims))
9,147✔
18
end
19

20
local _dims = pl.Set({ "width", "height", "depth" })
34✔
21

22
nodetypes.box = pl.class()
34✔
23
nodetypes.box.type = "special"
17✔
24

25
nodetypes.box.height = nil
17✔
26
nodetypes.box.depth = nil
17✔
27
nodetypes.box.width = nil
17✔
28
nodetypes.box.misfit = false
17✔
29
nodetypes.box.explicit = false
17✔
30
nodetypes.box.discardable = false
17✔
31
nodetypes.box.value = nil
17✔
32
nodetypes.box._default_length = "width"
17✔
33

34
function nodetypes.box:_init (spec)
34✔
35
   if
36
      type(spec) == "string"
4,621✔
37
      or type(spec) == "number"
4,485✔
38
      or SU.type(spec) == "measurement"
8,690✔
39
      or SU.type(spec) == "length"
8,680✔
40
   then
41
      self[self._default_length] = SU.cast("length", spec)
1,510✔
42
   elseif SU.type(spec) == "table" then
7,732✔
43
      if spec._tospec then
3,343✔
44
         spec = spec:_tospec()
×
45
      end
46
      for k, v in pairs(spec) do
17,624✔
47
         self[k] = _dims[k] and SU.cast("length", v) or v
18,441✔
48
      end
49
   elseif type(spec) ~= "nil" and SU.type(spec) ~= self.type then
523✔
50
      SU.error("Unimplemented, creating " .. self.type .. " node from " .. SU.type(spec), 1)
×
51
   end
52
   for dim in pairs(_dims) do
18,484✔
53
      if not self[dim] then
14,130✔
54
         self[dim] = SILE.types.length()
17,896✔
55
      end
56
   end
57
   self["is_" .. self.type] = true
4,621✔
58
   self.is_box = self.is_hbox or self.is_vbox or self.is_zerohbox or self.is_alternative or self.is_nnode
5,066✔
59
   self.is_zero = self.is_zerohbox or self.is_zerovglue
4,799✔
60
   if self.is_migrating then
4,710✔
61
      self.is_hbox, self.is_box = true, true
2✔
62
   end
63
end
64

65
-- De-init instances by shallow copying properties and removing meta table
66
function nodetypes.box:_tospec ()
34✔
67
   return pl.tablex.copy(self)
64✔
68
end
69

70
function nodetypes.box:tostring ()
34✔
71
   return self:__tostring()
×
72
end
73

74
function nodetypes.box:__tostring ()
34✔
75
   return self.type
×
76
end
77

78
function nodetypes.box.__concat (a, b)
34✔
79
   return tostring(a) .. tostring(b)
×
80
end
81

82
function nodetypes.box:absolute ()
34✔
83
   local clone = nodetypes[self.type](self:_tospec())
128✔
84
   for dim in pairs(_dims) do
256✔
85
      clone[dim] = self[dim]:absolute()
384✔
86
   end
87
   if self.nodes then
64✔
88
      clone.nodes = pl.tablex.map_named_method("absolute", self.nodes)
×
89
   end
90
   return clone
64✔
91
end
92

93
function nodetypes.box:lineContribution ()
34✔
94
   -- Regardless of the orientations, "width" is always in the
95
   -- writingDirection, and "height" is always in the "pageDirection"
96
   return self.misfit and self.height or self.width
2,960✔
97
end
98

99
function nodetypes.box:outputYourself ()
34✔
100
   SU.error(self.type .. " with no output routine")
×
101
end
102

103
function nodetypes.box:toText ()
34✔
104
   return self.type
×
105
end
106

107
function nodetypes.box:isBox ()
34✔
108
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
109
   return self.type == "hbox"
×
110
      or self.type == "zerohbox"
×
111
      or self.type == "alternative"
×
112
      or self.type == "nnode"
×
113
      or self.type == "vbox"
×
114
end
115

116
function nodetypes.box:isNnode ()
34✔
117
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
118
   return self.type == "nnode"
×
119
end
120

121
function nodetypes.box:isGlue ()
34✔
122
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
123
   return self.type == "glue"
×
124
end
125

126
function nodetypes.box:isVglue ()
34✔
127
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
128
   return self.type == "vglue"
×
129
end
130

131
function nodetypes.box:isZero ()
34✔
132
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
133
   return self.type == "zerohbox" or self.type == "zerovglue"
×
134
end
135

136
function nodetypes.box:isUnshaped ()
34✔
137
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
138
   return self.type == "unshaped"
×
139
end
140

141
function nodetypes.box:isAlternative ()
34✔
142
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
143
   return self.type == "alternative"
×
144
end
145

146
function nodetypes.box:isVbox ()
34✔
147
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
148
   return self.type == "vbox"
×
149
end
150

151
function nodetypes.box:isInsertion ()
34✔
152
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
153
   return self.type == "insertion"
×
154
end
155

156
function nodetypes.box:isMigrating ()
34✔
157
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
158
   return self.migrating
×
159
end
160

161
function nodetypes.box:isPenalty ()
34✔
162
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
163
   return self.type == "penalty"
×
164
end
165

166
function nodetypes.box:isDiscretionary ()
34✔
167
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
168
   return self.type == "discretionary"
×
169
end
170

171
function nodetypes.box:isKern ()
34✔
172
   SU.warn("Deprecated function, please use boolean is_<type> property to check types", true)
×
173
   return self.type == "kern"
×
174
end
175

176
nodetypes.hbox = pl.class(nodetypes.box)
34✔
177
nodetypes.hbox.type = "hbox"
17✔
178

179
function nodetypes.hbox:_init (spec)
34✔
180
   nodetypes.box._init(self, spec)
3,391✔
181
end
182

183
function nodetypes.hbox:__tostring ()
34✔
184
   return "H<" .. tostring(self.width) .. ">^" .. tostring(self.height) .. "-" .. tostring(self.depth) .. "v"
×
185
end
186

187
function nodetypes.hbox:scaledWidth (line)
34✔
188
   return SU.rationWidth(self:lineContribution(), self.width, line.ratio)
1,644✔
189
end
190

191
function nodetypes.hbox:outputYourself (typesetter, line)
34✔
192
   local outputWidth = self:scaledWidth(line)
811✔
193
   if not self.value.glyphString then
811✔
194
      return
263✔
195
   end
196
   if typesetter.frame:writingDirection() == "RTL" then
1,096✔
197
      typesetter.frame:advanceWritingDirection(outputWidth)
60✔
198
   end
199
   SILE.outputter:setCursor(typesetter.frame.state.cursorX, typesetter.frame.state.cursorY)
548✔
200
   SILE.outputter:setFont(self.value.options)
548✔
201
   SILE.outputter:drawHbox(self.value, outputWidth)
548✔
202
   if typesetter.frame:writingDirection() ~= "RTL" then
1,096✔
203
      typesetter.frame:advanceWritingDirection(outputWidth)
488✔
204
   end
205
end
206

207
nodetypes.zerohbox = pl.class(nodetypes.hbox)
34✔
208
nodetypes.zerohbox.type = "zerohbox"
17✔
209
nodetypes.zerohbox.value = { glyph = 0 }
17✔
210

211
nodetypes.nnode = pl.class(nodetypes.hbox)
34✔
212
nodetypes.nnode.type = "nnode"
17✔
213
nodetypes.nnode.language = ""
17✔
214
nodetypes.nnode.pal = nil
17✔
215
nodetypes.nnode.nodes = {}
17✔
216

217
function nodetypes.nnode:_init (spec)
34✔
218
   self:super(spec)
1,394✔
219
   if 0 == self.depth:tonumber() then
2,788✔
220
      self.depth = _maxnode(self.nodes, "depth")
2,788✔
221
   end
222
   if 0 == self.height:tonumber() then
2,788✔
223
      self.height = _maxnode(self.nodes, "height")
2,788✔
224
   end
225
   if 0 == self.width:tonumber() then
2,788✔
226
      self.width = SU.sum(SU.map(function (node)
4,182✔
227
         return node.width
1,305✔
228
      end, self.nodes))
2,788✔
229
   end
230
end
231

232
function nodetypes.nnode:__tostring ()
34✔
233
   return "N<"
×
234
      .. tostring(self.width)
×
235
      .. ">^"
×
236
      .. tostring(self.height)
×
237
      .. "-"
×
238
      .. tostring(self.depth)
×
239
      .. "v("
×
240
      .. self:toText()
×
241
      .. ")"
×
242
end
243

244
function nodetypes.nnode:absolute ()
34✔
245
   return self
×
246
end
247

248
function nodetypes.nnode:outputYourself (typesetter, line)
34✔
249
   -- See typesetter:computeLineRatio() which implements the currently rather messy
250
   -- and probably slightly dubious 'hyphenated' logic.
251
   -- Example: consider the word "out-put".
252
   -- The node queue therefore contains N(out)D(-)N(put) all pointing to the same
253
   -- parent N(output).
254
   if self.parent and not self.parent.hyphenated then
904✔
255
      -- When we hit N(out) and are not hyphenated, we output N(output) directly
256
      -- and mark it as used, so as to skip D(-)N(put) afterwards.
257
      -- I guess this was done to ensure proper kerning (vs. outputting each of
258
      -- the nodes separately).
259
      if not self.parent.used then
356✔
260
         self.parent:outputYourself(typesetter, line)
149✔
261
      end
262
      self.parent.used = true
356✔
263
   else
264
      -- It's possible not to have a parent, e.g. N(word) without hyphenation points.
265
      -- Either that, or we have a hyphenated parent but are in the case we are
266
      -- outputting one of the elements e.g. N(out)D(-) [line break] N(put).
267
      -- (ignoring the G(margin) nodes and potentially zerohbox nodes also on either side of the line break)
268
      for _, node in ipairs(self.nodes) do
1,096✔
269
         node:outputYourself(typesetter, line)
548✔
270
      end
271
   end
272
end
273

274
function nodetypes.nnode:toText ()
34✔
275
   return self.text
×
276
end
277

278
nodetypes.unshaped = pl.class(nodetypes.nnode)
34✔
279
nodetypes.unshaped.type = "unshaped"
17✔
280

281
function nodetypes.unshaped:_init (spec)
34✔
282
   self:super(spec)
89✔
283
   self.width = nil
89✔
284
end
285

286
function nodetypes.unshaped:__tostring ()
34✔
287
   return "U(" .. self:toText() .. ")"
×
288
end
289

290
getmetatable(nodetypes.unshaped).__index = function (_, _)
17✔
291
   -- if k == "width" then SU.error("Can't get width of unshaped node", true) end
292
   -- TODO: No idea why porting to proper Penlight classes this ^^^^^^ started
293
   -- killing everything. Perhaps because this function started working and would
294
   -- actually need to return rawget(self, k) or something?
295
end
296

297
function nodetypes.unshaped:shape ()
34✔
298
   local node = SILE.shaper:createNnodes(self.text, self.options)
77✔
299
   for i = 1, #node do
1,006✔
300
      node[i].parent = self.parent
1,858✔
301
   end
302
   return node
77✔
303
end
304

305
function nodetypes.unshaped.outputYourself (_)
34✔
306
   SU.error("An unshaped node made it to output", true)
×
307
end
308

309
nodetypes.discretionary = pl.class(nodetypes.hbox)
34✔
310

311
nodetypes.discretionary.type = "discretionary"
17✔
312
nodetypes.discretionary.prebreak = {}
17✔
313
nodetypes.discretionary.postbreak = {}
17✔
314
nodetypes.discretionary.replacement = {}
17✔
315
nodetypes.discretionary.used = false
17✔
316

317
function nodetypes.discretionary:__tostring ()
34✔
318
   return "D("
×
319
      .. SU.concat(self.prebreak, "")
×
320
      .. "|"
×
321
      .. SU.concat(self.postbreak, "")
×
322
      .. "|"
×
323
      .. SU.concat(self.replacement, "")
×
324
      .. ")"
×
325
end
326

327
function nodetypes.discretionary:toText ()
34✔
328
   return self.used and "-" or "_"
×
329
end
330

331
function nodetypes.discretionary:markAsPrebreak ()
34✔
332
   self.used = true
11✔
333
   if self.parent then
11✔
334
      self.parent.hyphenated = true
11✔
335
   end
336
   self.is_prebreak = true
11✔
337
end
338

339
function nodetypes.discretionary:cloneAsPostbreak ()
34✔
340
   if not self.used then
11✔
341
      SU.error("Cannot clone a non-used discretionary (previously marked as prebreak)")
×
342
   end
343
   return SILE.types.node.discretionary({
11✔
344
      prebreak = self.prebreak,
11✔
345
      postbreak = self.postbreak,
11✔
346
      replacement = self.replacement,
11✔
347
      parent = self.parent,
11✔
348
      used = true,
349
      is_prebreak = false,
350
   })
11✔
351
end
352

353
function nodetypes.discretionary:outputYourself (typesetter, line)
34✔
354
   -- See typesetter:computeLineRatio() which implements the currently rather
355
   -- messy hyphenated checks.
356
   -- Example: consider the word "out-put-ter".
357
   -- The node queue contains N(out)D(-)N(put)D(-)N(ter) all pointing to the same
358
   -- parent N(output), and here we hit D(-)
359

360
   -- Non-hyphenated parent: when N(out) was hit, we went for outputting
361
   -- the whole parent, so all other elements must now be skipped.
362
   if self.parent and not self.parent.hyphenated then
250✔
363
      return
207✔
364
   end
365

366
   -- It's possible not to have a parent (e.g. on a discretionary directly
367
   -- added in the queue and not coming from the hyphenator logic).
368
   -- Eiher that, or we have a hyphenated parent.
369
   if self.used then
43✔
370
      -- This is the actual hyphenation point.
371
      if self.is_prebreak then
22✔
372
         for _, node in ipairs(self.prebreak) do
22✔
373
            node:outputYourself(typesetter, line)
11✔
374
         end
375
      else
376
         for _, node in ipairs(self.postbreak) do
11✔
377
            node:outputYourself(typesetter, line)
×
378
         end
379
      end
380
   else
381
      -- This is not the hyphenation point (but another discretionary in the queue)
382
      -- E.g. we were in the case where we have N(out)D(-) [line break] N(out)D(-)N(ter)
383
      -- and now hit the second D(-).
384
      -- Unused discretionaries are obviously replaced.
385
      for _, node in ipairs(self.replacement) do
22✔
386
         node:outputYourself(typesetter, line)
1✔
387
      end
388
   end
389
end
390

391
function nodetypes.discretionary:prebreakWidth ()
34✔
392
   if self.prebw then
574✔
393
      return self.prebw
335✔
394
   end
395
   self.prebw = SILE.types.length()
478✔
396
   for _, node in ipairs(self.prebreak) do
478✔
397
      self.prebw:___add(node.width)
239✔
398
   end
399
   return self.prebw
239✔
400
end
401

402
function nodetypes.discretionary:postbreakWidth ()
34✔
403
   if self.postbw then
68✔
404
      return self.postbw
×
405
   end
406
   self.postbw = SILE.types.length()
136✔
407
   for _, node in ipairs(self.postbreak) do
68✔
408
      self.postbw:___add(node.width)
×
409
   end
410
   return self.postbw
68✔
411
end
412

413
function nodetypes.discretionary:replacementWidth ()
34✔
414
   if self.replacew then
331✔
415
      return self.replacew
92✔
416
   end
417
   self.replacew = SILE.types.length()
478✔
418
   for _, node in ipairs(self.replacement) do
242✔
419
      self.replacew:___add(node.width)
3✔
420
   end
421
   return self.replacew
239✔
422
end
423

424
function nodetypes.discretionary:prebreakHeight ()
34✔
425
   if self.prebh then
11✔
426
      return self.prebh
×
427
   end
428
   self.prebh = _maxnode(self.prebreak, "height")
22✔
429
   return self.prebh
11✔
430
end
431

432
function nodetypes.discretionary:postbreakHeight ()
34✔
433
   if self.postbh then
11✔
434
      return self.postbh
×
435
   end
436
   self.postbh = _maxnode(self.postbreak, "height")
22✔
437
   return self.postbh
11✔
438
end
439

440
function nodetypes.discretionary:replacementHeight ()
34✔
441
   if self.replaceh then
21✔
442
      return self.replaceh
×
443
   end
444
   self.replaceh = _maxnode(self.replacement, "height")
42✔
445
   return self.replaceh
21✔
446
end
447

448
function nodetypes.discretionary:replacementDepth ()
34✔
449
   if self.replaced then
×
450
      return self.replaced
×
451
   end
452
   self.replaced = _maxnode(self.replacement, "depth")
×
453
   return self.replaced
×
454
end
455

456
nodetypes.alternative = pl.class(nodetypes.hbox)
34✔
457

458
nodetypes.alternative.type = "alternative"
17✔
459
nodetypes.alternative.options = {}
17✔
460
nodetypes.alternative.selected = nil
17✔
461

462
function nodetypes.alternative:__tostring ()
34✔
463
   return "A(" .. SU.concat(self.options, " / ") .. ")"
×
464
end
465

466
function nodetypes.alternative:minWidth ()
34✔
467
   local minW = function (a, b)
468
      return SU.min(a.width, b.width)
×
469
   end
470
   return pl.tablex.reduce(minW, self.options)
×
471
end
472

473
function nodetypes.alternative:deltas ()
34✔
474
   local minWidth = self:minWidth()
×
475
   local rv = {}
×
476
   for i = 1, #self.options do
×
477
      rv[#rv + 1] = self.options[i].width - minWidth
×
478
   end
479
   return rv
×
480
end
481

482
function nodetypes.alternative:outputYourself (typesetter, line)
34✔
483
   if self.selected then
×
484
      self.options[self.selected]:outputYourself(typesetter, line)
×
485
   end
486
end
487

488
nodetypes.glue = pl.class(nodetypes.box)
34✔
489
nodetypes.glue.type = "glue"
17✔
490
nodetypes.glue.discardable = true
17✔
491

492
function nodetypes.glue:__tostring ()
34✔
493
   return (self.explicit and "E:" or "") .. "G<" .. tostring(self.width) .. ">"
×
494
end
495

496
function nodetypes.glue.toText (_)
34✔
497
   return " "
×
498
end
499

500
function nodetypes.glue:outputYourself (typesetter, line)
34✔
501
   local outputWidth = SU.rationWidth(self.width:absolute(), self.width:absolute(), line.ratio)
2,181✔
502
   typesetter.frame:advanceWritingDirection(outputWidth)
727✔
503
end
504

505
-- A hfillglue is just a glue with infinite stretch.
506
-- (Convenience so callers do not have to know what infinity is.)
507
nodetypes.hfillglue = pl.class(nodetypes.glue)
34✔
508
function nodetypes.hfillglue:_init (spec)
34✔
509
   self:super(spec)
28✔
510
   self.width = SILE.types.length(self.width.length, infinity, self.width.shrink)
56✔
511
end
512

513
-- A hssglue is just a glue with infinite stretch and shrink.
514
-- (Convenience so callers do not have to know what infinity is.)
515
nodetypes.hssglue = pl.class(nodetypes.glue)
34✔
516
function nodetypes.hssglue:_init (spec)
34✔
517
   self:super(spec)
×
518
   self.width = SILE.types.length(self.width.length, infinity, infinity)
×
519
end
520

521
nodetypes.kern = pl.class(nodetypes.glue)
34✔
522
nodetypes.kern.type = "kern" -- Perhaps some smell here, see comment on vkern
17✔
523
nodetypes.kern.discardable = false
17✔
524

525
function nodetypes.kern:__tostring ()
34✔
526
   return "K<" .. tostring(self.width) .. ">"
×
527
end
528

529
nodetypes.vglue = pl.class(nodetypes.box)
34✔
530
nodetypes.vglue.type = "vglue"
17✔
531
nodetypes.vglue.discardable = true
17✔
532
nodetypes.vglue._default_length = "height"
17✔
533
nodetypes.vglue.adjustment = nil
17✔
534

535
function nodetypes.vglue:_init (spec)
34✔
536
   self.adjustment = SILE.types.measurement()
458✔
537
   self:super(spec)
229✔
538
end
539

540
function nodetypes.vglue:__tostring ()
34✔
541
   return (self.explicit and "E:" or "") .. "VG<" .. tostring(self.height) .. ">"
×
542
end
543

544
function nodetypes.vglue:adjustGlue (adjustment)
34✔
545
   self.adjustment = adjustment
79✔
546
end
547

548
function nodetypes.vglue:outputYourself (typesetter, line)
34✔
549
   typesetter.frame:advancePageDirection(line.height:absolute() + line.depth:absolute() + self.adjustment)
605✔
550
end
551

552
function nodetypes.vglue:unbox ()
34✔
553
   return { self }
2✔
554
end
555

556
-- A vfillglue is just a vglue with infinite stretch.
557
-- (Convenience so callers do not have to know what infinity is.)
558
nodetypes.vfillglue = pl.class(nodetypes.vglue)
34✔
559
function nodetypes.vfillglue:_init (spec)
34✔
560
   self:super(spec)
32✔
561
   self.height = SILE.types.length(self.width.length, infinity, self.width.shrink)
64✔
562
end
563

564
-- A vssglue is just a vglue with infinite stretch and shrink.
565
-- (Convenience so callers do not have to know what infinity is.)
566
nodetypes.vssglue = pl.class(nodetypes.vglue)
34✔
567
function nodetypes.vssglue:_init (spec)
34✔
568
   self:super(spec)
×
569
   self.height = SILE.types.length(self.width.length, infinity, infinity)
×
570
end
571

572
nodetypes.zerovglue = pl.class(nodetypes.vglue)
34✔
573

574
nodetypes.vkern = pl.class(nodetypes.vglue)
34✔
575
-- FIXME TODO
576
-- Here we cannot do:
577
--   nodetypes.vkern.type = "vkern"
578
-- It cannot be typed as "vkern" as the pagebuilder doesn't check is_vkern.
579
-- So it's just a vglue currrenty, marked as not discardable...
580
-- But on the other hand, nodetypes.kern is typed "kern" and is not a glue...
581
-- Frankly, the discardable/explicit flags and the types are too
582
-- entangled and point towards a more general design issue.
583
-- N.B. this vkern node is only used in the linespacing package so far.
584
nodetypes.vkern.discardable = false
17✔
585

586
function nodetypes.vkern:__tostring ()
34✔
587
   return "VK<" .. tostring(self.height) .. ">"
×
588
end
589

590
nodetypes.penalty = pl.class(nodetypes.box)
34✔
591
nodetypes.penalty.type = "penalty"
17✔
592
nodetypes.penalty.discardable = true
17✔
593
nodetypes.penalty.penalty = 0
17✔
594

595
function nodetypes.penalty:_init (spec)
34✔
596
   self:super(spec)
172✔
597
   if type(spec) ~= "table" then
172✔
598
      self.penalty = SU.cast("number", spec)
280✔
599
   end
600
end
601

602
function nodetypes.penalty:__tostring ()
34✔
603
   return "P(" .. tostring(self.penalty) .. ")"
×
604
end
605

606
function nodetypes.penalty.outputYourself (_) end
100✔
607

608
function nodetypes.penalty.toText (_)
34✔
609
   return "(!)"
×
610
end
611

612
function nodetypes.penalty:unbox ()
34✔
613
   return { self }
×
614
end
615

616
nodetypes.vbox = pl.class(nodetypes.box)
34✔
617
nodetypes.vbox.type = "vbox"
17✔
618
nodetypes.vbox.nodes = {}
17✔
619
nodetypes.vbox._default_length = "height"
17✔
620

621
function nodetypes.vbox:_init (spec)
34✔
622
   self.nodes = {}
109✔
623
   self:super(spec)
109✔
624
   self.depth = _maxnode(self.nodes, "depth")
218✔
625
   self.height = _maxnode(self.nodes, "height")
218✔
626
end
627

628
function nodetypes.vbox:__tostring ()
34✔
629
   return "VB<" .. tostring(self.height) .. "|" .. self:toText() .. "v" .. tostring(self.depth) .. ")"
×
630
end
631

632
function nodetypes.vbox:toText ()
34✔
633
   return "VB["
×
634
      .. SU.concat(
×
635
         SU.map(function (node)
×
636
            return node:toText()
×
637
         end, self.nodes),
×
638
         ""
639
      )
640
      .. "]"
×
641
end
642

643
function nodetypes.vbox:outputYourself (typesetter, line)
34✔
644
   typesetter.frame:advancePageDirection(self.height)
101✔
645
   local initial = true
101✔
646
   for _, node in ipairs(self.nodes) do
2,166✔
647
      if not (initial and (node.is_glue or node.is_penalty)) then
2,065✔
648
         initial = false
2,065✔
649
         node:outputYourself(typesetter, line)
2,065✔
650
      end
651
   end
652
   typesetter.frame:advancePageDirection(self.depth)
101✔
653
   typesetter.frame:newLine()
101✔
654
end
655

656
function nodetypes.vbox:unbox ()
34✔
657
   for i = 1, #self.nodes do
3✔
658
      if self.nodes[i].is_vbox or self.nodes[i].is_vglue then
3✔
659
         return self.nodes
3✔
660
      end
661
   end
662
   return { self }
×
663
end
664

665
function nodetypes.vbox:append (box)
34✔
666
   local nodes = box
7✔
667
   if not box then
7✔
668
      SU.error("nil box given", true)
×
669
   end
670
   if nodes.type then
7✔
671
      nodes = box:unbox()
8✔
672
   end
673
   self.height = self.height:absolute()
14✔
674
   self.height:___add(self.depth)
7✔
675
   local lastdepth = SILE.types.length()
7✔
676
   for i = 1, #nodes do
187✔
677
      table.insert(self.nodes, nodes[i])
180✔
678
      self.height:___add(nodes[i].height)
180✔
679
      self.height:___add(nodes[i].depth:absolute())
360✔
680
      if nodes[i].is_vbox then
180✔
681
         lastdepth = nodes[i].depth
78✔
682
      end
683
   end
684
   self.height:___sub(lastdepth)
7✔
685
   self.ratio = 1
7✔
686
   self.depth = lastdepth
7✔
687
end
688

689
nodetypes.migrating = pl.class(nodetypes.hbox)
34✔
690
nodetypes.migrating.type = "migrating"
17✔
691
nodetypes.migrating.material = {}
17✔
692
nodetypes.migrating.value = {}
17✔
693
nodetypes.migrating.nodes = {}
17✔
694

695
function nodetypes.migrating:__tostring ()
34✔
696
   return "<M: " .. tostring(self.material) .. ">"
×
697
end
698

699
local _deprecated_nodefactory = {
17✔
700
   newHbox = true,
701
   newNnode = true,
702
   newUnshaped = true,
703
   newDisc = true,
704
   disc = true,
705
   newAlternative = true,
706
   newGlue = true,
707
   newKern = true,
708
   newVglue = true,
709
   newVKern = true,
710
   newPenalty = true,
711
   newDiscretionary = true,
712
   newVbox = true,
713
   newMigrating = true,
714
   zeroGlue = true,
715
   hfillGlue = true,
716
   vfillGlue = true,
717
   hssGlue = true,
718
   vssGlue = true,
719
   zeroHbox = true,
720
   zeroVglue = true,
721
}
722

723
setmetatable(nodetypes, {
34✔
724
   __index = function (_, prop)
725
      if _deprecated_nodefactory[prop] then
×
726
         SU.deprecated(
×
727
            "SILE.types.node." .. prop,
×
728
            "SILE.types.node." .. prop:match("n?e?w?(.*)"):lower(),
×
729
            "0.10.0",
730
            "0.14.0"
731
         )
732
      elseif type(prop) == "number" then -- luacheck: ignore 542
×
733
      -- Likely an attempt to iterate, inspect, or dump the table, sort of safe to ignore
734
      else
735
         SU.error("Attempt to access non-existent SILE.types.node." .. prop)
×
736
      end
737
   end,
738
})
739

740
return nodetypes
17✔
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