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

sile-typesetter / sile / 6915746301

18 Nov 2023 07:02PM UTC coverage: 56.433% (-12.3%) from 68.751%
6915746301

push

github

web-flow
Merge 8b3fdc301 into f64e235fa

8729 of 15468 relevant lines covered (56.43%)

932.75 hits per line

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

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

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

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

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

44
function package.formatCounter (_, counter)
18✔
45
  return SU.formatNumber(counter.value, { system = counter.display })
36✔
46
end
47

48
function package:formatMultilevelCounter (counter, options)
18✔
49
  options = options or {}
×
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?
52
  local minlevel = options.minlevel and SU.min(SU.cast("integer", options.minlevel, #counter.value)) or 1
×
53
  local out = {}
×
54
  if SU.boolean(options.noleadingzeros, false) then
×
55
    while counter.value[minlevel] == 0 do minlevel = minlevel + 1 end -- skip leading zeros
×
56
  end
57
  for x = minlevel, maxlevel do
×
58
    out[x - minlevel + 1] = self:formatCounter({ display = counter.display[x], value = counter.value[x] })
×
59
  end
60
  return table.concat(out, ".")
×
61
end
62

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

74
function package:registerCommands ()
18✔
75

76
  self:registerCommand("increment-counter", function (options, _)
36✔
77
    local id = SU.required(options, "id", "increment-counter")
×
78

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 counter.display = options.display end
×
88
  end, "Increments the counter named by the <id> option")
18✔
89

90
  self:registerCommand("set-counter", function (options, _)
36✔
91
    local id = SU.required(options, "id", "set-counter")
×
92

93
    local counter = self.class:getCounter(id)
×
94
    if options.value then counter.value = SU.cast("integer", options.value) end
×
95
    if options.display then counter.display = options.display end
×
96
  end, "Sets the counter named by the <id> option to <value>; sets its display type (roman/Roman/arabic) to type <display>.")
18✔
97

98

99
  self:registerCommand("show-counter", function (options, _)
36✔
100
    local id = SU.required(options, "id", "show-counter")
×
101

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

110
  self:registerCommand("increment-multilevel-counter", function (options, _)
36✔
111
    local id = SU.required(options, "id", "increment-multilevel-counter")
×
112

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

143
  self:registerCommand("set-multilevel-counter", function (options, _)
36✔
144
    local level = SU.cast("integer", SU.required(options, "level", "set-multilevel-counter"))
×
145
    local id = SU.required(options, "id", "set-multilevel-counter")
×
146

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

185
  self:registerCommand("show-multilevel-counter", function (options, _)
36✔
186
    local id = SU.required(options, "id", "show-multilevel-counter")
×
187

188
    local counter = self.class:getMultilevelCounter(id)
×
189
    if options.display then
×
190
      SU.deprecated("\\show-multilevel-counter[display=...]", '\\set-multilevel-counter[display=...]', "0.14.4", "0.16.0")
×
191
      counter.display[#counter.value] = options.display
×
192
    end
193

194
    SILE.typesetter:typeset(self:formatMultilevelCounter(counter, options))
×
195
  end, "Outputs the value of the multilevel counter <id>.")
18✔
196

197
end
198

199
package.documentation = [[
200
\begin{document}
201

202
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.
203
The counters package allows you to set up, increment, and typeset named counters.
204
It provides the following commands:
205

206
\begin{itemize}
207
\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).}
208
\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.}
209
\item{\autodoc:command{\show-counter[id=<counter-name>]}: Typesets the value of the counter according to the counter’s declared display type.}
210
\end{itemize}
211

212

213
The available built-in display types are:
214

215
\begin{itemize}
216
\item{\code{arabic}, the default}
217
\item{\code{alpha}, for lower-case alphabetic counting}
218
\item{\code{Alpha}, for upper-case alphabetic counting}
219
\item{\code{roman}, for lower-case Roman numerals}
220
\item{\code{ROMAN} for upper-case Roman numerals}
221
\end{itemize}
222

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

227
So, for example, the following SILE code:
228

229
\begin[type=autodoc:codeblock]{raw}
230
\set-counter[id=mycounter, value=2]
231
\show-counter[id=mycounter]
232

233
\increment-counter[id=mycounter, display=roman]
234
\show-counter[id=mycounter]
235
\end{raw}
236

237
produces:
238

239
\fullrule
240
\autodoc:example{
241
\noindent{}2
242

243
\noindent{}iii}
244
\par
245
\fullrule
246

247
The package also provides multi-level (hierarchical) counters, of the kind used in sectioning
248
commands:
249

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

270
return package
18✔
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