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

sile-typesetter / sile / 14860011647

06 May 2025 12:44PM UTC coverage: 67.057% (+32.5%) from 34.559%
14860011647

push

github

alerque
chore(typesetters): Fixup access to class from typesetter functions

7 of 7 new or added lines in 2 files covered. (100.0%)

1344 existing lines in 103 files now uncovered.

14880 of 22190 relevant lines covered (67.06%)

11549.16 hits per line

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

82.68
/core/frame.lua
1
SILE.frames = {}
302✔
2

3
-- Buggy, relies on side effects
4
-- luacheck: ignore solver frame
5
-- See https://github.com/sile-typesetter/sile/issues/694
6

7
local cassowary = require("cassowary")
302✔
8
local solver = cassowary.SimplexSolver()
302✔
9
local solverNeedsReloading = true
302✔
10

11
local widthdims = pl.Set({ "left", "right", "width" })
604✔
12
local heightdims = pl.Set({ "top", "bottom", "height" })
302✔
13
local alldims = widthdims + heightdims
302✔
14

15
SILE.framePrototype = pl.class({
604✔
16
   direction = "LTR-TTB",
17
   enterHooks = {},
302✔
18
   leaveHooks = {},
302✔
19

20
   -- This gets called by Penlght when creating the frame instance
21
   _init = function (self, spec, dummy)
22
      local direction = SILE.documentState.direction
842✔
23
      if direction then
842✔
24
         self.direction = direction
12✔
25
      end
26
      self.constraints = {}
842✔
27
      self.variables = {}
842✔
28
      self.id = spec.id
842✔
29
      for k, v in pairs(spec) do
5,123✔
30
         if not alldims[k] then
4,281✔
31
            self[k] = v
960✔
32
         end
33
      end
34
      self.balanced = SU.boolean(self.balanced, false)
1,684✔
35
      if not dummy then
842✔
36
         for method in pairs(alldims) do
5,887✔
37
            self.variables[method] = cassowary.Variable({ name = spec.id .. "_" .. method })
10,092✔
38
            self[method] = function (instance_self)
39
               instance_self:solve()
8,636✔
40
               local value = instance_self.variables[method].value
8,636✔
41
               return SILE.types.measurement(value)
8,636✔
42
            end
43
         end
44
         -- Add definitions of width and height
45
         for method in pairs(alldims) do
5,887✔
46
            if spec[method] then
5,046✔
47
               self:constrain(method, spec[method])
3,321✔
48
            end
49
         end
50
      end
51
   end,
52

53
   -- This gets called by us in typesetter before we start to use the frame
54
   init = function (self, typesetter)
55
      self.state = { totals = { height = SILE.types.measurement(0) } }
888✔
56
      self:enter(typesetter)
444✔
57
      self:newLine(typesetter)
444✔
58
      if self:pageAdvanceDirection() == "TTB" then
888✔
59
         self.state.cursorY = self:top()
872✔
60
      elseif self:pageAdvanceDirection() == "LTR" then
16✔
61
         self.state.cursorX = self:left()
4✔
62
      elseif self:pageAdvanceDirection() == "RTL" then
12✔
63
         self.state.cursorX = self:right()
8✔
64
      elseif self:pageAdvanceDirection() == "BTT" then
4✔
65
         self.state.cursorY = self:bottom()
4✔
66
      end
67
   end,
68

69
   constrain = function (self, method, dimension)
70
      self.constraints[method] = tostring(dimension)
3,491✔
71
      self:invalidate()
3,397✔
72
   end,
73

74
   invalidate = function ()
75
      solverNeedsReloading = true
3,757✔
76
   end,
77

78
   relax = function (self, method)
79
      self.constraints[method] = nil
59✔
80
   end,
81

82
   reifyConstraint = function (self, solver, method, stay)
83
      local constraint = self.constraints[method]
6,135✔
84
      if not constraint then
6,135✔
UNCOV
85
         return
×
86
      end
87
      constraint = SU.type(constraint) == "measurement" and constraint:tonumber() or SILE.frameParser:match(constraint)
12,270✔
88
      SU.debug("frames", "Adding constraint", self.id, function ()
12,270✔
UNCOV
89
         return "(" .. method .. ") = " .. tostring(constraint)
×
90
      end)
91
      local eq = cassowary.Equation(self.variables[method], constraint)
6,135✔
92
      solver:addConstraint(eq)
6,135✔
93
      if stay then
6,135✔
94
         solver:addStay(eq)
1,268✔
95
      end
96
   end,
97

98
   addWidthHeightDefinitions = function (self, solver)
99
      local vars = self.variables
1,540✔
100
      solver:addConstraint(cassowary.Equation(vars.width, cassowary.minus(vars.right, vars.left)))
4,620✔
101
      solver:addConstraint(cassowary.Equation(vars.height, cassowary.minus(vars.bottom, vars.top)))
4,620✔
102
   end,
103

104
   -- This is hideously inefficient,
105
   -- but it's the easiest way to allow users to reconfigure frames at runtime.
106
   solve = function (_)
107
      if not solverNeedsReloading then
8,636✔
108
         return
8,319✔
109
      end
110
      SU.debug("frames", "Solving...")
317✔
111
      solver = cassowary.SimplexSolver()
634✔
112
      if SILE.frames.page then
317✔
113
         for method, _ in pairs(SILE.frames.page.constraints) do
1,585✔
114
            SILE.frames.page:reifyConstraint(solver, method, true)
1,268✔
115
         end
116
         SILE.frames.page:addWidthHeightDefinitions(solver)
317✔
117
      end
118
      for id, frame in pairs(SILE.frames) do
1,857✔
119
         if not (id == "page") then
1,540✔
120
            for method, _ in pairs(frame.constraints) do
6,090✔
121
               frame:reifyConstraint(solver, method)
4,867✔
122
            end
123
            frame:addWidthHeightDefinitions(solver)
1,223✔
124
         end
125
      end
126
      solver:solve()
317✔
127
      solverNeedsReloading = false
317✔
128
   end,
129

130
   writingDirection = function (self)
131
      return self.direction:match("^(%a+)") or "LTR"
83,375✔
132
   end,
133

134
   pageAdvanceDirection = function (self)
135
      return self.direction:match("-(%a+)$") or "TTB"
7,819✔
136
   end,
137

138
   advanceWritingDirection = function (self, length)
139
      local amount = SU.cast("number", length)
19,731✔
140
      if amount == 0 then
19,731✔
141
         return
3,038✔
142
      end
143
      if self:writingDirection() == "RTL" then
33,386✔
144
         self.state.cursorX = self.state.cursorX - amount
980✔
145
      elseif self:writingDirection() == "LTR" then
32,406✔
146
         self.state.cursorX = self.state.cursorX + amount
32,330✔
147
      elseif self:writingDirection() == "TTB" then
76✔
148
         self.state.cursorY = self.state.cursorY + amount
64✔
149
      elseif self:writingDirection() == "BTT" then
12✔
150
         self.state.cursorY = self.state.cursorY - amount
12✔
151
      end
152
   end,
153

154
   advancePageDirection = function (self, length)
155
      local amount = SU.cast("number", length)
5,106✔
156
      if amount == 0 then
5,106✔
157
         return
356✔
158
      end
159
      if self:pageAdvanceDirection() == "TTB" then
9,500✔
160
         self.state.cursorY = self.state.cursorY + amount
9,408✔
161
      elseif self:pageAdvanceDirection() == "RTL" then
92✔
162
         self.state.cursorX = self.state.cursorX - amount
52✔
163
      elseif self:pageAdvanceDirection() == "LTR" then
40✔
164
         self.state.cursorX = self.state.cursorX + amount
20✔
165
      elseif self:pageAdvanceDirection() == "BTT" then
20✔
166
         self.state.cursorY = self.state.cursorY - amount
20✔
167
      end
168
   end,
169

170
   newLine = function (self, _)
171
      if self:writingDirection() == "LTR" then
3,578✔
172
         self.state.cursorX = self:left()
3,474✔
173
      elseif self:writingDirection() == "RTL" then
104✔
174
         self.state.cursorX = self:right()
80✔
175
      elseif self:writingDirection() == "TTB" then
24✔
176
         self.state.cursorY = self:top()
16✔
177
      elseif self:writingDirection() == "BTT" then
8✔
178
         self.state.cursorY = self:bottom()
8✔
179
      end
180
   end,
181

182
   lineWidth = function (self)
183
      SU.warn("Method :lineWidth() is deprecated, please use :getLineWidth()")
×
UNCOV
184
      return self:getLineWidth()
×
185
   end,
186

187
   getLineWidth = function (self)
188
      if self:writingDirection() == "LTR" or self:writingDirection() == "RTL" then
1,971✔
189
         return self:width()
956✔
190
      else
191
         return self:height()
13✔
192
      end
193
   end,
194

195
   pageTarget = function (self)
196
      SU.warn("Method :pageTarget() is deprecated, please use :getTargetLength()")
×
UNCOV
197
      return self:getTargetLength()
×
198
   end,
199

200
   getTargetLength = function (self)
201
      local direction = self:pageAdvanceDirection()
2,533✔
202
      if direction == "TTB" or direction == "BTT" then
2,533✔
203
         return self:height()
2,502✔
204
      else
205
         return self:width()
31✔
206
      end
207
   end,
208

209
   enter = function (self, typesetter)
210
      for i = 1, #self.enterHooks do
538✔
211
         self.enterHooks[i](self, typesetter)
6✔
212
      end
213
   end,
214

215
   leave = function (self, typesetter)
216
      for i = 1, #self.leaveHooks do
472✔
217
         self.leaveHooks[i](self, typesetter)
14✔
218
      end
219
   end,
220

221
   isAbsoluteConstraint = function (self, method)
222
      if not self.constraints[method] then
276✔
223
         return false
2✔
224
      end
225
      local constraint = SILE.frameParser:match(self.constraints[method])
274✔
226
      if type(constraint) ~= "table" then
274✔
227
         return true
68✔
228
      end
229
      if not constraint.terms then
206✔
230
         return false
202✔
231
      end
232
      for clv, _ in pairs(constraint.terms) do
4✔
233
         if clv.name and not clv.name:match("^page_") then
4✔
234
            return false
4✔
235
         end
236
      end
UNCOV
237
      return true
×
238
   end,
239

240
   isMainContentFrame = function (self)
241
      local tpt = SILE.documentState.thisPageTemplate
66✔
242
      local frame = tpt.firstContentFrame
66✔
243
      while frame do
72✔
244
         if frame == self then
72✔
245
            return true
45✔
246
         end
247
         if frame.next then
27✔
248
            frame = SILE.getFrame(frame.next)
12✔
249
         else
250
            return false
21✔
251
         end
252
      end
UNCOV
253
      return false
×
254
   end,
255

256
   __tostring = function (self)
257
      local str = "<Frame: " .. self.id .. ": "
×
258
      str = str .. " next=" .. (self.next or "nil") .. " "
×
259
      for method, dimension in pairs(self.constraints) do
×
UNCOV
260
         str = str .. method .. "=" .. dimension .. "; "
×
261
      end
262
      if self.hanmen then
×
263
         str = str .. "tate=" .. (self.tate and "true" or "false") .. "; "
×
264
         str = str .. "gridsize=" .. self.gridsize .. "; "
×
265
         str = str .. "linegap=" .. self.linegap .. "; "
×
266
         str = str .. "linelength=" .. self.linelength .. "; "
×
UNCOV
267
         str = str .. "linecount=" .. self.linecount .. "; "
×
268
      end
269
      str = str .. ">"
×
UNCOV
270
      return str
×
271
   end,
272
})
302✔
273

274
SILE.newFrame = function (spec, prototype)
302✔
275
   SU.required(spec, "id", "frame declaration")
841✔
276
   prototype = prototype or SILE.framePrototype
841✔
277
   local frame = prototype(spec)
841✔
278
   SILE.frames[spec.id] = frame
841✔
279
   return frame
841✔
280
end
281

282
SILE.getFrame = function (id)
302✔
283
   if type(id) == "table" then
366✔
UNCOV
284
      SU.error("Passed a table, expected a string", true)
×
285
   end
286
   local frame, last_attempt
287
   while not frame do
732✔
288
      frame = SILE.frames[id]
367✔
289
      id = id:gsub("_$", "")
367✔
290
      if id == last_attempt then
367✔
291
         break
1✔
292
      end
293
      last_attempt = id
366✔
294
   end
295
   return frame or SU.warn("Couldn't find frame ID " .. id, true)
367✔
296
end
297

298
SILE.parseComplexFrameDimension = function (dimension)
302✔
299
   local length = SILE.frameParser:match(SU.cast("string", dimension))
×
300
   if type(length) == "table" then
×
301
      local g = cassowary.Variable({ name = "t" })
×
302
      local eq = cassowary.Equation(g, length)
×
303
      solverNeedsReloading = true
×
304
      solver:addConstraint(eq)
×
305
      SILE.frames.page:solve()
×
306
      solverNeedsReloading = true
×
UNCOV
307
      return g.value
×
308
   end
UNCOV
309
   return length
×
310
end
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