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

sile-typesetter / sile / 14684598788

26 Apr 2025 07:55PM UTC coverage: 29.023% (-36.3%) from 65.328%
14684598788

push

github

alerque
test(fonts): Update test to work on new ICU instead of old

5840 of 20122 relevant lines covered (29.02%)

379.34 hits per line

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

70.31
/core/tracestack.lua
1
-- Represents a stack of stack frame objects,
2
-- which describe the call-stack stack of the currently processed document.
3
-- Stack frames are stored contiguously, treating this object as an array.
4
-- Most recent and relevant stack frames are in higher indices, up to #traceStack.
5
-- Do not manipulate the stack directly, use provided push<Type> and pop methods.
6
-- There are different types of stack frames, see pushFrame for more details.
7
local traceStack = pl.class({
26✔
8
   -- Stores the frame which was last popped. Reset after a push.
9
   -- Helps to further specify current location in the processed document.
10
   afterFrame = nil,
11
})
12

13
traceStack.defaultFrame = pl.class({
26✔
14

15
   location = function (self, relative)
16
      local str = ""
2✔
17
      if self.file and not relative then
2✔
18
         str = str .. self.file .. ":"
2✔
19
      end
20
      if self.lno then
2✔
21
         str = str .. self.lno .. ":"
1✔
22
         if self.col then
1✔
23
            str = str .. self.col .. ":"
1✔
24
         end
25
      end
26
      str = str .. (str:len() > 0 and " " or "") .. "in "
4✔
27
      str = str .. tostring(self)
4✔
28
      return str
2✔
29
   end,
30

31
   __tostring = function (self)
32
      self.file = nil
×
33
      self.lno = nil
×
34
      self.col = nil
×
35
      return #self > 0 and tostring(self) or ""
×
36
   end,
37
})
13✔
38

39
traceStack.documentFrame = pl.class(traceStack.defaultFrame)
26✔
40

41
local function oneline (str)
42
   return str:gsub("\n", "␤")
1✔
43
      :gsub("\r", "␍")
1✔
44
      :gsub("\f", "␊")
1✔
45
      :gsub("\a", "␇")
1✔
46
      :gsub("\b", "␈")
1✔
47
      :gsub("\t", "␉")
1✔
48
      :gsub("\v", "␋")
1✔
49
end
50

51
function traceStack.documentFrame:_init (file, snippet)
26✔
52
   self.command = "document"
16✔
53
   self.file = file
16✔
54
   self.snippet = snippet
16✔
55
end
56

57
function traceStack.documentFrame:__tostring ()
26✔
58
   return "<snippet>:\n\t\t[[" .. oneline(self.snippet) .. "]]"
2✔
59
end
60

61
traceStack.commandFrame = pl.class(traceStack.defaultFrame)
26✔
62

63
function traceStack.commandFrame:_init (command, content, options)
26✔
64
   self.command = command
231✔
65
   self.file = content.file or SILE.currentlyProcessingFile
231✔
66
   self.lno = content.lno
231✔
67
   self.col = content.col
231✔
68
   self.options = options or {}
231✔
69
end
70

71
function traceStack.commandFrame:__tostring ()
26✔
72
   local opts = pl.tablex.size(self.options) == 0 and ""
2✔
73
      or pl.pretty.write(self.options, ""):gsub("^{", "["):gsub("}$", "]")
1✔
74
   return "\\" .. tostring(self.command) .. opts
1✔
75
end
76

77
traceStack.contentFrame = pl.class(traceStack.commandFrame)
26✔
78

79
function traceStack.contentFrame:_init (command, content)
26✔
80
   self:super(command, content, content.options)
×
81
end
82

83
traceStack.textFrame = pl.class(traceStack.defaultFrame)
26✔
84

85
function traceStack.textFrame:_init (text)
26✔
86
   self.text = text
56✔
87
end
88

89
function traceStack.textFrame:__tostring ()
26✔
90
   if self.text:len() > 20 then
×
91
      self.text = luautf8.sub(self.text, 1, 18) .. "…"
×
92
   end
93
   self.text = oneline(self.text)
×
94
   return '"' .. self.text .. '"'
×
95
end
96

97
local function formatTraceLine (string)
98
   local prefix = "\t"
1✔
99
   return prefix .. string .. "\n"
1✔
100
end
101

102
-- Push a document processing run (input method) onto the stack
103
function traceStack:pushDocument (file, doc)
13✔
104
   if type(doc) == "table" then
16✔
105
      doc = tostring(doc)
×
106
   end
107
   local snippet = doc:sub(1, 100)
16✔
108
   local frame = traceStack.documentFrame(file, snippet)
16✔
109
   return self:pushFrame(frame)
16✔
110
end
111

112
-- Push a command frame on to the stack to record the execution trace for debugging.
113
-- Carries information about the command call, not the command itself.
114
-- Must be popped with `pop(returnOfPush)`.
115
function traceStack:pushCommand (command, content, options)
13✔
116
   if not command then
231✔
117
      SU.warn("Command should be specified for SILE.traceStack:pushCommand", true)
×
118
   end
119
   if type(content) == "function" then
231✔
120
      content = {}
10✔
121
   end
122
   local frame = traceStack.commandFrame(command, content, options)
231✔
123
   return self:pushFrame(frame)
231✔
124
end
125

126
-- Push a command frame on to the stack to record the execution trace for debugging.
127
-- Command arguments are inferred from AST content, any item may be overridden.
128
-- Must be popped with `pop(returnOfPush)`.
129
function traceStack:pushContent (content, command)
13✔
130
   if type(content) ~= "table" then
×
131
      SU.warn("Content parameter of SILE.traceStack:pushContent must be a table", true)
×
132
   end
133
   command = command or content.command
×
134
   if not command then
×
135
      SU.warn("Command should be specified or inferable for SILE.traceStack:pushContent", true)
×
136
   end
137
   local frame = traceStack.contentFrame(command, content)
×
138
   return self:pushFrame(frame)
×
139
end
140

141
-- Push a text that is going to get typeset on to the stack to record the execution trace for debugging.
142
-- Must be popped with `pop(returnOfPush)`.
143
function traceStack:pushText (text)
13✔
144
   local frame = traceStack.textFrame(text)
56✔
145
   return self:pushFrame(frame)
56✔
146
end
147

148
-- Internal: Push-pop balance checking ID
149
local lastPushId = 0
13✔
150

151
-- Push complete frame onto the stack.
152
-- Frame is a table with following optional fields:
153
-- .file = string - name of the file from which this originates
154
-- .lno = number - line in the file
155
-- .col = number - column on the line
156
-- .toStringHelper = function() that serializes extended information about the frame BESIDES location
157
function traceStack:pushFrame (frame)
13✔
158
   SU.debug("traceStack", function ()
606✔
159
      return string.rep(".", #self) .. "PUSH(" .. frame:location() .. ")"
×
160
   end)
161
   self[#self + 1] = frame
303✔
162
   self.afterFrame = nil
303✔
163
   lastPushId = lastPushId + 1
303✔
164
   frame._pushId = lastPushId
303✔
165
   return lastPushId
303✔
166
end
167

168
-- Pop previously pushed command from the stack.
169
-- Return value of `push` function must be provided as argument to check for balanced usage.
170
function traceStack:pop (pushId)
13✔
171
   if type(pushId) ~= "number" then
303✔
172
      SU.error("SILE.traceStack:pop's argument must be the result value of the corresponding push", true)
×
173
   end
174
   -- First verify that push/pop is balanced
175
   local popped = self[#self]
303✔
176
   if popped._pushId ~= pushId then
303✔
177
      local message = "Unbalanced content push/pop"
×
178
      if SILE.traceback or SU.debugging("traceStack") then
×
179
         message = message .. ". Expected " .. popped.pushId .. " - (" .. popped:location() .. "), got " .. pushId
×
180
      end
181
      SU.warn(message, true)
×
182
   else
183
      -- Correctly balanced: pop the frame
184
      self.afterFrame = popped
303✔
185
      self[#self] = nil
303✔
186
      SU.debug("traceStack", function ()
606✔
187
         return string.rep(".", #self) .. "POP(" .. popped:location() .. ")"
×
188
      end)
189
   end
190
end
191

192
-- Returns single line string with location of top most trace frame
193
function traceStack:locationHead ()
13✔
194
   local afterFrame = self.afterFrame
2✔
195
   local top = self[#self]
2✔
196
   if not top then
2✔
197
      -- Stack is empty, there is not much we can do
198
      return formatTraceLine(
1✔
199
         afterFrame and "after " .. afterFrame:location() or SILE.currentlyProcessingFile or "<nowhere>"
2✔
200
      )
1✔
201
   end
202
   local trace = top:location()
1✔
203
   local locationFrame = top
1✔
204
   -- Not all stack traces have to carry location information.
205
   -- If the first stack trace does not carry it, find a frame which does.
206
   -- Then append it, because its information may be useful.
207
   if not locationFrame.lno then
1✔
208
      for i = #self - 1, 1, -1 do
×
209
         if self[i].lno then
×
210
            locationFrame = self[i]
×
211
            trace = trace .. " near " .. locationFrame:location(locationFrame.file == top.file)
×
212
            break
213
         end
214
      end
215
   end
216
   -- Print after, if it is in a relevant file
217
   if afterFrame and (not locationFrame or afterFrame.file == locationFrame.file) then
1✔
218
      trace = trace .. " after " .. afterFrame:location(true)
×
219
   end
220
   return trace
1✔
221
end
222

223
-- Returns multiline trace string with locations of each frame up to maxdepth
224
function traceStack:locationTrace (maxdepth)
13✔
225
   local depth = maxdepth or #self
×
226
   local trace = formatTraceLine(self:locationHead())
×
227
   depth = depth - 1
×
228
   if depth > 1 then
×
229
      repeat
230
         trace = trace .. formatTraceLine(self[depth]:location())
×
231
         depth = depth - 1
×
232
      until depth == 1 -- stop at 1 (document) as not useful in trace
×
233
   end
234
   return trace
×
235
end
236

237
return traceStack
13✔
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