• 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

0.0
/languages/fr/init.lua
1
--- French language rules
2
-- @submodule languages
3

4
local computeSpaces = function ()
5
   -- Computes:
6
   --  -  regular inter-word space,
7
   --  -  half inter-word fixed space,
8
   --  -  "guillemet space", as defined in LaTeX's babel-french which is based
9
   --     on Thierry Bouche's recommendations,
10
   --  These should be usual for France and Canada. The Swiss may prefer a thin
11
   --  space for guillemets, that's why we are having settings hereafter.
12
   local enlargement = SILE.settings:get("shaper.spaceenlargementfactor")
×
13
   local stretch = SILE.settings:get("shaper.spacestretchfactor")
×
14
   local shrink = SILE.settings:get("shaper.spaceshrinkfactor")
×
15
   return {
×
16
      colonspace = SILE.types.length(enlargement .. "spc plus " .. stretch .. "spc minus " .. shrink .. "spc"),
17
      thinspace = SILE.types.length((0.5 * enlargement) .. "spc"),
18
      guillspace = SILE.types.length(
×
19
         (0.8 * enlargement) .. "spc plus " .. (0.3 * stretch) .. "spc minus " .. (0.8 * shrink) .. "spc"
×
20
      ),
×
21
   }
22
end
23

24
local spaces = computeSpaces()
×
25
-- NOTE: We are only doing it at load time. We don't expect the shaper settings to be often
26
-- changed arbitrarily _after_ having selected a language...
27

28
SILE.settings:declare({
×
29
   parameter = "languages.fr.colonspace",
30
   type = "kern",
31
   default = SILE.types.node.kern(spaces.colonspace),
32
   help = "The amount of space before a colon, theoretically a non-breakable, shrinkable, stretchable inter-word space",
33
})
34

35
SILE.settings:declare({
×
36
   parameter = "languages.fr.thinspace",
37
   type = "kern",
38
   default = SILE.types.node.kern(spaces.thinspace),
39
   help = "The amount of space before high punctuations, theoretically a fixed, non-breakable space, around half the inter-word space",
40
})
41

42
SILE.settings:declare({
×
43
   parameter = "languages.fr.guillspace",
44
   type = "kern",
45
   default = SILE.types.node.kern(spaces.guillspace),
46
   help = "The amount of space applying to guillemets, theoretically smaller than a non-breakable inter-word space, with reduced stretchability",
47
})
48

49
SILE.settings:declare({
×
50
   parameter = "languages.fr.debugspace",
51
   type = "boolean",
52
   default = false,
53
   help = "If switched to true, uses large spaces instead of the regular punctuation ones",
54
})
55

56
local getSpaceGlue = function (options, parameter)
57
   local sg
58
   if SILE.settings:get("languages.fr.debugspace") then
×
59
      sg = SILE.types.node.kern("5spc")
×
60
   else
61
      sg = SILE.settings:get(parameter)
×
62
   end
63
   -- Return the absolute (kern) length of the specified spacing parameter
64
   -- with a particular set of font options.
65
   -- As for SILE.shapers.base.measureSpace(), which has the same type of
66
   -- logic, caching this doesn't seem to have any significant speedup.
67
   SILE.settings:temporarily(function ()
×
68
      SILE.settings:set("font.size", options.size)
×
69
      SILE.settings:set("font.family", options.family)
×
70
      SILE.settings:set("font.filename", options.filename)
×
71
      sg = sg:absolute()
×
72
   end)
73
   -- Track a subtype on that kern:
74
   -- See automated italic correction at the typesetter level.
75
   sg.subtype = "punctspace"
×
76
   return sg
×
77
end
78

79
SILE.nodeMakers.fr = pl.class(SILE.nodeMakers.unicode)
×
80

81
-- Unfortunately, there is nothing in the Unicode properties
82
-- database which distinguishes between high and low punctuation, etc.
83
-- But in a way that's precisely why we can't just rely on Unicode
84
-- for everything and need our language-specific typesetting
85
-- processors.
86
SILE.nodeMakers.fr.colonPunctuations = { ":" }
×
87
SILE.nodeMakers.fr.openingQuotes = { "«", "‹" }
×
88
SILE.nodeMakers.fr.closingQuotes = { "»", "›" }
×
89
-- There's catch below: the shaper may have already processed common ligatures (!!, ?!, !?)
90
-- as a single item...
91
SILE.nodeMakers.fr.highPunctuations = { ";", "!", "?", "!!", "?!", "!?" }
×
92
-- High punctuations have some (kern) space before them... except in some cases!
93
-- By the books, they have it "after a letter or digit", at least. After a closing
94
-- punctuation, too, seems usual.
95
-- Otherwise, one shall have no space inside e.g. (?), ?!, [!], …?, !!! etc.
96
-- As a simplification, we reverse the rule and define after which characters the space
97
-- shall not be added. This is by no mean perfect, I couldn't find an explicit list
98
-- of exceptions. French typography is a delicate beast.
99
SILE.nodeMakers.fr.spaceExceptions =
×
100
   { "!", "?", ":", ".", "…", "(", "[", "{", "<", "«", "‹", "“", "‘", "?!", "!!", "!?" }
×
101

102
-- overridden properties from parent class
103
SILE.nodeMakers.fr.quoteTypes = { qu = true } -- split tokens at apostrophes &c.
×
104

105
-- methods defined in this class
106

107
function SILE.nodeMakers.fr.isIn (_, set, text)
×
108
   for _, v in ipairs(set) do
×
109
      if v == text then
×
110
         return true
×
111
      end
112
   end
113
   return false
×
114
end
115

116
function SILE.nodeMakers.fr:isOpeningQuote (text)
×
117
   return self:isIn(self.openingQuotes, text)
×
118
end
119

120
function SILE.nodeMakers.fr:isClosingQuote (text)
×
121
   return self:isIn(self.closingQuotes, text)
×
122
end
123

124
function SILE.nodeMakers.fr:isColonPunctuation (text)
×
125
   return self:isIn(self.colonPunctuations, text)
×
126
end
127

128
function SILE.nodeMakers.fr:isHighPunctuation (text)
×
129
   return self:isIn(self.highPunctuations, text)
×
130
end
131

132
function SILE.nodeMakers.fr:isSpaceException (text)
×
133
   return self:isIn(self.spaceExceptions, text)
×
134
end
135

136
function SILE.nodeMakers.fr:isPrevSpaceException ()
×
137
   return self.i > 1 and self:isSpaceException(self.items[self.i - 1].text) or false
×
138
end
139

140
function SILE.nodeMakers.fr:makeUnbreakableSpace (parameter)
×
141
   self:makeToken()
×
142
   self.lastnode = "glue"
×
143
   coroutine.yield(getSpaceGlue(self.options, parameter))
×
144
end
145

146
function SILE.nodeMakers.fr:handleSpaceBefore (item)
×
147
   if self:isHighPunctuation(item.text) and not self:isPrevSpaceException() then
×
148
      self:makeUnbreakableSpace("languages.fr.thinspace")
×
149
      self:makeToken()
×
150
      self:addToken(item.text, item)
×
151
      return true
×
152
   end
153
   if self:isColonPunctuation(item.text) and not self:isPrevSpaceException() then
×
154
      self:makeUnbreakableSpace("languages.fr.colonspace")
×
155
      self:makeToken()
×
156
      self:addToken(item.text, item)
×
157
      return true
×
158
   end
159
   if self:isClosingQuote(item.text) then
×
160
      self:makeUnbreakableSpace("languages.fr.guillspace")
×
161
      self:makeToken()
×
162
      self:addToken(item.text, item)
×
163
      return true
×
164
   end
165
   return false
×
166
end
167

168
function SILE.nodeMakers.fr:handleSpaceAfter (item)
×
169
   if self:isOpeningQuote(item.text) then
×
170
      self:addToken(item.text, item)
×
171
      self:makeUnbreakableSpace("languages.fr.guillspace")
×
172
      self:makeToken()
×
173
      return true
×
174
   end
175
   return false
×
176
end
177

178
function SILE.nodeMakers.fr:mustRemove (i, items)
×
179
   -- Clear "manual" spaces we do not want, so that later we only have to
180
   -- insert the relevant kerns.
181
   local curr = items[i].text
×
182
   if self:isSpace(curr) or self:isNonBreakingSpace(curr) then
×
183
      if i < #items then
×
184
         local next = items[i + 1].text
×
185
         if
186
            self:isSpace(next)
×
187
            or self:isNonBreakingSpace(next)
×
188
            or self:isHighPunctuation(next)
×
189
            or self:isColonPunctuation(next)
×
190
            or self:isClosingQuote(next)
×
191
         then
192
            return true
×
193
         end
194
      end
195
      if i > 1 then
×
196
         local prev = items[i - 1].text
×
197
         if self:isOpeningQuote(prev) then
×
198
            return true
×
199
         end
200
      end
201
   end
202
   return false
×
203
end
204

205
-- overridden methods from parent class
206

207
function SILE.nodeMakers.fr:dealWith (item)
×
208
   if self:handleSpaceBefore(item) then
×
209
      return
×
210
   end
211
   if self:handleSpaceAfter(item) then
×
212
      return
×
213
   end
214
   self._base.dealWith(self, item)
×
215
end
216

217
function SILE.nodeMakers.fr:handleWordBreak (item)
×
218
   if self:handleSpaceBefore(item) then
×
219
      return
×
220
   end
221
   if self:handleSpaceAfter(item) then
×
222
      return
×
223
   end
224
   self._base.handleWordBreak(self, item)
×
225
end
226

227
function SILE.nodeMakers.fr:handleLineBreak (item, subtype)
×
228
   if self:isSpace(item.text) then
×
229
      self:handleWordBreak(item)
×
230
      return
×
231
   end
232
   if self:handleSpaceBefore(item) then
×
233
      return
×
234
   end
235
   if self:handleSpaceAfter(item) then
×
236
      return
×
237
   end
238

239
   self._base.handleLineBreak(self, item, subtype)
×
240
end
241

242
function SILE.nodeMakers.fr:iterator (items)
×
243
   -- We start by cleaning up the input once for all.
244
   local cleanItems = {}
×
245
   local removed = 0
×
246
   for k = 1, #items do
×
247
      if self:mustRemove(k, items) then
×
248
         -- the index is actually a character position in the byte stream.
249
         -- So we need to take its actual byte length into account.
250
         -- For instance, U+00A0 NBSP is 2 bytes long (0xC2 0xA0) in UTF-8.
251
         removed = removed + string.len(items[k].text)
×
252
      else
253
         -- index has changed due to removals
254
         items[k].index = items[k].index - removed
×
255
         table.insert(cleanItems, items[k])
×
256
      end
257
   end
258
   return self._base.iterator(self, cleanItems)
×
259
end
260

261
local hyphens = require("languages.fr.hyphens-tex")
×
262
SILE.hyphenator.languages["fr"] = hyphens
×
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