PhantomCaleb (talk | contribs) No edit summary |
PhantomCaleb (talk | contribs) No edit summary |
||
Line 532: | Line 532: | ||
end |
end |
||
end |
end |
||
+ | for _, param in ipairs(data.params) do |
||
⚫ | |||
+ | if type(param.required) == "string" then |
||
+ | param.required = true |
||
+ | end |
||
+ | end |
||
for i = 1, repeatedGroupCount do |
for i = 1, repeatedGroupCount do |
||
for _, paramName in ipairs(repeatedGroupParams) do |
for _, paramName in ipairs(repeatedGroupParams) do |
||
Line 555: | Line 559: | ||
table.insert(data.paramOrder, "_") |
table.insert(data.paramOrder, "_") |
||
end |
end |
||
⚫ | |||
local templateData = mw.getCurrentFrame():extensionTag("templatedata", mw.text.jsonEncode(data)) |
local templateData = mw.getCurrentFrame():extensionTag("templatedata", mw.text.jsonEncode(data)) |
||
local html = mw.html.create("div") |
local html = mw.html.create("div") |
Revision as of 12:44, 1 August 2020
local p = {}
local h = {}
local ListPages = require("Module:List Pages")
local i18n = require("Module:I18n")
local s = i18n.getString
local utilsArg = require("Module:UtilsArg")
local utilsLayout = require("Module:UtilsLayout")
local utilsMarkup = require("Module:UtilsMarkup")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")
local FORMATS = {
inline = {
templateData = "{{_|_= _}}",
argSeparator = "|",
afterLastArg = "",
},
block = {
templateData = "{{_\n|_= _\n}}",
argSeparator = "\n%s|",
afterLastArg = "\n",
}
}
local VARARG_KEY = "..."
function p.Template(frame)
local title = mw.title.getCurrentTitle()
local templateName = frame.args.template or title.text
if title.subpageText == "Documentation" then
templateName = title.baseText
end
local moduleName = frame.args.module or title.baseText
return p.printTemplateDoc(templateName, moduleName)
end
function p.Examples(frame)
local args = frame:getParent().args
local examples = h.examplesFromFrame(args)
return p.printExamples(examples)
end
function p.printTemplateDoc(templateName, moduleName)
moduleName = moduleName or templateName
local templateSpec = require("Module:" .. moduleName).Templates[templateName]
utilsArg.schemaValidate(p.Schemas.TemplateSpec, "TemplateSpec", templateSpec, templateName)
local result = ""
if templateSpec.wip then
result = result .. mw.getCurrentFrame():expandTemplate({title = "UC"})
end
local result = result .. "__TOC__\n"
if templateSpec.purpose then
result = result .. utilsMarkup.heading(2, s("heading.purpose"))
result = result .. mw.getCurrentFrame():preprocess(templateSpec.purpose) .. "\n"
end
result = result .. utilsMarkup.heading(2, s("heading.usage"))
if templateSpec.usesData then
local dataSource
if type(templateSpec.usesData) == "table" then
dataSource = ListPages.main(templateSpec.usesData)
else
dataSource = utilsMarkup.link(templateSpec.usesData)
end
result = result .. utilsMarkup.italic(s("usesData", {dataSource = dataSource})) .. "\n"
end
if templateSpec.usesModuleData then
local dataSource = utilsMarkup.link("Module:" .. moduleName .. "/Data")
result = result .. utilsMarkup.italic(s("usesModuleData", {dataSource = dataSource})) .. "\n"
end
if templateSpec.storesData then
local dataSource = utilsMarkup.link(templateSpec.storesData)
result = result .. utilsMarkup.italic(s("storesData", {dataSource = dataSource})) .. "\n"
end
result = result .. p.printUsage(templateName, templateSpec)
if templateSpec.examples then
local examples = h.examplesFromSpec(templateName, templateSpec)
result = result .. utilsMarkup.heading(3, s("heading.examples"))
result = result .. p.printExamples(examples)
end
result = result .. h.templateData(templateSpec)
result = result .. h.categories(templateSpec)
return result
end
function p.printUsage(templateName, templateSpec)
local params = {}
local paramOrder = templateSpec.paramOrder
local format = templateSpec.format
local positionalParamCount = 0
local hasVarArg = false
local repeatedParams = templateSpec.repeatedGroup and templateSpec.repeatedGroup.params or {}
for k, v in pairs(templateSpec.params) do
if type(k) == "number" then
positionalParamCount = positionalParamCount + 1
elseif k == VARARG_KEY then
hasVarArg = true
end
if utilsTable.keyOf(repeatedParams, k) then
-- do nothing, repeated params processed separately
else
local i = paramOrder and utilsTable.keyOf(paramOrder, k) or (#params + 1)
params[i] = utilsTable.merge({}, v, {
param = k
})
end
end
local repeatedGroup = {}
for _, param in ipairs(repeatedParams) do
repeatedGroup[#repeatedGroup + 1] = utilsTable.merge({}, templateSpec.params[param], {
param = param
})
end
local format = FORMATS[templateSpec.format]
local result = ""
local boilerplateOptions = utilsTable.merge({}, templateSpec.boilerplate or {}, {
repeatedGroupCount = templateSpec.repeatedGroup and templateSpec.repeatedGroup.count
})
if positionalParamCount > 0 or hasVarArg then
result = result .. utilsLayout.tabs({
{
label = s("tabs.syntax"),
content = h.syntax(templateName, params, repeatedGroup, format, templateSpec.indent)
},
{
label = s("tabs.boilerplate"),
content = h.boilerplate(templateName, params, repeatedGroup, format, templateSpec.indent, boilerplateOptions)
},
})
else
result = h.boilerplate(templateName, params, repeatedGroup, format, templateSpec.indent, boilerplateOptions)
end
params = utilsTable.concat(params, repeatedGroup)
result = result .. h.params(params, positionalParamCount)
return result
end
function p.printExamples(examples)
for i, example in ipairs(examples) do
local input = mw.text.trim(example.input)
if string.find(input, "\n") or examples.vertical then
input = utilsMarkup.pre(input, {
wrapLines = false
})
else
input = utilsMarkup.code(mw.text.nowiki(input))
end
if example.output then
local output, categories = utilsMarkup.stripCategories(example.output)
local output = utilsMarkup.killBacklinks(output)
local categoryList = utilsMarkup.bulletList(categories)
examples[i] = {
input = input,
output = output,
categoryList = categoryList,
desc = example.desc
}
else
examples[i] = {
input = input,
desc = example.desc
}
end
end
if not examples.vertical then
local rows = {}
for _, example in ipairs(examples) do
if example.desc then
table.insert(rows, {
{
header = true,
colspan = -1,
styles = {
["text-align"] = "left"
},
content = example.desc,
}
})
end
table.insert(rows, {example.input, example.output, example.categoryList})
end
return utilsLayout.table({
hideEmptyColumns = true,
headers = { s("header.input"), s("header.output"), s("header.categoriesAdded"), header = true },
rows = rows
})
end
local result = ""
for _, example in ipairs(examples) do
local headerStyles = {
["width"] = "5rem" -- for alignment. See Template:Letter/Documentation for an example of why this is needed
}
result = result .. utilsLayout.table({
hideEmptyRows = true,
rows = {
{
{ header = true, content= s("header.input"), styles = headerStyles},
example.input,
},
{
{ header = true, content = s("header.output"), styles = headerStyles},
example.output
},
{
{ header = true, content = s("header.categoriesAdded"), styles = headerStyles },
example.categoryList
},
}
}) .. "\n"
end
return result
end
function h.syntax(templateName, params, repeatedGroup, format, indent)
local args = {}
for i, param in ipairs(params) do
if param.param == VARARG_KEY then
h.insertVarArgsSyntax(args, i, param.placeholder)
elseif type(param.param) == "number" then
args[i] = {
param = param.param,
value = param.placeholder or string.format("<%s>", param.name or param.param),
inline = param.inline,
}
else
args[i] = {
param = param.param,
inline = param.inline,
value = ""
}
end
end
-- Add syntax for repeated group
local afterArg = {}
if #repeatedGroup > 0 then
for i, n in ipairs({"1", "2", "N"}) do
for _, param in ipairs(repeatedGroup) do
table.insert(args, {
param = param.param .. n,
value = ""
})
end
if i ~= 3 then
afterArg[#args] = "\n"
end
end
end
local result = h.printTemplateInstance(templateName, args, format, {
indent = indent,
afterArg = afterArg,
})
return utilsMarkup.pre(result)
end
function h.insertVarArgsSyntax(args, i, placeholder)
for j = 1, 3 do
table.insert(args, i+j-1, {
param = j,
value = placeholder .. j
})
end
table.insert(args, i+3, {
param = i+3,
value = "..."
})
table.insert(args, i+4, {
param = i+4,
value = placeholder .. "N"
})
end
function h.boilerplate(templateName, params, repeatedGroup, format, indent, options)
local options = options or {}
local args = {}
local i = 1
for _, param in ipairs(params) do
if param.canOmit then
-- do nothing
elseif param.param == VARARG_KEY then
h.insertVarArgBoilerplate(args, i)
else
i = i + 1
table.insert(args, {
param = param.param,
value = "",
inline = param.inline,
})
end
end
local afterArg = {}
for i = 1, options.repeatedGroupCount or 0 do
for _, param in ipairs(repeatedGroup) do
table.insert(args, {
param = param.param .. i,
value = "",
})
end
if i ~= options.repeatedGroupCount then
afterArg[#args] = "\n"
end
end
local result = h.printTemplateInstance(templateName, args, format, {
indent = indent,
afterArg = afterArg
})
if options.list then
local listArgs = {
{
param = 1,
value = result,
},
{
param = 2,
value = result,
},
{
param = 3,
value = result,
},
}
result = h.printTemplateInstance("List", listArgs, FORMATS.block, {
indent = 1
})
end
if options.before then
result = options.before .. result
end
if options.after then
result = result .. options.after
end
return utilsMarkup.pre(result)
end
function h.insertVarArgBoilerplate(args, i)
for j = 1, 3 do
table.insert(args, i+j-1, {
param = j,
value = ""
})
end
end
function h.examplesFromFrame(examples)
local result = {
vertical = examples.vertical
}
for i, example in ipairs(examples) do
local input = mw.text.unstripNoWiki(example)
local output = mw.getCurrentFrame():preprocess(mw.text.decode(input))
result[i] = {
input = input,
output = output
}
end
return result
end
function h.examplesFromSpec(templateName, templateSpec)
local paramOrder = templateSpec.paramOrder
local repeatedParams = templateSpec.repeatedGroup and templateSpec.repeatedGroup.params or {}
local repeatedParamsMap = utilsTable.invert(repeatedParams)
local isRepeated = h.isRepeated(repeatedParamsMap)
local examples = templateSpec.examples
local result = {
vertical = examples.vertical
}
for i, example in ipairs(examples) do
local args = {}
local unsortedArgs = {}
local repeatedGroupArgs = {}
for k, v in pairs(example.args or example) do
local arg = {
param = k,
value = v,
inline = templateSpec.params[k] and templateSpec.params[k].inline
}
local isRepeated, index, param = isRepeated(k)
if isRepeated then
repeatedGroupArgs[index] = repeatedGroupArgs[index] or {}
utilsTable.merge(repeatedGroupArgs[index], {
[param] = arg
})
else
local idx = paramOrder and utilsTable.keyOf(paramOrder, k)
if idx then
args[idx] = arg
else
table.insert(unsortedArgs, arg)
end
end
end
args = utilsTable.compact(args) -- needed when template has a mix of named parameters and optional anonymous parameters (e.g. Template:Cite)
args = utilsTable.concat(args, unsortedArgs)
-- repeated groups go last
local afterArg = {}
for i, group in ipairs(repeatedGroupArgs) do
for _, param in ipairs(repeatedParams) do
table.insert(args, group[param])
end
if i ~= #repeatedGroupArgs then
afterArg[#args] = "\n"
end
end
result[i] = {}
result[i].input = h.printTemplateInstance(templateName, args, FORMATS[templateSpec.format], {
indent = templateSpec.indent,
afterArg = afterArg,
})
if not templateSpec.storesData then
-- It's important not to use frame:expandTemplate here.
-- We've had a bug in the past that we didn't catch because expandTemplate can accept args with "=" in them, while actual template calls cannot.
result[i].output = mw.getCurrentFrame():preprocess(result[i].input)
end
result[i].desc = example.desc
end
return result
end
function h.isRepeated(repeatedParamsMap)
-- @param param a template parameter e.g. "tab1"
-- @return boolean indicating whether parameter is part of a repeated group
-- @return number index of the repition, e.g. 1 for "tab1", 2 for "tab2", etc.
-- @return name of the parameter without the index, e.g. "tab" for "tab1", "tab2", etc.
return function(param)
if type(param) == "number" then
return false
end
local name = utilsString.trim(param, "0-9")
local index = tonumber(string.sub(param, #name + 1))
if not repeatedParamsMap[name] or not index then
return false
end
return true, index, name
end
end
function h.printTemplateInstance(name, args, format, options)
local options = options or {}
local afterArg = options.afterArg or {}
local indent = string.rep(" ", options.indent or 0)
local result = "{{" .. name
for i, arg in ipairs(args) do
local printedArg
if type(arg.param) == "number" then
printedArg = arg.value
else
printedArg = string.format("%s= %s", arg.param, arg.value)
end
if arg.inline then
result = result .. FORMATS.inline.argSeparator .. printedArg
else
result = result .. string.format(format.argSeparator, indent) .. printedArg
end
if afterArg[i] then
result = result .. afterArg[i]
end
end
result = result .. format.afterLastArg .. "}}"
return result
end
function h.params(params, positionalParamCount)
local rows = {}
for i, param in ipairs(params) do
rows[i] = h.paramRow(param, positionalParamCount)
end
return utilsLayout.table({
hideEmptyColumns = true,
headers = {s("header.parameter"), s("header.status"), s("header.description"), s("header.enum")},
rows = rows,
})
end
function h.paramRow(param, i)
if param.param == VARARG_KEY then
local varArgs = utilsTable.map({"1", ".", ".", "N"}, utilsMarkup.code)
varArgs = utilsMarkup.list(varArgs)
paramCell = {{varArgs, utilsMarkup.code(param.name)}}
elseif param.name and param.name ~= param.param then
paramCell = {{utilsMarkup.code(param.param), utilsMarkup.code(param.name)}}
else
paramCell = utilsMarkup.code(param.param)
end
local statusCell = param.required and utilsMarkup.bold(s("status.required")) or s("status.optional")
local descriptionCell = param.desc
local enumCell = param.enum and h.printEnum(param) or ""
return {paramCell, statusCell, descriptionCell, enumCell}
end
function h.printEnum(param)
local enum = param.enum
local dependsOn = param.enumDependsOn
local enumReference = type(enum) == "table" and enum.reference or nil
if dependsOn then
return s("enum.depends", { arg = utilsMarkup.code(dependsOn) })
elseif enumReference then
return s("enum.ref", { dataSource = enumReference })
else
local values = utilsTable.map(enum, utilsMarkup.code)
return utilsMarkup.bulletList(values)
end
end
function h.templateData(templateSpec)
local hasParamOrder = templateSpec.paramOrder
local data = {
description = templateSpec.purpose,
params = {},
paramOrder = templateSpec.paramOrder or {},
}
local repeatedGroupParams = templateSpec.repeatedGroup and templateSpec.repeatedGroup.params or {}
local repeatedGroupCount = templateSpec.repeatedGroup and templateSpec.repeatedGroup.count or 0
for k, v in pairs(templateSpec.params) do
if not utilsTable.keyOf(repeatedGroupParams, k) then
data.params[k] = {
label = v.name or k,
required = v.required,
description = v.desc,
aliases = type(k) == "number" and {v.name} or nil,
type = v.type,
}
if not hasParamOrder then
table.insert(data.paramOrder, k)
end
end
end
for _, param in ipairs(data.params) do
if type(param.required) == "string" then
param.required = true
end
end
for i = 1, repeatedGroupCount do
for _, paramName in ipairs(repeatedGroupParams) do
local param = templateSpec.params[paramName]
local paramKey = paramName .. i
data.params[paramKey] = {
label = l,
required = param.required,
description = param.desc,
type = param.type
}
table.insert(data.paramOrder, paramKey)
end
end
-- The following workaround is ugly, but necessary to ensure that data.params is encoded as a JSON object rather than an array.
if utilsTable.isArray(templateSpec.params) then
data.params["_"] = {
label = "_",
description = "Dummy parameter. Do not use.",
}
table.insert(data.paramOrder, "_")
end
local templateData = mw.getCurrentFrame():extensionTag("templatedata", mw.text.jsonEncode(data))
local html = mw.html.create("div")
:addClass("mw-collapsible mw-collapsed")
:attr("data-expandtext", s("templateData.show"))
:attr("data-collapsetext", s("templateData.hide"))
:css("float", "left")
:wikitext(templateData)
return tostring(html)
end
function h.categories(templateSpec)
local categories = {s("categories.all")}
if templateSpec.usesData then
table.insert(categories, s("categories.usesData"))
end
if templateSpec.storesData then
table.insert(categories, s("categories.storesData"))
end
return utilsMarkup.categories(categories)
end
i18n.loadStrings({
en = {
categories = {
all = "Category:Templates",
usesData = "Category:Cargo Query Templates",
storesData = "Category:Cargo Storage Templates",
},
usesData = "This template relies on the centralized data stored at ${dataSource}.",
usesModuleData = "This template relies on configuration data stored at ${dataSource}.",
storesData = "This template is used to store centralized data at ${dataSource}.",
enum = {
ref = "See ${dataSource}",
depends = "Depends on ${arg}",
},
header = {
description = "Description",
parameter = "Parameter",
status = "Status",
enum = "Accepted values",
categoriesAdded = "Categories added",
input = "Input",
output = "Output",
},
heading = {
examples = "Examples",
purpose = "Purpose",
usage = "Usage",
},
status = {
optional = "optional",
required = "required",
},
tabs = {
boilerplate = "Boilerplate",
syntax = "Syntax",
},
templateData = {
show = "show TemplateData ▼",
hide = "hide TemplateData ▲",
},
}
})
p.Schemas = {
TemplateSpec = {
type = "record",
properties = {
{
name = "purpose",
type = "string",
required = true,
desc = "Purpose of the template.",
},
{
name = "wip",
type = "boolean",
desc = "Flag to indicate the template is a work in progress.",
},
{
name = "storesData",
type = "string",
desc = "For <code>/Store</code> templates, use this field to indicate which Data page this template is used on. If present, the template is added to [[:Category:Cargo Storage Templates]].",
},
{
name = "usesData",
oneOf = {
{
type = "string"
},
{
type = "array",
items = { type = "string" }
},
},
desc = "For templates that use Cargo data, use this field to indicate which page(s) the data is stored on. If present, the template is added to [[:Category:Cargo Query Templates]].",
},
{
name = "usesModuleData",
type = "boolean",
desc = "Set to true for templates that rely on [[:Category:Module Data|/Data]] pages in the Module namespace.",
},
{
name = "format",
type = "string",
required = true,
enum = {"inline", "block"},
desc = "Indicates how the template should be laid out in source. <code>inline</code> for single-line templates, <code>block</code> for multiline templates."
},
{
name = "params",
type = "map",
required = true,
desc = "<p>Map of the template parameters. Numerical keys indicate positional parameters. String keys indicate named parameters. A special key named <code>" .. VARARG_KEY .. "</code> indicates a variadic parameter (i.e. a template with a variable number of trailing arguments, such as [[Template:List]]).</p><p>This data can be used by [[Module:UtilsArg]] to parse template arguments.</p>",
keys = {
oneOf = {
{ type = "string" },
{ type = "number" },
},
},
values = { _ref = "#/definitions/param" },
},
{
name = "repeatedGroup",
type = "record",
desc = 'This is used to make templates that accept "arrays of objects" or "rows" of input. For an example, see [[Template:Trading Quest]]. (Ideally, [[Template:Tab2]] could be using this as well.)',
properties = {
{
name = "name",
required = true,
type = "string",
desc = "The key that the resulting array will be assigned to in the returned argument table.",
},
{
name = "params",
required = true,
type = "array",
items = { type = "string" },
desc = "An array of parameter keys (similar to <code>paramOrder</code>), indicating which parameters are repeated."
},
{
name = "count",
required = true,
type = "number",
desc = "<p>Roughly the maximum amount of times one can expect the params to be repeated. This is merely to decide how many repetitions to include in the boilerplate. It is always possible to repeat more times than this number.</p><p>For example, [[Template:Trading Quest]] is set to 20. The longest trading quest in the series is 14 items, so 20 is likely to cover any future trading quests."
}
}
},
{
name = "paramOrder",
type = "array",
items = {
oneOf = {
{ type = "number" },
{ type = "string" },
},
},
desc = "Array of parameter keys indicating what order they should be presented in.",
},
{
name = "indent",
type = "number",
default = 0,
desc = "Number of spaces to indent block-format parameters.",
},
{
name = "boilerplate",
type = "record",
desc = "Directives for how generate boilerplate for the template.",
properties = {
{
name = "before",
type = "string",
desc = "Any wikitext entered here will be prepended to the boilerplate",
},
{
name = "after",
type = "string",
desc = "Any wikitext entered here will be appended to the boilerplate",
},
{
name = "list",
type = "boolean",
desc = "If true, boilerplate includes [[Template:List]]. See [[Template:Game Rating]] for example.",
},
},
},
{
name = "examples",
desc = "Array of argument tables representing different invocations of the template + an optional <code>vertical</code> key. It is possible to add descriptions to specific examples as well. See [[Template:List]] for examples.",
allOf = {
{
type = "record",
properties = {
{
name = "vertical",
type = "boolean",
default = "false",
desc = "If false, examples are laid out in a single table (e.g. [[Template:List]]). If true, examples are listed one after the other (e.g. [[Template:Letter]])."
},
},
},
{
type = "array",
items = {
type = "map",
keys = {
oneOf = {
{ type = "number" },
{ type = "string" },
},
},
values = { type = "any" },
},
},
},
},
},
definitions = {
param = {
type = "record",
properties = {
{
name = "name",
type = "string",
desc = "Use this to assign names to positional parameters.",
},
{
name = "placeholder",
type = "string",
desc = "Placeholder to use for argument value when demonstrating template usage. Defaults to <code><name></code> for positional parameters; empty string for named parameters.",
},
{
name = "type",
type = "string",
enum = {
"unknown",
"number",
"string",
"line",
"boolean",
"date",
"url",
"wiki-page-name",
"wiki-file-name",
"wiki-template-name",
"wiki-user-name",
"content",
"unbalanced-wikitext",
reference = "{{Mediawiki|Extension:TemplateData}}",
},
desc = "One of the {{Mediawiki|Extension:TemplateData}} types."
},
{
name = "required",
oneOf = {
{ type = "boolean" },
{ type = "string" },
},
desc = "Indicates a required parameter.",
},
{
name = "deprecated",
type = "boolean",
desc = "Indicates a parameter that should no longer be used.",
},
{
name = "enum",
type = "any",
desc = "An array of allowed values. Optionally, a <code>reference</code> key can be added to the Lua table which links to a page listing all the allowed values (see [[Module:Franchise#enum]] for example).",
},
{
name = "enumDependsOn",
type = "string",
desc = "If the allowed values for this parameter depends on the value of another parameter, specify that parameter name here. Then, instead of <code>enum</code> being an array, it can be a function that returns an array. The value of the dependency argument is passed to the function. See [[Module:Letter]] for an example.",
},
{
name = "desc",
type = "string",
required = true,
desc = "Wikitext description of the parameter.",
},
{
name = "canOmit",
type = "boolean",
desc = "Omit parameter from generated boilerplate. Use for parameters that cover edge cases and should thefore not be used in normal circumstances. See [[Template:Game Rating]] for example.",
},
{
name = "inline",
type = "boolean",
desc = 'If true, then the parameter will be printed on the same line as the previous parameter, even if <code>format</code> is set to <code>"block"</code>. See [[Template:Sequence/Store]] for example.',
},
{
name = "trim",
type = "boolean",
desc = "Indicator to [[Module:UtilsArg#parse|utilsArg.parse]] that this template argument should be trimmed using [[Module:UtilsString#trim|utilsString.trim]].",
},
{
name = "nilIfEmpty",
type = "boolean",
desc = "Indicator to [[Module:UtilsArg#parse|utilsArg.parse]] that this template argument should be made nil if it is an empty string, using [[Module:UtilsString#nilIfEmpty|utilsString.nilIfEmpty]].",
},
{
name = "split",
oneOf = {
{ type = "string" },
{ type = "boolean" },
},
desc = "Indicator to [[Module:UtilsArg#parse|utilsArg.parse]]. If set to <code>true</code>, the template argument is treated as a list of commma-separated values, to be turned into an array using [[Module:UtilsString#split|utilsString.split]]. If set to a string, that string will be used as the splitting pattern.",
},
},
},
},
},
}
return p