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

sile-typesetter / sile / 9428435077

08 Jun 2024 11:35AM UTC coverage: 64.56% (-9.9%) from 74.46%
9428435077

push

github

web-flow
Merge pull request #2047 from alerque/end-pars

23 of 46 new or added lines in 5 files covered. (50.0%)

1684 existing lines in 60 files now uncovered.

11145 of 17263 relevant lines covered (64.56%)

4562.45 hits per line

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

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

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

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

10
SILE.formatMultilevelCounter = function ()
60✔
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)
60✔
45
   return SU.formatNumber(counter.value, { system = counter.display })
139✔
46
end
47

48
function package:formatMultilevelCounter (counter, options)
60✔
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 ()
60✔
66
   base._init(self)
94✔
67
   if not SILE.scratch.counters then
94✔
68
      SILE.scratch.counters = {}
60✔
69
   end
70
   self:export("getCounter", getCounter)
94✔
71
   self:export("getMultilevelCounter", getMultilevelCounter)
94✔
72
   self:deprecatedExport("formatCounter", self.formatCounter)
94✔
73
   self:deprecatedExport("formatMultilevelCounter", self.formatMultilevelCounter)
94✔
74
end
75

76
function package:registerCommands ()
60✔
77
   self:registerCommand("increment-counter", function (options, _)
120✔
UNCOV
78
      local id = SU.required(options, "id", "increment-counter")
×
79

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

93
   self:registerCommand(
120✔
94
      "set-counter",
60✔
95
      function (options, _)
UNCOV
96
         local id = SU.required(options, "id", "set-counter")
×
97

UNCOV
98
         local counter = self.class:getCounter(id)
×
UNCOV
99
         if options.value then
×
UNCOV
100
            counter.value = SU.cast("integer", options.value)
×
101
         end
UNCOV
102
         if options.display then
×
UNCOV
103
            counter.display = options.display
×
104
         end
105
      end,
106
      "Sets the counter named by the <id> option to <value>; sets its display type (roman/Roman/arabic) to type <display>."
107
   )
60✔
108

109
   self:registerCommand("show-counter", function (options, _)
120✔
UNCOV
110
      local id = SU.required(options, "id", "show-counter")
×
111

UNCOV
112
      local counter = self.class:getCounter(id)
×
UNCOV
113
      if options.display then
×
114
         SU.deprecated("\\show-counter[display=...]", "\\set-counter[display=...]", "0.14.4", "0.16.0")
×
115
         counter.display = options.display
×
116
      end
UNCOV
117
      SILE.typesetter:typeset(self:formatCounter(counter))
×
118
   end, "Outputs the value of counter <id>, optionally displaying it with the <display> format.")
60✔
119

120
   self:registerCommand("increment-multilevel-counter", function (options, _)
120✔
121
      local id = SU.required(options, "id", "increment-multilevel-counter")
8✔
122

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

155
   self:registerCommand(
120✔
156
      "set-multilevel-counter",
60✔
157
      function (options, _)
158
         local level = SU.cast("integer", SU.required(options, "level", "set-multilevel-counter"))
×
159
         local id = SU.required(options, "id", "set-multilevel-counter")
×
160

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

201
   self:registerCommand("show-multilevel-counter", function (options, _)
120✔
202
      local id = SU.required(options, "id", "show-multilevel-counter")
9✔
203

204
      local counter = self.class:getMultilevelCounter(id)
9✔
205
      if options.display then
9✔
206
         SU.deprecated(
×
207
            "\\show-multilevel-counter[display=...]",
208
            "\\set-multilevel-counter[display=...]",
209
            "0.14.4",
210
            "0.16.0"
211
         )
212
         counter.display[#counter.value] = options.display
×
213
      end
214

215
      SILE.typesetter:typeset(self:formatMultilevelCounter(counter, options))
18✔
216
   end, "Outputs the value of the multilevel counter <id>.")
69✔
217
end
218

219
package.documentation = [[
220
\begin{document}
221

222
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.
223
The counters package allows you to set up, increment, and typeset named counters.
224
It provides the following commands:
225

226
\begin{itemize}
227
\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).}
228
\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.}
229
\item{\autodoc:command{\show-counter[id=<counter-name>]}: Typesets the value of the counter according to the counter’s declared display type.}
230
\end{itemize}
231

232

233
The available built-in display types are:
234

235
\begin{itemize}
236
\item{\code{arabic}, the default}
237
\item{\code{alpha}, for lower-case alphabetic counting}
238
\item{\code{Alpha}, for upper-case alphabetic counting}
239
\item{\code{roman}, for lower-case Roman numerals}
240
\item{\code{ROMAN}, for upper-case Roman numerals}
241
\item{\code{greek}, for Greek letters in alphabetical order (not Greek numerals)}
242
\end{itemize}
243

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

248
So, for example, the following SILE code:
249

250
\begin[type=autodoc:codeblock]{raw}
251
\set-counter[id=mycounter, value=2]
252
\show-counter[id=mycounter]
253

254
\increment-counter[id=mycounter, display=roman]
255
\show-counter[id=mycounter]
256
\end{raw}
257

258
produces:
259

260
\fullrule
261
\autodoc:example{
262
\noindent{}2
263

264
\noindent{}iii}
265
\par
266
\fullrule
267

268
The package also provides multi-level (hierarchical) counters, of the kind used in sectioning
269
commands:
270

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

291
return package
60✔
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