Module:descendants tree: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
local export = {} | local export = {} | ||
local alternative_forms_module = "Module:alternative forms" | |||
local string_utilities_module = "Module:string utilities" | |||
local remove_comments = require(string_utilities_module).remove_comments | |||
local function track(page) | |||
--[[Special:WhatLinksHere/Wiktionary:Tracking/descendants tree/PAGE]] | |||
return require("Module:debug/track")("descendants tree/" .. page) | |||
end | |||
local function preview_error(what, entry_name, language_name, reason) | local function preview_error(what, entry_name, language_name, reason) | ||
mw.log("Could not retrieve " .. what .. " for " .. language_name .. " in the entry [[" | mw.log("Could not retrieve " .. what .. " for " .. language_name .. " in the entry [[" | ||
.. entry_name .. "]]: " .. reason .. ".") | .. entry_name .. "]]: " .. reason .. ".") | ||
track(what .. " error") | |||
end | end | ||
local function get_content_after_senseid(content, entry_name, lang, id) | local function get_content_after_senseid(content, entry_name, lang, id) | ||
local code = lang:getFullCode() | |||
local code = lang: | local t_start, t_end | ||
local t_start | for template in require("Module:template parser").find_templates(content) do | ||
local name = template:get_name() | |||
for | if name == "senseid" then | ||
if name == "senseid" | local args = template:get_arguments() | ||
if args[1] == code and args[2] == id then | |||
t_start = template.index | |||
end | |||
elseif name == "etymid" then | elseif name == "etymid" then | ||
local args = template:get_arguments() | |||
if args[1] == code and args[2] == id then | if args[1] == code and args[2] == id then | ||
t_start = index | t_start = template.index | ||
elseif t_start ~= nil and t_end == nil then | elseif t_start ~= nil and t_end == nil then | ||
t_end = index | t_end = template.index | ||
end | end | ||
elseif name == "head" | elseif name == "head" then | ||
if | local args = template:get_arguments() | ||
t_start = index | local id_arg = args.id | ||
elseif | if id_arg == id then | ||
t_end = index | t_start = template.index | ||
elseif id_arg ~= nil and t_start ~= nil and t_end == nil then | |||
t_end = template.index | |||
end | end | ||
end | end | ||
| Line 46: | Line 63: | ||
end | end | ||
function export. | function export.get_alternative_forms(lang, entry_name, id, default_separator) | ||
local page = mw.title.new(entry_name) | local page = mw.title.new(entry_name) | ||
local content = page:getContent() | local content = page:getContent() | ||
local function alt_form_error(reason) | local function alt_form_error(reason) | ||
preview_error("alternative forms", entry_name, lang: | preview_error("alternative forms", entry_name, lang:getFullName(), reason) | ||
end | end | ||
| Line 58: | Line 74: | ||
-- FIXME, should be an error | -- FIXME, should be an error | ||
alt_form_error("nonexistent page") | alt_form_error("nonexistent page") | ||
track("alts-nonexistent-page") | |||
return "" | return "" | ||
end | end | ||
local _, index = string.find(content, | local _, index = string.find(content, | ||
"==[ \t]*" .. require( | "==[ \t]*" .. require(string_utilities_module).pattern_escape(lang:getFullName()) .. "[ \t]*==") | ||
if not index then | if not index then | ||
-- FIXME, should be an error | -- FIXME, should be an error | ||
alt_form_error("L2 header for language not found") | alt_form_error("L2 header for language not found") | ||
track("alts-lang-not-found") | |||
return "" | return "" | ||
end | end | ||
| Line 77: | Line 95: | ||
local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) | local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) | ||
local _, index = string.find(content, "\n(====?=?)[ \t]*Alternative forms[ \t]*%1", index, false) | local _, index = string.find(content, "\n(====?=?)[ \t]*Alternative forms[ \t]*%1", index, false) | ||
if not index then | |||
_, index = string.find(content, "\n(====?=?)[ \t]*Alternative reconstructions[ \t]*%1", index, false) | |||
end | |||
if not index then | if not index then | ||
-- FIXME, should be an error | -- FIXME, should be an error | ||
alt_form_error("'Alternative forms' section for language not found") | alt_form_error("'Alternative forms' section for language not found") | ||
track("alts-section-not-found") | |||
return "" | return "" | ||
end | end | ||
local langCodeRegex = require( | local langCodeRegex = require(string_utilities_module).pattern_escape(lang:getFullCode()) | ||
index = string.find(content, "{{alte?r?|" .. langCodeRegex .. "|[^|}]+", index) | index = string.find(content, "{{alte?r?|" .. langCodeRegex .. "|[^|}]+", index) | ||
if (not index) or (next_lang and next_lang < index) then | if (not index) or (next_lang and next_lang < index) then | ||
-- FIXME, should be an error | -- FIXME, should be an error | ||
alt_form_error("no 'alt' or 'alter' template in 'Alternative forms' section for language") | alt_form_error("no 'alt' or 'alter' template in 'Alternative forms' section for language") | ||
track("alts-alter-not-found") | |||
return "" | return "" | ||
end | end | ||
| Line 100: | Line 123: | ||
local altforms = require("Module:alternative forms") | local altforms = require("Module:alternative forms") | ||
for | for template in require("Module:template parser").find_templates(alternative_forms_section) do | ||
if ( | if template:get_name() == "alter" then | ||
local args = template:get_arguments() | |||
if args[1] == lang:getFullCode() then | |||
saw_alter = true | |||
local formatted_altforms = altforms.display_alternative_forms(args, entry_name, "allow self link", default_separator) | |||
table.insert(terms_list, formatted_altforms) | |||
end | |||
end | end | ||
end | end | ||
| Line 111: | Line 137: | ||
-- FIXME, should be an error | -- FIXME, should be an error | ||
alt_form_error("no terms in 'alt' or 'alter' template in 'Alternative forms' section for language") | alt_form_error("no terms in 'alt' or 'alter' template in 'Alternative forms' section for language") | ||
track("alts-no-terms-in-alter") | |||
return "" | return "" | ||
end | end | ||
return table.concat(terms_list, default_separator or ", ") | |||
end | end | ||
function export. | function export.get_descendants(lang, entry_name, id, noerror) | ||
local page = mw.title.new(entry_name) | local page = mw.title.new(entry_name) | ||
local content = page:getContent() | local content = page:getContent() | ||
| Line 125: | Line 150: | ||
local function desc_error(reason) | local function desc_error(reason) | ||
preview_error("descendants", entry_name, lang: | preview_error("descendants", entry_name, lang:getFullName(), reason) | ||
end | end | ||
| Line 131: | Line 156: | ||
-- FIXME, should be an error | -- FIXME, should be an error | ||
desc_error("nonexistent page") | desc_error("nonexistent page") | ||
track("desctree-nonexistent-page") | |||
return "" | return "" | ||
end | end | ||
-- Ignore HTML comments, columns and blank lines. | -- Ignore HTML comments, columns and blank lines. | ||
content = content | content = remove_comments(content) | ||
:gsub("{{top%d}}%s", "") | :gsub("{{top%d}}%s", "") | ||
:gsub("{{mid%d}}%s", "") | :gsub("{{mid%d}}%s", "") | ||
| Line 149: | Line 174: | ||
local _, index = string.find(content, | local _, index = string.find(content, | ||
"%f[^\n%z]==[ \t]*" .. lang: | "%f[^\n%z]==[ \t]*" .. lang:getFullName() .. "[ \t]*==", nil, true) | ||
if not index then | if not index then | ||
_, index = string.find(content, "%f[^\n%z]==[ \t]*" | _, index = string.find(content, "%f[^\n%z]==[ \t]*" | ||
.. require( | .. require(string_utilities_module).pattern_escape(lang:getFullName()) | ||
.. "[ \t]*==", nil, false) | .. "[ \t]*==", nil, false) | ||
end | end | ||
if not index then | if not index then | ||
desc_error("L2 header for language not found") | desc_error("L2 header for language not found") | ||
-- FIXME, should be an error | |||
track("desctree-lang-not-found") | |||
return "" | return "" | ||
end | end | ||
| Line 167: | Line 194: | ||
local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) | local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) | ||
local _, index = string.find(content, "\n(====*)[ \t]*Descendants[ \t]*%1", index, false) | local _, index = string.find(content, "\n(====*)[ \t]*Descendants[ \t]*%1", index, false) | ||
local function desctree_no_descendants(with_lang_in_error) | |||
if noerror and (namespace == "" or namespace == "Reconstruction") then | if noerror and (namespace == "" or namespace == "Reconstruction") then | ||
track("desctree-no-descendants") | |||
return "<small class=\"error previewonly\">(" .. | return "<small class=\"error previewonly\">(" .. | ||
"Please either change this template to {{desc}} " .. | "Please either change this template to {{desc}} " .. | ||
"or insert a ====Descendants==== section in [[" .. | "or insert a ====Descendants==== section in [[" .. | ||
entry_name .. "#" .. lang: | entry_name .. "#" .. lang:getFullName() .. "]])</small>" .. | ||
"[[Category:" .. lang: | "[[Category:" .. lang:getFullName() .. " descendants to be fixed in desctree]]" | ||
else | else | ||
error("No Descendants section was found in the entry [[" | error(("No Descendants section was found in the entry [[%s]]%s"):format(entry_name, | ||
with_lang_in_error and (" under the header for %s"):format(lang:getFullName()) or "")) | |||
end | end | ||
end | |||
if not index then | |||
return desctree_no_descendants() | |||
elseif next_lang and next_lang < index then | elseif next_lang and next_lang < index then | ||
return desctree_no_descendants("with lang in error") | |||
end | end | ||
Revision as of 21:10, 11 January 2025
Documentation for this module may be created at Module:descendants tree/doc
local export = {}
local alternative_forms_module = "Module:alternative forms"
local string_utilities_module = "Module:string utilities"
local remove_comments = require(string_utilities_module).remove_comments
local function track(page)
--[[Special:WhatLinksHere/Wiktionary:Tracking/descendants tree/PAGE]]
return require("Module:debug/track")("descendants tree/" .. page)
end
local function preview_error(what, entry_name, language_name, reason)
mw.log("Could not retrieve " .. what .. " for " .. language_name .. " in the entry [["
.. entry_name .. "]]: " .. reason .. ".")
track(what .. " error")
end
local function get_content_after_senseid(content, entry_name, lang, id)
local code = lang:getFullCode()
local t_start, t_end
for template in require("Module:template parser").find_templates(content) do
local name = template:get_name()
if name == "senseid" then
local args = template:get_arguments()
if args[1] == code and args[2] == id then
t_start = template.index
end
elseif name == "etymid" then
local args = template:get_arguments()
if args[1] == code and args[2] == id then
t_start = template.index
elseif t_start ~= nil and t_end == nil then
t_end = template.index
end
elseif name == "head" then
local args = template:get_arguments()
local id_arg = args.id
if id_arg == id then
t_start = template.index
elseif id_arg ~= nil and t_start ~= nil and t_end == nil then
t_end = template.index
end
end
end
if t_start == nil then
error("Could not find the correct senseid template in the entry [["
.. entry_name .. "]] (with language " .. code .. " and id '" .. id .. "')")
end
if t_end == nil then
-- terminate on L2 or another "Etymology ..." header
-- match L2 and remove it and everything after it
content = string.gsub(content:sub(t_start), "\n==[^=].+$", "")
-- match Etymology header and remove it and everything after it
content = string.gsub(content, "\n===+%s*Etymology.+$", "")
return content
end
return content:sub(t_start, t_end)
end
function export.get_alternative_forms(lang, entry_name, id, default_separator)
local page = mw.title.new(entry_name)
local content = page:getContent()
local function alt_form_error(reason)
preview_error("alternative forms", entry_name, lang:getFullName(), reason)
end
if not content then
-- FIXME, should be an error
alt_form_error("nonexistent page")
track("alts-nonexistent-page")
return ""
end
local _, index = string.find(content,
"==[ \t]*" .. require(string_utilities_module).pattern_escape(lang:getFullName()) .. "[ \t]*==")
if not index then
-- FIXME, should be an error
alt_form_error("L2 header for language not found")
track("alts-lang-not-found")
return ""
end
if id then
content = get_content_after_senseid(content, entry_name, lang, id)
index = 0
end
local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false)
local _, index = string.find(content, "\n(====?=?)[ \t]*Alternative forms[ \t]*%1", index, false)
if not index then
_, index = string.find(content, "\n(====?=?)[ \t]*Alternative reconstructions[ \t]*%1", index, false)
end
if not index then
-- FIXME, should be an error
alt_form_error("'Alternative forms' section for language not found")
track("alts-section-not-found")
return ""
end
local langCodeRegex = require(string_utilities_module).pattern_escape(lang:getFullCode())
index = string.find(content, "{{alte?r?|" .. langCodeRegex .. "|[^|}]+", index)
if (not index) or (next_lang and next_lang < index) then
-- FIXME, should be an error
alt_form_error("no 'alt' or 'alter' template in 'Alternative forms' section for language")
track("alts-alter-not-found")
return ""
end
local next_section = string.find(content, "\n(=+)[^=]+%1", index)
local alternative_forms_section = string.sub(content, index, next_section)
local terms_list = {}
local altforms = require("Module:alternative forms")
for template in require("Module:template parser").find_templates(alternative_forms_section) do
if template:get_name() == "alter" then
local args = template:get_arguments()
if args[1] == lang:getFullCode() then
saw_alter = true
local formatted_altforms = altforms.display_alternative_forms(args, entry_name, "allow self link", default_separator)
table.insert(terms_list, formatted_altforms)
end
end
end
if #terms_list == 0 then
-- FIXME, should be an error
alt_form_error("no terms in 'alt' or 'alter' template in 'Alternative forms' section for language")
track("alts-no-terms-in-alter")
return ""
end
return table.concat(terms_list, default_separator or ", ")
end
function export.get_descendants(lang, entry_name, id, noerror)
local page = mw.title.new(entry_name)
local content = page:getContent()
local namespace = mw.title.getCurrentTitle().nsText
local function desc_error(reason)
preview_error("descendants", entry_name, lang:getFullName(), reason)
end
if not content then
-- FIXME, should be an error
desc_error("nonexistent page")
track("desctree-nonexistent-page")
return ""
end
-- Ignore HTML comments, columns and blank lines.
content = remove_comments(content)
:gsub("{{top%d}}%s", "")
:gsub("{{mid%d}}%s", "")
:gsub("{{bottom}}%s", "")
:gsub("\n?{{(desc?%-%l+)|?[^}]*}}",
function (template_name)
if template_name == "desc-top" or template_name == "desc-bottom" or template_name == "des-top" or template_name == "des-mid" or template_name == "des-bottom" then
return ""
end
end)
:gsub("\n%s*\n", "\n")
local _, index = string.find(content,
"%f[^\n%z]==[ \t]*" .. lang:getFullName() .. "[ \t]*==", nil, true)
if not index then
_, index = string.find(content, "%f[^\n%z]==[ \t]*"
.. require(string_utilities_module).pattern_escape(lang:getFullName())
.. "[ \t]*==", nil, false)
end
if not index then
desc_error("L2 header for language not found")
-- FIXME, should be an error
track("desctree-lang-not-found")
return ""
end
if id then
content = get_content_after_senseid(content, entry_name, lang, id)
index = 0
end
local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false)
local _, index = string.find(content, "\n(====*)[ \t]*Descendants[ \t]*%1", index, false)
local function desctree_no_descendants(with_lang_in_error)
if noerror and (namespace == "" or namespace == "Reconstruction") then
track("desctree-no-descendants")
return "<small class=\"error previewonly\">(" ..
"Please either change this template to {{desc}} " ..
"or insert a ====Descendants==== section in [[" ..
entry_name .. "#" .. lang:getFullName() .. "]])</small>" ..
"[[Category:" .. lang:getFullName() .. " descendants to be fixed in desctree]]"
else
error(("No Descendants section was found in the entry [[%s]]%s"):format(entry_name,
with_lang_in_error and (" under the header for %s"):format(lang:getFullName()) or ""))
end
end
if not index then
return desctree_no_descendants()
elseif next_lang and next_lang < index then
return desctree_no_descendants("with lang in error")
end
-- Skip past final equals sign.
index = index + 1
-- Skip past spaces or tabs.
while true do
local new_index = string.match(content, "^[ \t]+()", index)
if not new_index then
break
end
index = new_index
end
local items = require("Module:array")()
local frame = mw.getCurrentFrame()
local previous_list_markers = ""
-- Skip paragraphs at beginning of Descendants section.
while true do
local new_index = content:match("^\n[^%*:=][^\n]*()", index)
if not new_index then
break
else
index = new_index
end
end
previous_index = 1
-- Find a consecutive series of list items that begins directly after the
-- Descendants header.
-- start_index and previous_index are used to check that list items are
-- consecutive.
for start_index, list_markers, item, index in string.gmatch(content:sub(index), "()\n([%*:]+) *([^\n]+)()") do
if start_index ~= previous_index then
break
end
-- Preprocess, but replace recursive calls to avoid template loop errors
item = string.gsub(item, "{{desctree|", "{{#invoke:etymology/templates/descendant|descendants_tree|")
item = frame:preprocess(item)
local difference = #list_markers - #previous_list_markers
if difference > 0 then
for i = #previous_list_markers + 1, #list_markers do
items:insert(list_markers:sub(i, i) == "*" and "<ul>" or "<dl>")
end
else
if difference < 0 then
for i = #previous_list_markers, #list_markers + 1, -1 do
items:insert(previous_list_markers:sub(i, i) == "*" and "</li></ul>" or "</dd></dl>")
end
else
items:insert(previous_list_markers:sub(-1, -1) == "*" and "</li>" or "</dd>")
end
if previous_list_markers:sub(#list_markers, #list_markers) ~= list_markers:sub(-1, -1) then
items:insert(list_markers:sub(-1, -1) == "*" and "</dl><ul>" or "</ul><dl>")
end
end
items:insert(list_markers:sub(-1, -1) == "*" and "<li>" or "<dd>")
items:insert(item)
previous_list_markers = list_markers
previous_index = index
end
for i = #previous_list_markers, 1, -1 do
items:insert(previous_list_markers:sub(i, i) == "*" and "</li></ul>" or "</dd></dl>")
end
return items:concat()
end
return export