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

sile-typesetter / sile / 6932773445

20 Nov 2023 04:11PM UTC coverage: 60.703% (-1.6%) from 62.266%
6932773445

Pull #1904

github

alerque
feat(utilities): Add Greek alphabetical (non-arithmetic) numbering

Useful in some context such as biblical annotations etc. where greek
characters are used orderly for numbering.
Pull Request #1904: Merge develop into master (commit to next release being breaking)

66 of 193 new or added lines in 19 files covered. (34.2%)

321 existing lines in 26 files now uncovered.

9452 of 15571 relevant lines covered (60.7%)

2104.43 hits per line

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

69.49
/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({
106✔
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({
106✔
14

15
    location = function (self, relative)
16
      local str = ""
6✔
17
      if self.file and not relative then
6✔
18
        str = str .. self.file .. ":"
3✔
19
      end
20
      if self.lno then
6✔
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 "
12✔
27
      str = str .. tostring(self)
12✔
28
      return str
6✔
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
  })
53✔
38

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

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

45
function traceStack.documentFrame:_init (file, snippet)
106✔
46
  self.command = "document"
62✔
47
  self.file = file
62✔
48
  self.snippet = snippet
62✔
49
end
50

51
function traceStack.documentFrame:__tostring ()
106✔
52
  return "<snippet>:\n\t\t[[" .. oneline(self.snippet) .. "]]"
4✔
53
end
54

55
traceStack.commandFrame = pl.class(traceStack.defaultFrame)
106✔
56

57
function traceStack.commandFrame:_init (command, content, options)
106✔
58
  self.command = command
636✔
59
  self.file = content.file or SILE.currentlyProcessingFile
636✔
60
  self.lno = content.lno
636✔
61
  self.col = content.col
636✔
62
  self.options = options or {}
636✔
63
end
64

65
function traceStack.commandFrame:__tostring ()
106✔
66
  local opts = pl.tablex.size(self.options) == 0 and "" or
8✔
UNCOV
67
    pl.pretty.write(self.options, ""):gsub("^{", "["):gsub("}$", "]")
×
68
  return "\\" .. tostring(self.command) .. opts
4✔
69
end
70

71
traceStack.contentFrame = pl.class(traceStack.commandFrame)
106✔
72

73
function traceStack.contentFrame:_init (command, content)
106✔
UNCOV
74
  self:super(command, content, content.options)
×
75
end
76

77
traceStack.textFrame = pl.class(traceStack.defaultFrame)
106✔
78

79
function traceStack.textFrame:_init (text)
106✔
80
  self.text = text
206✔
81
end
82

83
function traceStack.textFrame:__tostring ()
106✔
UNCOV
84
  if self.text:len() > 20 then
×
UNCOV
85
    self.text = luautf8.sub(self.text, 1, 18) .. "…"
×
86
  end
UNCOV
87
  self.text = oneline(self.text)
×
UNCOV
88
  return '"' .. self.text .. '"'
×
89
end
90

91
local function formatTraceLine (string)
92
  local prefix = "\t"
1✔
93
  return prefix .. string .. "\n"
1✔
94
end
95

96
-- Push a document processing run (input method) onto the stack
97
function traceStack:pushDocument(file, doc)
53✔
98
  if type(doc) == "table" then doc = tostring(doc) end
62✔
99
  local snippet = doc:sub(1, 100)
62✔
100
  local frame = traceStack.documentFrame(file, snippet)
62✔
101
  return self:pushFrame(frame)
62✔
102
end
103

104
-- Push a command frame on to the stack to record the execution trace for debugging.
105
-- Carries information about the command call, not the command itself.
106
-- Must be popped with `pop(returnOfPush)`.
107
function traceStack:pushCommand(command, content, options)
53✔
108
  if not command then
636✔
109
    SU.warn("Command should be specified for SILE.traceStack:pushCommand", true)
×
110
  end
111
  if type(content) == "function" then content = {} end
636✔
112
  local frame = traceStack.commandFrame(command, content, options)
636✔
113
  return self:pushFrame(frame)
636✔
114
end
115

116
-- Push a command frame on to the stack to record the execution trace for debugging.
117
-- Command arguments are inferred from AST content, any item may be overridden.
118
-- Must be popped with `pop(returnOfPush)`.
119
function traceStack:pushContent(content, command)
53✔
UNCOV
120
  if type(content) ~= "table" then
×
121
    SU.warn("Content parameter of SILE.traceStack:pushContent must be a table", true)
×
122
  end
UNCOV
123
  command = command or content.command
×
UNCOV
124
  if not command then
×
125
    SU.warn("Command should be specified or inferable for SILE.traceStack:pushContent", true)
×
126
  end
UNCOV
127
  local frame = traceStack.contentFrame(command, content)
×
UNCOV
128
  return self:pushFrame(frame)
×
129
end
130

131
-- Push a text that is going to get typeset on to the stack to record the execution trace for debugging.
132
-- Must be popped with `pop(returnOfPush)`.
133
function traceStack:pushText(text)
53✔
134
  local frame = traceStack.textFrame(text)
206✔
135
  return self:pushFrame(frame)
206✔
136
end
137

138
-- Internal: Push-pop balance checking ID
139
local lastPushId = 0
53✔
140

141
-- Push complete frame onto the stack.
142
-- Frame is a table with following optional fields:
143
-- .file = string - name of the file from which this originates
144
-- .lno = number - line in the file
145
-- .col = number - column on the line
146
-- .toStringHelper = function() that serializes extended information about the frame BESIDES location
147
function traceStack:pushFrame(frame)
53✔
148
  SU.debug("traceStack", function ()
1,808✔
149
    return string.rep(".", #self) .. "PUSH(" .. frame:location() .. ")"
×
150
  end)
151
  self[#self + 1] = frame
904✔
152
  self.afterFrame = nil
904✔
153
  lastPushId = lastPushId + 1
904✔
154
  frame._pushId = lastPushId
904✔
155
  return lastPushId
904✔
156
end
157

158
-- Pop previously pushed command from the stack.
159
-- Return value of `push` function must be provided as argument to check for balanced usage.
160
function traceStack:pop(pushId)
53✔
161
  if type(pushId) ~= "number" then
904✔
162
    SU.error("SILE.traceStack:pop's argument must be the result value of the corresponding push", true)
×
163
  end
164
  -- First verify that push/pop is balanced
165
  local popped = self[#self]
904✔
166
  if popped._pushId ~= pushId then
904✔
167
    local message = "Unbalanced content push/pop"
×
168
    if SILE.traceback or SU.debugging("traceStack") then
×
169
      message = message .. ". Expected " .. popped.pushId .. " - (" .. popped:location() .. "), got " .. pushId
×
170
    end
171
    SU.warn(message, true)
×
172
  else
173
    -- Correctly balanced: pop the frame
174
    self.afterFrame = popped
904✔
175
    self[#self] = nil
904✔
176
    SU.debug("traceStack", function ()
1,808✔
177
      return string.rep(".", #self) .. "POP(" .. popped:location() .. ")"
×
178
    end)
179
  end
180
end
181

182
-- Returns single line string with location of top most trace frame
183
function traceStack:locationHead()
53✔
184
  local afterFrame = self.afterFrame
6✔
185
  local top = self[#self]
6✔
186
  if not top then
6✔
187
    -- Stack is empty, there is not much we can do
188
    return formatTraceLine(afterFrame and "after " .. afterFrame:location() or SILE.currentlyProcessingFile or "<nowhere>")
2✔
189
  end
190
  local trace = top:location()
5✔
191
  local locationFrame = top
5✔
192
  -- Not all stack traces have to carry location information.
193
  -- If the first stack trace does not carry it, find a frame which does.
194
  -- Then append it, because its information may be useful.
195
  if not locationFrame.lno then
5✔
196
    for i = #self - 1, 1, -1 do
9✔
197
      if self[i].lno then
5✔
UNCOV
198
        locationFrame = self[i]
×
UNCOV
199
        trace = trace .. " near " .. locationFrame:location(locationFrame.file == top.file)
×
200
        break
201
      end
202
    end
203
  end
204
  -- Print after, if it is in a relevant file
205
  if afterFrame and (not locationFrame or afterFrame.file == locationFrame.file) then
5✔
UNCOV
206
    trace = trace .. " after " .. afterFrame:location(true)
×
207
  end
208
  return trace
5✔
209
end
210

211
-- Returns multiline trace string with locations of each frame up to maxdepth
212
function traceStack:locationTrace(maxdepth)
53✔
UNCOV
213
  local depth = maxdepth or #self
×
UNCOV
214
  local trace = formatTraceLine(self:locationHead())
×
UNCOV
215
  depth = depth - 1
×
UNCOV
216
  if depth > 1 then
×
217
    repeat
218
      trace = trace .. formatTraceLine(self[depth]:location())
×
219
      depth = depth - 1
×
220
    until depth == 1 -- stop at 1 (document) as not useful in trace
×
221
  end
UNCOV
222
  return trace
×
223
end
224

225
return traceStack
53✔
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