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

sile-typesetter / sile / 14908152066

08 May 2025 01:49PM UTC coverage: 61.309% (-5.7%) from 67.057%
14908152066

push

github

alerque
chore(registries): Touchup command registry deprecation shims

1 of 2 new or added lines in 2 files covered. (50.0%)

1379 existing lines in 46 files now uncovered.

13556 of 22111 relevant lines covered (61.31%)

11834.23 hits per line

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

56.82
/packages/counters/init.lua
1
local base = require("packages.base")
42✔
2

3
local package = pl.class(base)
42✔
4
package._name = "counters"
42✔
5

6
SILE.formatCounter = function ()
42✔
7
   SU.deprecated("SILE.formatCounter", "class:formatCounter", "0.13.0", "0.15.0")
×
8
end
9

10
SILE.formatMultilevelCounter = function ()
42✔
11
   SU.deprecated("SILE.formatMultilevelCounter", "class:formatMultilevelCounter", "0.13.0", "0.15.0")
×
12
end
13

14
local function getCounter (_, id)
UNCOV
15
   local counter = SILE.scratch.counters[id]
×
UNCOV
16
   if not counter then
×
UNCOV
17
      counter = {
×
18
         value = 0,
19
         display = "arabic",
20
         format = package.formatCounter,
21
      }
UNCOV
22
      SILE.scratch.counters[id] = counter
×
UNCOV
23
   elseif type(counter.value) ~= "number" then
×
24
      SU.error("Counter " .. id .. " is not a single-level counter")
×
25
   end
UNCOV
26
   return counter
×
27
end
28

29
local function getMultilevelCounter (_, id)
30
   local counter = SILE.scratch.counters[id]
19✔
31
   if not counter then
19✔
32
      counter = {
2✔
33
         value = { 0 },
2✔
34
         display = { "arabic" },
2✔
35
         format = package.formatMultilevelCounter,
2✔
36
      }
2✔
37
      SILE.scratch.counters[id] = counter
2✔
38
   elseif type(counter.value) ~= "table" then
17✔
39
      SU.error("Counter " .. id .. " is not a multi-level counter")
×
40
   end
41
   return counter
19✔
42
end
43

44
function package:formatCounter (counter)
42✔
45
   return SU.formatNumber(counter.value, { system = counter.display })
96✔
46
end
47

48
function package:formatMultilevelCounter (counter, options)
42✔
49
   options = options or {}
11✔
50
   local maxlevel = options.level and SU.min(SU.cast("integer", options.level), #counter.value) or #counter.value
13✔
51
   -- Option minlevel is undocumented and should perhaps be deprecated: is there a real use case for it?
52
   local minlevel = options.minlevel and SU.min(SU.cast("integer", options.minlevel), #counter.value) or 1
11✔
53
   local out = {}
11✔
54
   if SU.boolean(options.noleadingzeros, false) then
22✔
55
      while counter.value[minlevel] == 0 do
×
56
         minlevel = minlevel + 1
×
57
      end -- skip leading zeros
58
   end
59
   for x = minlevel, maxlevel do
41✔
60
      out[x - minlevel + 1] = self:formatCounter({ display = counter.display[x], value = counter.value[x] })
60✔
61
   end
62
   return table.concat(out, ".")
11✔
63
end
64

65
function package:_init ()
42✔
66
   base._init(self)
68✔
67
   if not SILE.scratch.counters then
68✔
68
      SILE.scratch.counters = {}
42✔
69
   end
70
   self:export("getCounter", getCounter)
68✔
71
   self:export("getMultilevelCounter", getMultilevelCounter)
68✔
72
end
73

74
function package:registerCommands ()
42✔
75
   self.commands:register("increment-counter", function (options, _)
126✔
UNCOV
76
      local id = SU.required(options, "id", "increment-counter")
×
UNCOV
77
      local counter = self.class:getCounter(id)
×
UNCOV
78
      if options["set-to"] then
×
79
         SU.deprecated("\\increment-counter[set-to=...]", "\\set-counter[value=...]", "0.14.4", "0.16.0")
×
80
      else
UNCOV
81
         counter.value = counter.value + 1
×
82
      end
UNCOV
83
      if options.display then
×
84
         counter.display = options.display
×
85
      end
86
   end, "Increments the counter named by the <id> option")
42✔
87

88
   self.commands:register(
84✔
89
      "set-counter",
42✔
90
      function (options, _)
UNCOV
91
         local id = SU.required(options, "id", "set-counter")
×
UNCOV
92
         local counter = self.class:getCounter(id)
×
UNCOV
93
         if options.value then
×
UNCOV
94
            counter.value = SU.cast("integer", options.value)
×
95
         end
UNCOV
96
         if options.display then
×
UNCOV
97
            counter.display = options.display
×
98
         end
99
      end,
100
      "Sets the counter named by the <id> option to <value>; sets its display type (roman/Roman/arabic) to type <display>."
101
   )
42✔
102

103
   self.commands:register("show-counter", function (options, _)
126✔
UNCOV
104
      local id = SU.required(options, "id", "show-counter")
×
UNCOV
105
      local counter = self.class:getCounter(id)
×
UNCOV
106
      if options.display then
×
107
         SU.deprecated("\\show-counter[display=...]", "\\set-counter[display=...]", "0.14.4", "0.16.0")
×
108
      end
UNCOV
109
      SILE.typesetter:typeset(self:formatCounter(counter))
×
110
   end, "Outputs the value of counter <id>, optionally displaying it with the <display> format.")
42✔
111

112
   self.commands:register("increment-multilevel-counter", function (options, _)
126✔
113
      local id = SU.required(options, "id", "increment-multilevel-counter")
8✔
114
      local counter = self.class:getMultilevelCounter(id)
8✔
115
      local currentLevel = #counter.value
8✔
116
      local level = SU.cast("integer", options.level or currentLevel)
8✔
117
      local reset = SU.boolean(options.reset, true)
8✔
118
      -- Option reset=false is undocumented and was previously somewhat broken.
119
      -- It should perhaps be deprecated: is there a real use case for it?
120
      if level == currentLevel then
8✔
121
         counter.value[level] = counter.value[level] + 1
4✔
122
      elseif level > currentLevel then
4✔
123
         while level - 1 > currentLevel do
7✔
124
            currentLevel = currentLevel + 1
4✔
125
            counter.value[currentLevel] = 0
4✔
126
            counter.display[currentLevel] = counter.display[currentLevel - 1]
4✔
127
         end
128
         currentLevel = currentLevel + 1
3✔
129
         counter.value[level] = 1
3✔
130
         counter.display[level] = counter.display[currentLevel - 1]
3✔
131
      else -- level < currentLevel
132
         counter.value[level] = counter.value[level] + 1
1✔
133
         while currentLevel > level do
3✔
134
            if reset then
2✔
135
               counter.value[currentLevel] = nil
2✔
136
               counter.display[currentLevel] = nil
2✔
137
            end
138
            currentLevel = currentLevel - 1
2✔
139
         end
140
      end
141
      if options.display then
8✔
142
         counter.display[currentLevel] = options.display
×
143
      end
144
   end, "Increments the value of the multilevel counter <id> at the given <level> or the current level.")
50✔
145

146
   self.commands:register(
84✔
147
      "set-multilevel-counter",
42✔
148
      function (options, _)
149
         local level = SU.cast("integer", SU.required(options, "level", "set-multilevel-counter"))
×
150
         local id = SU.required(options, "id", "set-multilevel-counter")
×
151
         local counter = self.class:getMultilevelCounter(id)
×
152
         local currentLevel = #counter.value
×
153
         if options.value then
×
154
            local value = SU.cast("integer", options.value)
×
155
            if level == currentLevel then
×
156
               -- e.g. set to x the level 3 of 1.2.3 => 1.2.x
157
               counter.value[level] = value
×
158
            elseif level > currentLevel then
×
159
               -- Fill all missing levels in-between, assuming same display format.
160
               -- e.g. set to x the level 3 of 1 => 1.0.x
161
               while level - 1 > currentLevel do
×
162
                  currentLevel = currentLevel + 1
×
163
                  counter.value[currentLevel] = 0
×
164
                  counter.display[currentLevel] = counter.display[currentLevel - 1]
×
165
               end
166
               currentLevel = currentLevel + 1
×
167
               counter.value[level] = value
×
168
               counter.display[level] = counter.display[currentLevel - 1]
×
169
            else -- level < currentLevel
170
               -- Reset all upper levels
171
               -- e.g. set to x the level 2 of 1.2.3 => 1.x
172
               counter.value[level] = value
×
173
               while currentLevel > level do
×
174
                  counter.value[currentLevel] = nil
×
175
                  counter.display[currentLevel] = nil
×
176
                  currentLevel = currentLevel - 1
×
177
               end
178
            end
179
         end
180
         if options.display then
×
181
            if level <= #counter.value then
×
182
               counter.display[level] = options.display
×
183
            else
184
               SU.warn("Ignoring attempt to set the display of a multilevel counter beyond its level")
×
185
            end
186
         end
187
      end,
188
      "Sets the multilevel counter named by the <id> option to <value> at level <level>; optionally sets its display type at that level to <display>."
189
   )
42✔
190

191
   self.commands:register("show-multilevel-counter", function (options, _)
126✔
192
      local id = SU.required(options, "id", "show-multilevel-counter")
9✔
193
      local counter = self.class:getMultilevelCounter(id)
9✔
194
      if options.display then
9✔
195
         SU.deprecated(
×
196
            "\\show-multilevel-counter[display=...]",
197
            "\\set-multilevel-counter[display=...]",
198
            "0.14.4",
199
            "0.16.0"
200
         )
201
      end
202
      SILE.typesetter:typeset(self:formatMultilevelCounter(counter, options))
18✔
203
   end, "Outputs the value of the multilevel counter <id>.")
51✔
204
end
205

206
package.documentation = [[
207
\begin{document}
208

209
Various parts of SILE such as the \autodoc:package{footnotes} package and the sectioning commands keep a counter of things going on: the current footnote number, the chapter number, and so on.
210
The counters package allows you to set up, increment, and typeset named counters.
211
It provides the following commands:
212

213
\begin{itemize}
214
\item{\autodoc:command{\set-counter[id=<counter-name>, value=<value>]}: Sets the counter with the specified name to the given value. The command takes an optional \autodoc:parameter{display=<display-type>} parameter to set the display type of the counter (see below).}
215
\item{\autodoc:command{\increment-counter[id=<counter-name>]}: Increments the counter by one. The command creates the counter if it does not exist and also accepts setting the display type.}
216
\item{\autodoc:command{\show-counter[id=<counter-name>]}: Typesets the value of the counter according to the counter’s declared display type.}
217
\end{itemize}
218

219

220
The available built-in display types are:
221

222
\begin{itemize}
223
\item{\code{arabic}, the default}
224
\item{\code{alpha}, for lower-case alphabetic counting}
225
\item{\code{Alpha}, for upper-case alphabetic counting}
226
\item{\code{roman}, for lower-case Roman numerals}
227
\item{\code{ROMAN}, for upper-case Roman numerals}
228
\item{\code{greek}, for Greek letters in alphabetical order (not Greek numerals)}
229
\end{itemize}
230

231
The ICU library also provides ways of formatting numbers in global (non-Latin) scripts.
232
You can use any of the display types in this list: \href{http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/number.xml}.
233
For example, \autodoc:parameter{display=being} will format your numbers in Bengali digits.
234

235
So, for example, the following SILE code:
236

237
\begin[type=autodoc:codeblock]{raw}
238
\set-counter[id=mycounter, value=2]
239
\show-counter[id=mycounter]
240

241
\increment-counter[id=mycounter, display=roman]
242
\show-counter[id=mycounter]
243
\end{raw}
244

245
produces:
246

247
\fullrule
248
\autodoc:example{
249
\noindent{}2
250

251
\noindent{}iii}
252
\par
253
\fullrule
254

255
The package also provides multi-level (hierarchical) counters, of the kind used in sectioning
256
commands:
257

258
\begin{itemize}
259
\item{\autodoc:command{\set-multilevel-counter[id=<counter-name>, level=<level>, value=<value>]}:
260
Sets the multi-level counter with the specified name to the given value at the given level.
261
The command also takes an optional \autodoc:parameter{display=<display-type>}, also acting at the given level.}
262
\item{\autodoc:command{\increment-multilevel-counter[id=<counter-name>]}:
263
Increments the counter by one at its current (deepest) level.
264
The command creates the counter if it does not exist.
265
If given the \autodoc:parameter{level=<level>} parameter, the command increments that level,
266
clearing any lower level (and filling previous levels with zeros, if they weren’t properly set).
267
It also accepts setting the display type at the target level.}
268
\item{\autodoc:command{\show-multilevel-counter[id=<counter-name>]}:
269
Typesets the value of the multi-level counter according to the counter’s declared display types
270
at each level. By default, all levels are output; option \autodoc:parameter{level=<level>} may be
271
used to display the counter up to a given level. Option \autodoc:parameter{noleadingzeros=true}
272
skips any leading zero (which may happen if a counter is at some level, without previous levels
273
having been set).}
274
\end{itemize}
275
\end{document}
276
]]
42✔
277

278
return package
42✔
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