local export = {}
local rsplit = mw.text.split
local error_on_no_descendants = false
local function qualifier(content)
if content then
return '<span class="ib-brac qualifier-brac">(</span><span class="ib-content qualifier-content">' .. content .. '</span><span class="ib-brac qualifier-brac">)</span>'
end
end
local function track(page)
return require("Module:debug/track")("descendant/" .. page)
end
local function ine(arg)
if arg == "" then
return nil
else
return arg
end
end
local function add_tooltip(text, tooltip)
return '<span class="desc-arr" title="' .. tooltip .. '">' .. text .. '</span>'
end
local function desc_or_desc_tree(frame, desc_tree)
local params
if desc_tree then
params = {
[1] = {required = true, default = "gem-pro"},
[2] = {required = true, list = true, allow_holes = true, default = "*fuhsaz"},
["notext"] = {type = "boolean"},
["noalts"] = {type = "boolean"},
["noparent"] = {type = "boolean"},
}
else
params = {
[1] = {required = true},
[2] = {list = true, allow_holes = true},
["alts"] = {type = "boolean"}
}
end
for k, v in pairs({
["alt"] = {list = true, allow_holes = true},
["g"] = {list = true, allow_holes = true},
["gloss"] = {alias_of = "t", list = true, allow_holes = true},
["id"] = {list = true, allow_holes = true},
["lit"] = {list = true, allow_holes = true},
["pos"] = {list = true, allow_holes = true},
["t"] = {list = true, allow_holes = true},
["tr"] = {list = true, allow_holes = true},
["ts"] = {list = true, allow_holes = true},
["sc"] = {list = true, allow_holes = true},
["inh"] = {type = "boolean"},
["partinh"] = {type = "boolean", list = "inh", allow_holes = true, require_index = true},
["bor"] = {type = "boolean"},
["partbor"] = {type = "boolean", list = "bor", allow_holes = true, require_index = true},
["lbor"] = {type = "boolean"},
["partlbor"] = {type = "boolean", list = "lbor", allow_holes = true, require_index = true},
["slb"] = {type = "boolean"},
["partslb"] = {type = "boolean", list = "slb", allow_holes = true, require_index = true},
["translit"] = {type = "boolean"},
["parttranslit"] = {type = "boolean", list = "translit", allow_holes = true, require_index = true},
["der"] = {type = "boolean"},
["partder"] = {type = "boolean", list = "der", allow_holes = true, require_index = true},
["clq"] = {type = "boolean"},
["partclq"] = {type = "boolean", list = "clq", allow_holes = true, require_index = true},
["cal"] = {alias_of = "clq", type = "boolean"},
["partcal"] = {alias_of = "partclq", type = "boolean", list = "cal", allow_holes = true, require_index = true},
["calq"] = {alias_of = "clq", type = "boolean"},
["partcalq"] = {alias_of = "partclq", type = "boolean", list = "calq", allow_holes = true, require_index = true},
["calque"] = {alias_of = "clq", type = "boolean"},
["partcalque"] = {alias_of = "partclq", type = "boolean", list = "calque", allow_holes = true, require_index = true},
["pclq"] = {type = "boolean"},
["partpclq"] = {type = "boolean", list = "pclq", allow_holes = true, require_index = true},
["sml"] = {type = "boolean"},
["partsml"] = {type = "boolean", list = "sml", allow_holes = true, require_index = true},
["unc"] = {type = "boolean"},
["partunc"] = {type = "boolean", list = "unc", allow_holes = true, require_index = true},
["sclb"] = {type = "boolean"},
["nolb"] = {type = "boolean"},
["q"] = {},
["qq"] = {},
["partqq"] = {list = "qq", allow_holes = true, require_index = true},
["sandbox"] = {type = "boolean"},
}) do
params[k] = v
end
local namespace = mw.title.getCurrentTitle().nsText
local parent_args
if frame.args[1] then
parent_args = frame.args
else
parent_args = frame:getParent().args
end
-- Error to catch most uses of old-style parameters.
if ine(parent_args[4]) and not ine(parent_args[3]) and not ine(parent_args.tr2) and not ine(parent_args.ts2)
and not ine(parent_args.t2) and not ine(parent_args.gloss2) and not ine(parent_args.g2)
and not ine(parent_args.alt2) then
error("You specified a term in 4= and not one in 3=. You probably meant to use t= to specify a gloss instead. "
.. "If you intended to specify two terms, put the second term in 3=.")
end
if not ine(parent_args[3]) and not ine(parent_args.alt2) and not ine(parent_args.tr2) and not ine(parent_args.ts2)
and ine(parent_args.g2) then
error("You specified a gender in g2= but no term in 3=. You were probably trying to specify two genders for "
.. "a single term. To do that, put both genders in g=, comma-separated.")
end
if parent_args.q then
track("q")
error("Please use qq= not q=. q= will be switching to put the qualifier *before* the term, not after.")
end
local args = require("Module:parameters").process(parent_args, params)
if args.sandbox then
if namespace == "" or namespace == "Reconstruction" then
error("The sandbox module, Module:descendants tree/sandbox, should not be used in entries.")
end
end
local m_desctree
if desc_tree or args["alts"] then
if args.sandbox or require("Module:yesno")(frame.args.sandbox, false) then
m_desctree = require("Module:descendants tree/sandbox")
else
m_desctree = require("Module:descendants tree")
end
end
local lang = args[1]
local terms = args[2]
if mw.title.getCurrentTitle().nsText == "Template" then
lang = lang or "en"
if #terms == 0 then
terms = {"word"}
terms.maxindex = 1
end
end
local m_languages = require("Module:languages")
lang = m_languages.getByCode(lang, 1, "allow etym")
local entryLang = m_languages.getNonEtymological(lang)
if not desc_tree and entryLang:getType() == "family" then
error("Cannot use language family code in [[Template:desc]].")
end
if lang:getCode() ~= entryLang:getCode() then
-- [[Special:WhatLinksHere/Template:tracking/descendant/etymological]]
track("etymological")
track("etymological/" .. lang:getCode())
end
local languageName = lang:getCanonicalName()
local label
if args["sclb"] then
local sc = args["sc"][1] and require("Module:scripts").getByCode(args["sc"][1], "sc")
local term = terms[1]
if sc then
label = sc:getCanonicalName()
else
label = require("Module:scripts").findBestScript(term, lang):getCanonicalName()
end
else
label = languageName
end
-- Find the maximum index among any of the list parameters.
local maxmaxindex = terms.maxindex
for k, v in pairs(args) do
if type(v) == "table" and v.maxindex and v.maxindex > maxmaxindex then
maxmaxindex = v.maxindex
end
end
local function get_arrow(index)
local function val(arg)
if index == 0 then
return args[arg]
else
return args["part" .. arg][index]
end
end
local arrow
if val("bor") then
arrow = add_tooltip("→", "借詞")
elseif val("lbor") then
arrow = add_tooltip("→", "古典借詞")
elseif val("slb") then
arrow = add_tooltip("→", "半接觸借詞")
elseif args["translit"] then
arrow = add_tooltip("→", "音譯")
elseif val("clq") then
arrow = add_tooltip("→", "仿譯")
elseif val("pclq") then
arrow = add_tooltip("→", "部分仿譯")
elseif val("sml") then
arrow = add_tooltip("→", "語義借用")
elseif val("inh") or (val("unc") and not val("der")) then
arrow = add_tooltip(">", "繼承詞")
else
arrow = ""
end
-- allow der=1 in conjunction with bor=1 to indicate e.g. English "pars recta"
-- derived and borrowed from Latin "pars".
if val("der") then
arrow = arrow .. add_tooltip("⇒", "詞形受類比影響或添加了額外詞素")
end
if val("unc") then
arrow = arrow .. add_tooltip("?", "不詳")
end
if arrow ~= "" then
arrow = arrow .. " "
end
return arrow
end
local function get_post_qualifiers(index)
local function val(arg)
if index == 0 then
return args[arg]
else
return args["part" .. arg][index]
end
end
local postqs = {}
if val("inh") then
table.insert(postqs, qualifier("繼承"))
end
if val("lbor") then
table.insert(postqs, qualifier("古典借詞"))
end
if val("slb") then
table.insert(postqs, qualifier("半接觸借詞"))
end
if val("translit") then
table.insert(postqs, qualifier("音譯"))
end
if val("clq") then
table.insert(postqs, qualifier("仿譯"))
end
if val("pclq") then
table.insert(postqs, qualifier("部分仿譯"))
end
if val("sml") then
table.insert(postqs, qualifier("語義借用"))
end
-- FIXME, should we use the qualifier support in full_link() (in which case the qualifier precedes the term)?
if index == 0 and val("q") then -- FIXME: Switch to pre-term qualifier
table.insert(postqs, require("Module:qualifier").format_qualifier(val("q")))
end
if val("qq") then
table.insert(postqs, require("Module:qualifier").format_qualifier(val("qq")))
end
if #postqs > 0 then
return " " .. table.concat(postqs, " ")
else
return ""
end
end
local parts = {}
local descendants = {}
local saw_descendants = false
local seen_terms = {}
for i = 1, maxmaxindex do
local term = terms[i]
local alt = args["alt"][i]
local id = args["id"][i]
local sc = args["sc"][i] and require("Module:scripts").getByCode(args["sc"][i], "sc" .. (i == 1 and "" or i))
local tr = args["tr"][i]
local ts = args["ts"][i]
local gloss = args["t"][i]
local pos = args["pos"][i]
local lit = args["lit"][i]
local g = args["g"][i] and rsplit(args["g"][i], "%s*,%s*") or {}
local link = ""
if term ~= "-" then -- including term == nil
link = require("Module:links").full_link(
{
lang = entryLang,
sc = sc,
term = term,
alt = alt,
id = id,
tr = tr,
ts = ts,
genders = g,
gloss = gloss,
pos = pos,
lit = lit,
},
nil,
true)
elseif ts or gloss or #g > 0 then
-- [[Special:WhatLinksHere/Template:tracking/descendant/no term]]
track("no term")
link = require("Module:links").full_link(
{
lang = entryLang,
sc = sc,
ts = ts,
gloss = gloss,
genders = g,
},
nil,
true)
link = link
:gsub("<small>%[Term%?%]</small> ", "")
:gsub("<small>%[Term%?%]</small> ", "")
:gsub("%[%[Category:[^%[%]]+ term requests%]%]", "")
else -- display no link at all
-- [[Special:WhatLinksHere/Template:tracking/descendant/no term or annotations]]
track("no term or annotations")
end
local arrow = get_arrow(i)
local postqs = get_post_qualifiers(i)
local alts
if desc_tree and term and term ~= "-" then
table.insert(seen_terms, term)
-- This is what I ([[User:Benwing2]]) had in Nov 2020 when I first implemented this.
-- Since then, [[User:Fytcha]] added `true` as the fourth param.
-- descendants[i] = m_desctree.getDescendants(entryLang, term, id, maxmaxindex > 1)
descendants[i] = m_desctree.getDescendants(entryLang, term, id, true)
if descendants[i] then
saw_descendants = true
end
end
descendants[i] = descendants[i] or ""
if term and (desc_tree and not args["noalts"] or not desc_tree and args["alts"]) then
-- [[Special:WhatLinksHere/Template:tracking/descendant/alts]]
track("alts")
alts = m_desctree.getAlternativeForms(entryLang, term, id)
else
alts = ""
end
local linktext = table.concat{link, alts, postqs}
if not args["notext"] then
linktext = arrow .. linktext
end
if linktext ~= "" then
table.insert(parts, linktext)
end
end
if error_on_no_descendants and desc_tree and not saw_descendants then
if #seen_terms == 0 then
error("[[Template:desctree]] invoked but no terms to retrieve descendants from")
elseif #seen_terms == 1 then
error("No Descendants section was found in the entry [[" .. seen_terms[1] ..
"]] under the header for " .. entryLang:getCanonicalName() .. ".")
else
for i, term in ipairs(seen_terms) do
seen_terms[i] = "[[" .. term .. "]]"
end
error("No Descendants section was found in any of the entries " ..
table.concat(seen_terms, ", ") .. " under the header for " .. entryLang:getCanonicalName() .. ".")
end
end
descendants = table.concat(descendants)
if args["noparent"] then
return descendants
end
local initial_arrow = get_arrow(0)
local final_postqs = get_post_qualifiers(0)
local all_linktext = table.concat(parts, "、") .. final_postqs .. descendants
if args["notext"] then
return all_linktext
elseif args["nolb"] then
return initial_arrow .. all_linktext
else
return table.concat{initial_arrow, label, ":", all_linktext ~= "" and " " or "", all_linktext}
end
end
function export.descendant(frame)
return desc_or_desc_tree(frame, false) .. require("Module:TemplateStyles")("Module:etymology/style.css")
end
function export.descendants_tree(frame)
return desc_or_desc_tree(frame, true)
end
return export