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

sile-typesetter / sile / 14860011647

06 May 2025 12:44PM UTC coverage: 67.057% (+32.5%) from 34.559%
14860011647

push

github

alerque
chore(typesetters): Fixup access to class from typesetter functions

7 of 7 new or added lines in 2 files covered. (100.0%)

1344 existing lines in 103 files now uncovered.

14880 of 22190 relevant lines covered (67.06%)

11549.16 hits per line

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

91.95
/core/settings.lua
1
--- core settings registry instance
2
--- @module SILE.settings
3

4
--- @type settings
5
local registry = require("types.registry")
302✔
6
local settings = pl.class(registry)
302✔
7
settings._name = "settings"
302✔
8

9
function settings:_init ()
302✔
10
   registry._init(self)
302✔
11
   self.state = {}
302✔
12
   self.stateQueue = {}
302✔
13
   self.hooks = {}
302✔
14
end
15

16
--- Stash the current values of all settings in a stack to be returned to later
17
function settings:pushState (_parent)
302✔
18
   table.insert(self.stateQueue, self.state)
1,524✔
19
   self.state = pl.tablex.copy(self.state)
3,048✔
20
end
21

22
--- Return the most recently pushed set of values in the setting stack
23
function settings:popState (parent)
302✔
24
   local previous = self.state
1,528✔
25
   self.state = table.remove(self.stateQueue)
3,056✔
26
   for parameter, oldvalue in pairs(previous) do
87,467✔
27
      if self.hooks[parameter] then
85,939✔
28
         local newvalue = self.state[parameter]
85,939✔
29
         if newvalue == nil then
85,939✔
30
            self:reset(parent, parameter)
1,064✔
31
         else
32
            if oldvalue ~= newvalue then
85,407✔
33
               self:set(parent, parameter, newvalue)
1,472✔
34
            end
35
         end
36
      end
37
   end
38
   self:_runAllHooks(parent)
1,528✔
39
end
40

41
--- Declare a new setting
42
--- @tparam table spec { parameter, type, default, help, hook, ... } declaration specification
43
function settings:declare (parent, spec)
302✔
44
   if self:exists(parent, spec.parameter) then
19,582✔
UNCOV
45
      SU.debug("settings", "WARNING: Redeclaring setting", spec.parameter)
×
46
   else
47
      self._registry[spec.parameter] = {}
9,791✔
48
      self.hooks[spec.parameter] = {}
9,791✔
49
      if spec.hook then
9,791✔
UNCOV
50
         self:registerHook(parent, spec.parameter, spec.hook)
×
51
      end
52
   end
53
   local callback = function (value)
54
      self:runHooks(parent, spec.parameter, value)
21,059✔
55
   end
56
   local setting = SILE.types.setting(spec.parameter, spec.type, spec.default, spec.help, callback)
9,791✔
57
   return self:push(parent, setting)
9,791✔
58
end
59

60
--- Reset all settings to their registered default values.
61
function settings:reset (_parent)
302✔
UNCOV
62
   for _, setting in pairs(self.state) do
×
UNCOV
63
      setting:reset()
×
64
   end
65
end
66

67
--- Restore all settings to the value they had in the top-level state,
68
-- that is at the tap of the settings stack (normally the document level).
69
function settings:toplevelState (parent)
302✔
70
   if #self.stateQueue ~= 0 then
126✔
71
      for parameter, _ in pairs(self.state) do
6,804✔
72
         -- Bypass self:set() as the latter performs some tests and a cast,
73
         -- but the setting might not have been defined in the top level state
74
         local setting = self:pull(parent, parameter)
6,678✔
75
         setting.value = self.stateQueue[1][parameter]
6,678✔
76
      end
77
   end
78
end
79

80
--- Get the value of a setting
81
-- @tparam string parameter The full name of the setting to fetch.
82
-- @return Value of setting
83
function settings:get (parent, parameter)
302✔
84
   -- HACK FIXME https://github.com/sile-typesetter/sile/issues/1699
85
   -- See comment on set() below.
86
   if parameter == "current.parindent" then
241,684✔
87
      return SILE.typesetter and SILE.typesetter.state.parindent
1,377✔
88
   end
89
   local setting = self:pull(parent, parameter)
240,307✔
90
   if not setting then
240,307✔
UNCOV
91
      SU.error("Undefined setting '" .. parameter .. "'")
×
92
   end
93
   return setting:get()
240,307✔
94
end
95

96
--- Set the value of a setting
97
-- @tparam string parameter The full name of the setting to change.
98
-- @param value The new value to change it to.
99
-- @tparam[opt=false] boolean makedefault Whether to make this the new default value.
100
-- @tparam[opt=false] boolean reset Whether to reset the value to the current default value.
101
function settings:set (parent, parameter, value, makedefault, reset)
302✔
102
   -- HACK FIXME https://github.com/sile-typesetter/sile/issues/1699
103
   -- Anything dubbed current.xxx should likely NOT be a "setting" (subject
104
   -- to being pushed/popped via temporary stacking) and actually has its
105
   -- own lifecycle (e.g. reset for the next paragraph).
106
   -- These should be rather typesetter states, or something to that extent
107
   -- yet to clarify. Notably, current.parindent falls in that category,
108
   -- BUT probably current.hangAfter and current.hangIndent too.
109
   -- To avoid breaking too much code yet without being sure of the solution,
110
   -- we implement a hack of sorts for current.parindent only.
111
   -- Note moreover that current.parindent is currently probably a bad concept
112
   -- anyway:
113
   --   - It can be nil (= document.parindent applies)
114
   --   - It can be a zero-glue (\noindent, ragged environments, etc.)
115
   --   - It can be a valued glue set to document.parindent
116
   --     (e.g. from \indent, and document.parindent thus applies)
117
   --   - It could be another valued glue (uh, use case to ascertain)
118
   -- What we would _likely_ only need to track is whether document.parindent
119
   -- applies or not on the paragraph just composed afterwards...
120
   if parameter == "current.parindent" then
13,972✔
121
      if SILE.typesetter and not SILE.typesetter.state.hmodeOnly then
2,047✔
122
         SILE.typesetter.state.parindent = SU.cast("glue or nil", value)
4,064✔
123
      end
124
      return
2,047✔
125
   end
126
   local setting = self:pull(parent, parameter)
11,925✔
127
   if not setting then
11,925✔
UNCOV
128
      SU.error("Undefined setting '" .. parameter .. "'")
×
129
   end
130
   if reset then
11,925✔
131
      if makedefault then
4✔
UNCOV
132
         SU.error("Can't set a new default and revert to and old default setting at the same time")
×
133
      end
134
      -- SU.deprecated("settings:set(parameter, _, _, _, true)", "settings:reset(parameter)", "0.16.0", "0.17.0")
135
      setting:reset()
8✔
136
   else
137
      -- if makedefault then
138
      --    SU.deprecated("settings:set(parameter, value, _, true)", "settings:setDefault(parameter, value)", "0.16.0", "0.17.0")
139
      -- end
140
      setting:set(value, makedefault)
11,921✔
141
   end
142
end
143

144
function settings:reset (parent, parameter)
302✔
145
   local setting = self:pull(parent, parameter)
532✔
146
   setting:reset()
532✔
147
end
148

149
--- Register a callback hook to be run when a setting changes.
150
-- @tparam string parameter Name of the setting to add a hook to.
151
-- @tparam function func Callback function accepting one argument (the new value).
152
function settings:registerHook (_parent, parameter, func)
302✔
153
   table.insert(self.hooks[parameter], func)
489✔
154
end
155

156
--- Trigger execution of callback hooks for a given setting.
157
-- @tparam string parameter The name of the parameter changes.
158
-- @param value The new value of the setting, passed as the first argument to the hook function.
159
function settings:runHooks (_parent, parameter, value)
302✔
160
   self.state[parameter] = value
123,238✔
161
   if self.hooks[parameter] then
123,238✔
162
      for _, func in ipairs(self.hooks[parameter]) do
129,460✔
163
         SU.debug("settings", "Running setting hook for", parameter)
6,222✔
164
         func(value)
6,222✔
165
      end
166
   end
167
end
168

169
function settings:_runAllHooks (parent)
302✔
170
   SU.debug("settings", "Running all hooks after push/pop")
1,528✔
171
   for parameter, _ in pairs(self.hooks) do
103,707✔
172
      local setting = self:pull(parent, parameter)
102,179✔
173
      self:runHooks(parent, parameter, setting:get())
204,358✔
174
   end
175
end
176

177
--- Isolate a block of processing so that setting changes made during the block don't last past the block.
178
-- (Under the hood this just uses `:pushState()`, the processes the function, then runs `:popState()`)
179
-- @tparam function func A function wrapping the actions to take without affecting settings for future use.
180
function settings:temporarily (parent, func)
302✔
181
   self:pushState(parent)
1,170✔
182
   func()
1,170✔
183
   self:popState(parent)
1,170✔
184
end
185

186
--- Create a settings wrapper function that applies current settings to later content processing.
187
--- @treturn function a closure function accepting one argument (content) to process using
188
--- typesetter settings as they are at the time of closure creation.
189
function settings:wrap (parent)
302✔
190
   local clSettings = pl.tablex.copy(self.state)
13✔
191
   return function (content)
192
      table.insert(self.stateQueue, self.state)
4✔
193
      self.state = clSettings
4✔
194
      SILE.process(content)
4✔
195
      self:popState(parent)
4✔
196
   end
197
end
198

199
return settings
302✔
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