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

sile-typesetter / sile / 14510870853

17 Apr 2025 07:55AM UTC coverage: 31.472% (-25.8%) from 57.267%
14510870853

push

github

web-flow
Merge pull request #2267 from Omikhleia/feat-csl-position

0 of 109 new or added lines in 2 files covered. (0.0%)

4871 existing lines in 34 files now uncovered.

6341 of 20148 relevant lines covered (31.47%)

2774.57 hits per line

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

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

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

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

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

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

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

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

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

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

76
function package:registerCommands ()
10✔
77
   self:registerCommand("increment-counter", function (options, _)
20✔
78
      local id = SU.required(options, "id", "increment-counter")
×
79
      local counter = self.class:getCounter(id)
×
80
      if options["set-to"] then
×
81
         SU.deprecated("\\increment-counter[set-to=...]", "\\set-counter[value=...]", "0.14.4", "0.16.0")
×
82
         -- An increment command that does a set is plain weird...
83
         counter.value = SU.cast("integer", options["set-to"])
×
84
      else
85
         counter.value = counter.value + 1
×
86
      end
87
      if options.display then
×
88
         counter.display = options.display
×
89
      end
90
   end, "Increments the counter named by the <id> option")
10✔
91

92
   self:registerCommand(
20✔
93
      "set-counter",
10✔
94
      function (options, _)
95
         local id = SU.required(options, "id", "set-counter")
×
96
         local counter = self.class:getCounter(id)
×
97
         if options.value then
×
98
            counter.value = SU.cast("integer", options.value)
×
99
         end
100
         if options.display then
×
101
            counter.display = options.display
×
102
         end
103
      end,
104
      "Sets the counter named by the <id> option to <value>; sets its display type (roman/Roman/arabic) to type <display>."
105
   )
10✔
106

107
   self:registerCommand("show-counter", function (options, _)
20✔
108
      local id = SU.required(options, "id", "show-counter")
×
109
      local counter = self.class:getCounter(id)
×
110
      if options.display then
×
111
         SU.deprecated("\\show-counter[display=...]", "\\set-counter[display=...]", "0.14.4", "0.16.0")
×
112
         counter.display = options.display
×
113
      end
114
      SILE.typesetter:typeset(self:formatCounter(counter))
×
115
   end, "Outputs the value of counter <id>, optionally displaying it with the <display> format.")
10✔
116

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

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

196
   self:registerCommand("show-multilevel-counter", function (options, _)
20✔
UNCOV
197
      local id = SU.required(options, "id", "show-multilevel-counter")
×
UNCOV
198
      local counter = self.class:getMultilevelCounter(id)
×
UNCOV
199
      if options.display then
×
200
         SU.deprecated(
×
201
            "\\show-multilevel-counter[display=...]",
202
            "\\set-multilevel-counter[display=...]",
203
            "0.14.4",
204
            "0.16.0"
205
         )
206
         counter.display[#counter.value] = options.display
×
207
      end
UNCOV
208
      SILE.typesetter:typeset(self:formatMultilevelCounter(counter, options))
×
209
   end, "Outputs the value of the multilevel counter <id>.")
10✔
210
end
211

212
package.documentation = [[
213
\begin{document}
214

215
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.
216
The counters package allows you to set up, increment, and typeset named counters.
217
It provides the following commands:
218

219
\begin{itemize}
220
\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).}
221
\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.}
222
\item{\autodoc:command{\show-counter[id=<counter-name>]}: Typesets the value of the counter according to the counter’s declared display type.}
223
\end{itemize}
224

225

226
The available built-in display types are:
227

228
\begin{itemize}
229
\item{\code{arabic}, the default}
230
\item{\code{alpha}, for lower-case alphabetic counting}
231
\item{\code{Alpha}, for upper-case alphabetic counting}
232
\item{\code{roman}, for lower-case Roman numerals}
233
\item{\code{ROMAN}, for upper-case Roman numerals}
234
\item{\code{greek}, for Greek letters in alphabetical order (not Greek numerals)}
235
\end{itemize}
236

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

241
So, for example, the following SILE code:
242

243
\begin[type=autodoc:codeblock]{raw}
244
\set-counter[id=mycounter, value=2]
245
\show-counter[id=mycounter]
246

247
\increment-counter[id=mycounter, display=roman]
248
\show-counter[id=mycounter]
249
\end{raw}
250

251
produces:
252

253
\fullrule
254
\autodoc:example{
255
\noindent{}2
256

257
\noindent{}iii}
258
\par
259
\fullrule
260

261
The package also provides multi-level (hierarchical) counters, of the kind used in sectioning
262
commands:
263

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

284
return package
10✔
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