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

sile-typesetter / sile / 11534409649

26 Oct 2024 07:27PM UTC coverage: 33.196% (-28.7%) from 61.897%
11534409649

push

github

alerque
chore(tooling): Update editor-config key for stylua as accepted upstream

Our setting addition is still not in a tagged release, but the PR was
accepted into the default branch of stylua. This means you no longer
need to run my fork of Stylua to get this project's style, you just nead
any build from the main development branch. However the config key was
renamed as part of the acceptance, so this is the relevant adjustment.

5810 of 17502 relevant lines covered (33.2%)

1300.57 hits per line

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

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

4
local deprecator = function ()
5
   SU.deprecated("SILE.settings.*", "SILE.settings:*", "0.13.0", "0.15.0")
×
6
end
7

8
--- @type settings
9
local settings = pl.class()
17✔
10

11
function settings:_init ()
17✔
12
   self.state = {}
17✔
13
   self.declarations = {}
17✔
14
   self.stateQueue = {}
17✔
15
   self.defaults = {}
17✔
16
   self.hooks = {}
17✔
17

18
   self:declare({
34✔
19
      parameter = "document.language",
20
      type = "string",
21
      default = "en",
22
      hook = function (language)
23
         if SILE.scratch.loaded_languages and not SILE.scratch.loaded_languages[language] then
23✔
24
            SU.warn(([[
2✔
25
               Setting document.language to '%s', but support for '%s' has not been loaded
26

27
               Consider invoking \language[main=%s] which loads language support before
28
               setting it or manually calling SILE.languageSupport.loadLanguage("%s").
29
            ]]):format(language, language, language, language))
1✔
30
         end
31
         fluent:set_locale(language)
23✔
32
      end,
33
      help = "Locale for localized language support",
34
   })
35

36
   self:declare({
34✔
37
      parameter = "document.parindent",
38
      type = "glue",
39
      default = SILE.types.node.glue("1bs"),
51✔
40
      help = "Glue at start of paragraph",
41
   })
42

43
   self:declare({
34✔
44
      parameter = "document.baselineskip",
45
      type = "vglue",
46
      default = SILE.types.node.vglue("1.2em plus 1pt"),
34✔
47
      help = "Leading",
48
   })
49

50
   self:declare({
34✔
51
      parameter = "document.lineskip",
52
      type = "vglue",
53
      default = SILE.types.node.vglue("1pt"),
34✔
54
      help = "Leading",
55
   })
56

57
   self:declare({
34✔
58
      parameter = "document.parskip",
59
      type = "vglue",
60
      default = SILE.types.node.vglue("0pt plus 1pt"),
34✔
61
      help = "Leading",
62
   })
63

64
   self:declare({
17✔
65
      parameter = "document.spaceskip",
66
      type = "length or nil",
67
      default = nil,
68
      help = "The length of a space (if nil, then measured from the font)",
69
   })
70

71
   self:declare({
17✔
72
      parameter = "document.rskip",
73
      type = "glue or nil",
74
      default = nil,
75
      help = "Skip to be added to right side of line",
76
   })
77

78
   self:declare({
17✔
79
      parameter = "document.lskip",
80
      type = "glue or nil",
81
      default = nil,
82
      help = "Skip to be added to left side of line",
83
   })
84

85
   self:declare({
17✔
86
      parameter = "document.zenkakuchar",
87
      default = "あ",
88
      type = "string",
89
      help = "The character measured to determine the length of a zenkaku width (全角幅)",
90
   })
91

92
   SILE.registerCommand(
34✔
93
      "set",
17✔
94
      function (options, content)
95
         local makedefault = SU.boolean(options.makedefault, false)
13✔
96
         local reset = SU.boolean(options.reset, false)
13✔
97
         local value = options.value
13✔
98
         if content and (type(content) == "function" or content[1]) then
13✔
99
            if makedefault then
×
100
               SU.warn(
×
101
                  "Are you sure meant to set default settings *and* pass content to ostensibly apply them to temporarily?"
102
               )
103
            end
104
            self:temporarily(function ()
×
105
               if options.parameter then
×
106
                  local parameter = SU.required(options, "parameter", "\\set command")
×
107
                  self:set(parameter, value, makedefault, reset)
×
108
               end
109
               SILE.process(content)
×
110
            end)
111
         else
112
            local parameter = SU.required(options, "parameter", "\\set command")
13✔
113
            self:set(parameter, value, makedefault, reset)
13✔
114
         end
115
      end,
116
      "Set a SILE parameter <parameter> to value <value> (restoring the value afterwards if <content> is provided)",
17✔
117
      nil,
17✔
118
      true
119
   )
17✔
120
end
121

122
--- Stash the current values of all settings in a stack to be returned to later
123
function settings:pushState ()
17✔
124
   if not self then
53✔
125
      return deprecator()
×
126
   end
127
   table.insert(self.stateQueue, self.state)
53✔
128
   self.state = pl.tablex.copy(self.state)
106✔
129
end
130

131
--- Return the most recently pushed set of values in the setting stack
132
function settings:popState ()
17✔
133
   if not self then
53✔
134
      return deprecator()
×
135
   end
136
   local previous = self.state
53✔
137
   self.state = table.remove(self.stateQueue)
106✔
138
   for parameter, oldvalue in pairs(previous) do
2,734✔
139
      if self.hooks[parameter] then
2,681✔
140
         local newvalue = self.state[parameter]
2,681✔
141
         if oldvalue ~= newvalue then
2,681✔
142
            self:runHooks(parameter, newvalue)
79✔
143
         end
144
      end
145
   end
146
end
147

148
--- Declare a new setting
149
--- @tparam table specs { parameter, type, default, help, hook, ... } declaration specification
150
function settings:declare (spec)
17✔
151
   if not spec then
1,062✔
152
      return deprecator()
×
153
   end
154
   if spec.name then
1,062✔
155
      SU.deprecated(
×
156
         "'name' argument of SILE.settings:declare",
157
         "'parameter' argument of SILE.settings:declare",
158
         "0.10.10",
159
         "0.11.0"
160
      )
161
   end
162
   if self.declarations[spec.parameter] then
1,062✔
163
      SU.debug("settings", "Attempt to re-declare setting:", spec.parameter)
208✔
164
      return
208✔
165
   end
166
   self.declarations[spec.parameter] = spec
854✔
167
   self.hooks[spec.parameter] = {}
854✔
168
   if spec.hook then
854✔
169
      self:registerHook(spec.parameter, spec.hook)
17✔
170
   end
171
   self:set(spec.parameter, spec.default, true)
854✔
172
end
173

174
--- Reset all settings to their registered default values.
175
function settings:reset ()
17✔
176
   if not self then
×
177
      return deprecator()
×
178
   end
179
   for k, _ in pairs(self.state) do
×
180
      self:set(k, self.defaults[k])
×
181
   end
182
end
183

184
--- Restore all settings to the value they had in the top-level state,
185
-- that is at the tap of the settings stack (normally the document level).
186
function settings:toplevelState ()
17✔
187
   if not self then
15✔
188
      return deprecator()
×
189
   end
190
   if #self.stateQueue ~= 0 then
15✔
191
      for parameter, _ in pairs(self.state) do
763✔
192
         -- Bypass self:set() as the latter performs some tests and a cast,
193
         -- but the setting might not have been defined in the top level state
194
         -- (in which case, assume the default value).
195
         self.state[parameter] = self.stateQueue[1][parameter] or self.defaults[parameter]
748✔
196
      end
197
   end
198
end
199

200
--- Get the value of a setting
201
-- @tparam string parameter The full name of the setting to fetch.
202
-- @return Value of setting
203
function settings:get (parameter)
17✔
204
   -- HACK FIXME https://github.com/sile-typesetter/sile/issues/1699
205
   -- See comment on set() below.
206
   if parameter == "current.parindent" then
11,310✔
207
      return SILE.typesetter and SILE.typesetter.state.parindent
91✔
208
   end
209
   if not parameter then
11,219✔
210
      return deprecator()
×
211
   end
212
   if not self.declarations[parameter] then
11,219✔
213
      SU.error("Undefined setting '" .. parameter .. "'")
×
214
   end
215
   if type(self.state[parameter]) ~= "nil" then
11,219✔
216
      return self.state[parameter]
5,452✔
217
   else
218
      return self.defaults[parameter]
5,767✔
219
   end
220
end
221

222
--- Set the value of a setting
223
-- @tparam string parameter The full name of the setting to change.
224
-- @param value The new value to change it to.
225
-- @tparam[opt=false] boolean makedefault Whether to make this the new default value.
226
-- @tparam[opt=false] boolean reset Whether to reset the value to the current default value.
227
function settings:set (parameter, value, makedefault, reset)
17✔
228
   -- HACK FIXME https://github.com/sile-typesetter/sile/issues/1699
229
   -- Anything dubbed current.xxx should likely NOT be a "setting" (subject
230
   -- to being pushed/popped via temporary stacking) and actually has its
231
   -- own lifecycle (e.g. reset for the next paragraph).
232
   -- These should be rather typesetter states, or something to that extent
233
   -- yet to clarify. Notably, current.parindent falls in that category,
234
   -- BUT probably current.hangAfter and current.hangIndent too.
235
   -- To avoid breaking too much code yet without being sure of the solution,
236
   -- we implement a hack of sorts for current.parindent only.
237
   -- Note moreover that current.parindent is currently probably a bad concept
238
   -- anyway:
239
   --   - It can be nil (= document.parindent applies)
240
   --   - It can be a zero-glue (\noindent, ragged environments, etc.)
241
   --   - It can be a valued glue set to document.parindent
242
   --     (e.g. from \indent, and document.parindent thus applies)
243
   --   - It could be another valued glue (uh, use case to ascertain)
244
   -- What we would _likely_ only need to track is whether document.parindent
245
   -- applies or not on the paragraph just composed afterwards...
246
   if parameter == "current.parindent" then
1,171✔
247
      if SILE.typesetter and not SILE.typesetter.state.hmodeOnly then
137✔
248
         SILE.typesetter.state.parindent = SU.cast("glue or nil", value)
254✔
249
      end
250
      return
137✔
251
   end
252
   if type(self) ~= "table" then
1,034✔
253
      return deprecator()
×
254
   end
255
   if not self.declarations[parameter] then
1,034✔
256
      SU.error("Undefined setting '" .. parameter .. "'")
×
257
   end
258
   if reset then
1,034✔
259
      if makedefault then
4✔
260
         SU.error("Can't set a new default and revert to and old default setting at the same time")
×
261
      end
262
      value = self.defaults[parameter]
4✔
263
   else
264
      value = SU.cast(self.declarations[parameter].type, value)
2,060✔
265
   end
266
   self.state[parameter] = value
1,034✔
267
   if makedefault then
1,034✔
268
      self.defaults[parameter] = value
861✔
269
   end
270
   self:runHooks(parameter, value)
1,034✔
271
end
272

273
--- Register a callback hook to be run when a setting changes.
274
-- @tparam string parameter Name of the setting to add a hook to.
275
-- @tparam function func Callback function accepting one argument (the new value).
276
function settings:registerHook (parameter, func)
17✔
277
   table.insert(self.hooks[parameter], func)
17✔
278
end
279

280
--- Trigger execution of callback hooks for a given setting.
281
-- @tparam string parameter The name of the parameter changes.
282
-- @param value The new value of the setting, passed as the first argument to the hook function.
283
function settings:runHooks (parameter, value)
17✔
284
   if self.hooks[parameter] then
1,113✔
285
      for _, func in ipairs(self.hooks[parameter]) do
1,136✔
286
         SU.debug("classhooks", "Running setting hook for", parameter)
23✔
287
         func(value)
23✔
288
      end
289
   end
290
end
291

292
--- Isolate a block of processing so that setting changes made during the block don't last past the block.
293
-- (Under the hood this just uses `:pushState()`, the processes the function, then runs `:popState()`)
294
-- @tparam function func A function wrapping the actions to take without affecting settings for future use.
295
function settings:temporarily (func)
17✔
296
   if not func then
31✔
297
      return deprecator()
×
298
   end
299
   self:pushState()
31✔
300
   func()
31✔
301
   self:popState()
31✔
302
end
303

304
--- Create a settings wrapper function that applies current settings to later content processing.
305
--- @treturn function a closure function accepting one argument (content) to process using
306
--- typesetter settings as they are at the time of closure creation.
307
function settings:wrap ()
17✔
308
   if not self then
×
309
      return deprecator()
×
310
   end
311
   local clSettings = pl.tablex.copy(self.state)
×
312
   return function (content)
313
      table.insert(self.stateQueue, self.state)
×
314
      self.state = clSettings
×
315
      SILE.process(content)
×
316
      self:popState()
×
317
   end
318
end
319

320
return settings
17✔
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