Module:MultiReplace/sandbox
Appearance
| This is the module sandbox page for Module:MultiReplace (diff). |
| This Lua module is used on approximately 2,160,000 pages, or roughly 3% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
| This module depends on the following other modules: |
Replaces matches of multiple patterns in a given string with given replacements. For each replacement instance, the pattern matching at the lowest position is chosen. If there are multiple such patterns, then the one specified earliest in the pattern list is chosen.
Usage
[edit]{{#invoke:MultiReplace|main|input|plain=yes (optional)|pattern1| replacement1|pattern2|replacement2... }}
If plain=yes is specified, then the patterns and replacements are treated as plain text, otherwise as Lua Unicode patterns.
An equals sign in a pattern will trigger an Unpaired argument error. Use {{=}}, which expands to an equals sign that will not be interpreted.
require[[strict]]
--- ---
--- PRIVATE ENVIRONMENT ---
--- ________________________________ ---
--- ---
-- Get the actual arguments
local function get_args (frame, ...)
return type(frame) ~= 'table' and { frame, ... } or
type(frame.args) ~= 'table' and frame or
frame.args[1] and frame.args or
frame:getParent().args
end
-- Copied from Module:String's `str._escapePattern()`
local function escape_pattern (str)
return (string.gsub(str, "[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%0"))
end
--- ---
--- PUBLIC ENVIRONMENT ---
--- ________________________________ ---
--- ---
local iface = {}
-- Syntax: #invoke:multiReplace|singlepass|[plain=yes (optional)]|pattern1|
-- replacement1|pattern2|replacement2|...|
iface.singlepass = function (frame, ...)
local args = get_args(frame, ...)
local argc, seq, txt, plain =
0, {}, args[1] or '{{{1}}}', args.plain == 'yes'
for key, val in ipairs(args) do argc, seq[key] = argc + 1, val end
if argc % 2 == 0 then
error('Module:MultiReplace, ‘singlepass’: Unpaired argument: <code>'
.. argc .. ' = ' .. mw.text.nowiki(seq[argc]) .. '</code>', 0)
end
local start, bestStart, bestStop, bestChange
local len, pos, result, maps, rlen = mw.ustring.len(txt), 1, {}, {}, 0
for idx = 2, argc, 2 do
maps[idx - 1], maps[idx] =
mw.ustring.find(txt, seq[idx], pos, plain)
end
while pos <= len do
bestStart, bestStop, bestChange = len + 1, len, nil
for idx = 2, argc, 2 do
start = maps[idx - 1]
if start and start < pos then
maps[idx - 1], maps[idx] = mw.ustring.find(txt,
seq[idx], pos, plain)
start = maps[idx - 1]
end
if start and start < bestStart then
bestStart, bestStop, bestChange =
start, maps[idx], idx
end
end
rlen = rlen + 1
result[rlen] = mw.ustring.sub(txt, pos, bestStart - 1)
if bestChange then
maps[bestChange - 1], maps[bestChange] =
mw.ustring.find(txt, seq[bestChange],
maps[bestChange] + 1, plain)
rlen = rlen + 1
result[rlen] = (plain and seq[bestChange + 1] or
mw.ustring.gsub(mw.ustring.sub(txt, bestStart,
bestStop), seq[bestChange],
seq[bestChange + 1], 1))
end
pos = bestStop + 1
end
return table.concat(result)
end
-- Syntax: #invoke:multiReplace|cascade|[plain=yes (optional)]|pattern1|
-- replacement1|pattern2|replacement2|...|
iface.cascade = function (frame, ...)
local args, argc = get_args(frame, ...), 0
for _ in ipairs(args) do argc = argc + 1 end
if argc % 2 == 0 then
error('Module:MultiReplace, ‘cascade’: Unpaired argument: <code>'
.. argc .. ' = ' .. mw.text.nowiki(args[argc]) .. '</code>', 0)
end
local txt = args[1] or '{{{1}}}'
if args.plain == 'yes' then
for idx = 2, argc, 2 do
txt = mw.ustring.gsub(txt, escape_pattern(args[idx]),
args[idx + 1])
end
else
for idx = 2, argc, 2 do
txt = mw.ustring.gsub(txt, args[idx], args[idx + 1])
end
end
return txt
end
-- For compatibility reasons; must not be documented; to delete in the future
iface.main = iface.singlepass
return iface