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

sile-typesetter / sile / 14284237390

05 Apr 2025 05:33PM UTC coverage: 63.158% (+31.8%) from 31.375%
14284237390

push

github

web-flow
Merge pull request #2248 from alerque/class-warfare

Normalize module layout across all module types

257 of 350 new or added lines in 14 files covered. (73.43%)

71 existing lines in 11 files now uncovered.

13670 of 21644 relevant lines covered (63.16%)

3070.68 hits per line

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

0.0
/packages/url/init.lua
1
local base = require("packages.base")
×
2

3
local package = pl.class(base)
×
4
package._name = "url"
×
5

6
-- URL escape sequence, URL fragment:
7
local preferBreakBefore = "%#"
×
8
-- URL path elements, URL query arguments, acceptable extras:
9
local preferBreakAfter = ":/.;?&=!_-"
×
10
-- URL scheme:
11
local alwaysBreakAfter = ":" -- Must have only one character here!
×
12

13
local escapeRegExpMinimal = function (str)
14
   -- Minimalist = just what's needed for the above strings
15
   return string.gsub(str, "([%.%?%-%%])", "%%%1")
×
16
end
17

18
local breakPattern = "[" .. escapeRegExpMinimal(preferBreakBefore .. preferBreakAfter .. alwaysBreakAfter) .. "]"
×
19

20
function package:_init ()
×
21
   base._init(self)
×
22
   self:loadPackage("verbatim")
×
23
   self:loadPackage("inputfilter")
×
24
   self:loadPackage("pdf")
×
25
end
26

27
function package.declareSettings (_)
×
28
   SILE.settings:declare({
×
29
      parameter = "url.linebreak.primaryPenalty",
30
      type = "integer",
31
      default = 100,
32
      help = "Penalty for breaking lines in URLs at preferred breakpoints",
33
   })
UNCOV
34
   SILE.settings:declare({
×
35
      parameter = "url.linebreak.secondaryPenalty",
36
      type = "integer",
37
      default = 200,
38
      help = "Penalty for breaking lines in URLs at tolerable breakpoints (should be higher than url.linebreak.primaryPenalty)",
39
   })
40
end
41

42
function package:registerCommands ()
×
43
   self:registerCommand("href", function (options, content)
×
44
      if options.src then
×
45
         SILE.call("pdf:link", {
×
46
            dest = options.src,
47
            external = true,
48
            borderwidth = options.borderwidth,
49
            borderstyle = options.borderstyle,
50
            bordercolor = options.bordercolor,
51
            borderoffset = options.borderoffset,
52
         }, content)
×
53
      else
54
         options.src = content[1]
×
55
         SILE.call("pdf:link", {
×
56
            dest = options.src,
57
            external = true,
58
            borderwidth = options.borderwidth,
59
            borderstyle = options.borderstyle,
60
            bordercolor = options.bordercolor,
61
            borderoffset = options.borderoffset,
62
         }, function (_, _)
63
            SILE.call("url", { language = options.language }, content)
×
64
         end)
65
      end
66
   end, "Inserts a PDF hyperlink.")
×
67

68
   local urlFilter = function (node, content, options)
69
      if type(node) == "table" then
×
70
         return node
×
71
      end
72
      local result = {}
×
73
      for token in SU.gtoke(node, breakPattern) do
×
74
         if token.string then
×
75
            result[#result + 1] = token.string
×
76
         else
77
            if string.find(preferBreakBefore, escapeRegExpMinimal(token.separator)) then
×
78
               -- Accepts breaking before, and at the extreme worst after.
79
               result[#result + 1] = self.class.packages.inputfilter:createCommand(
×
80
                  content.pos,
×
81
                  content.col,
×
82
                  content.lno,
×
83
                  "penalty",
84
                  { penalty = options.primaryPenalty }
×
85
               )
86
               result[#result + 1] = token.separator
×
87
               result[#result + 1] = self.class.packages.inputfilter:createCommand(
×
88
                  content.pos,
×
89
                  content.col,
×
90
                  content.lno,
×
91
                  "penalty",
92
                  { penalty = options.worsePenalty }
×
93
               )
94
            elseif token.separator == alwaysBreakAfter then
×
95
               -- Accept breaking after (only).
96
               result[#result + 1] = token.separator
×
97
               result[#result + 1] = self.class.packages.inputfilter:createCommand(
×
98
                  content.pos,
×
99
                  content.col,
×
100
                  content.lno,
×
101
                  "penalty",
102
                  { penalty = options.primaryPenalty }
×
103
               )
104
            else
105
               -- Accept breaking after, but tolerate breaking before.
106
               result[#result + 1] = self.class.packages.inputfilter:createCommand(
×
107
                  content.pos,
×
108
                  content.col,
×
109
                  content.lno,
×
110
                  "penalty",
111
                  { penalty = options.secondaryPenalty }
×
112
               )
113
               result[#result + 1] = token.separator
×
114
               result[#result + 1] = self.class.packages.inputfilter:createCommand(
×
115
                  content.pos,
×
116
                  content.col,
×
117
                  content.lno,
×
118
                  "penalty",
119
                  { penalty = options.primaryPenalty }
×
120
               )
121
            end
122
         end
123
      end
124
      return result
×
125
   end
126

127
   self:registerCommand("url", function (options, content)
×
128
      SILE.settings:temporarily(function ()
×
129
         local primaryPenalty = SILE.settings:get("url.linebreak.primaryPenalty")
×
130
         local secondaryPenalty = SILE.settings:get("url.linebreak.secondaryPenalty")
×
131
         local worsePenalty = primaryPenalty + secondaryPenalty
×
132

133
         if options.language then
×
134
            SILE.languageSupport.loadLanguage(options.language)
×
135
            if options.language == "fr" then
×
136
               -- Trick the engine by declaring a "fake"" language that doesn't apply
137
               -- the typographic rules for punctuations
138
               SILE.hyphenator.languages["_fr_noSpacingRules"] = SILE.hyphenator.languages.fr
×
139
               -- Not needed (the engine already defaults to SILE.nodeMakers.unicode if
140
               -- the language is not found):
141
               -- SILE.nodeMakers._fr_noSpacingRules = SILE.nodeMakers.unicode
142
               SILE.settings:set("document.language", "_fr_noSpacingRules")
×
143
            else
144
               SILE.settings:set("document.language", options.language)
×
145
            end
146
         else
147
            SILE.settings:set("document.language", "und")
×
148
         end
149

150
         local result = self.class.packages.inputfilter:transformContent(content, urlFilter, {
×
151
            primaryPenalty = primaryPenalty,
152
            secondaryPenalty = secondaryPenalty,
153
            worsePenalty = worsePenalty,
154
         })
155
         SILE.call("urlstyle", {}, result)
×
156
      end)
157
   end, "Inserts penalties in an URL so it can be broken over multiple lines at appropriate places.")
×
158

159
   self:registerCommand("urlstyle", function (options, content)
×
160
      SILE.call("code", options, content)
×
161
   end, "Hook that may be redefined to change the styling of URLs")
×
162
end
163

164
package.documentation = [[
165
\begin{document}
166
\use[module=packages.url]
167
This package enhances the typesetting of URLs in two ways.
168
First, it provides the \autodoc:command{\href[src=<url>]{<content>}} command which inserts PDF hyperlinks, \href[src=http://www.sile-typesetter.org/]{like this}.
169

170
The \autodoc:command{\href} command accepts the same \autodoc:parameter{borderwidth}, \autodoc:parameter{bordercolor}, \autodoc:parameter{borderstyle}, and \autodoc:parameter{borderoffset} styling options as the \autodoc:command[check=false]{\pdf:link} command from the \autodoc:package{pdf} package, for instance \href[src=http://www.sile-typesetter.org/, borderwidth=0.4pt, bordercolor=blue, borderstyle=underline]{like this}.
171

172
Nowadays, it is a common practice to have URLs in print articles (whether it is a good practice or not is yet \em{another} topic).
173
Therefore, the package also provides the \autodoc:command{\url} command, which will automatically insert breakpoints into unwieldy URLs like \url{https://github.com/sile-typesetter/sile-typesetter.github.io/tree/master/examples} so that they can be broken up over multiple lines.
174

175
It allows line breaks after the colon, and before or after appropriate segments of an URL (path elements, query parts, fragments, etc.).
176
By default, the \autodoc:command{\url} command ignores the current language, as one would not want hyphenation to occur in URL segments.
177
If you have no other choice, however, you can pass it a \autodoc:parameter{language} option to enforce a language to be applied.
178
Note that if French (\code{fr}) is selected, the special typographic rules applying to punctuations in this language are disabled.
179

180
To typeset a URL and also make it an active hyperlink, use the \autodoc:command{\href} command without the \autodoc:parameter{src} option,
181
but with the URL passed as argument.
182

183
The breaks are controlled by two penalty settings: \autodoc:setting{url.linebreak.primaryPenalty} for preferred breakpoints and, for less acceptable but still tolerable breakpoints, \autodoc:setting{url.linebreak.secondaryPenalty}—its value should logically be higher than the previous one.
184

185
The \autodoc:command{\urlstyle} command hook may be overridden to change the style of URLs.
186
By default, they are typeset as “code”.
187

188
\end{document}
189
]]
×
190

191
return package
×
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