More actions
Skycrafter (talk | contribs) No edit summary |
Skycrafter (talk | contribs) No edit summary |
||
(2 intermediate revisions by the same user not shown) | |||
Line 4: | Line 4: | ||
"Description", | "Description", | ||
"Use Cases & Purpose", | "Use Cases & Purpose", | ||
"Conditions and limits", | |||
"How to Perform", | "How to Perform", | ||
"Origin & History", | |||
"Variations & Related Tricks", | "Variations & Related Tricks", | ||
"Examples in Practice" | |||
} | } | ||
-- Core sections that MUST exist (case-insensitive; '&' titles accept left/right/full) | |||
local coreRequired = { | |||
"Description", | |||
"Conditions and limits", | |||
"How to Perform", | |||
} | |||
-- Too-few-sections threshold. | |||
local MIN_SECTIONS = 3 | |||
-- Utils | |||
local function norm(s) | |||
if not s then return "" end | |||
s = mw.text.trim(s) | |||
s = mw.ustring.gsub(s, "&", "&") | |||
s = mw.ustring.lower(s) | |||
-- unify "and" with "&" for matching | |||
s = mw.ustring.gsub(s, "%s+and%s+", " & ") | |||
-- normalize spaces around ampersand | |||
s = mw.ustring.gsub(s, "%s*&%s*", " & ") | |||
-- collapse spaces/underscores | |||
s = mw.ustring.gsub(s, "[%s_]+", " ") | |||
return mw.text.trim(s) | |||
end | |||
local function ampVariants(title) | |||
-- returns full + left/right variants if '&' present after normalization | |||
local out = {} | |||
local t = norm(title) | |||
out[t] = true | |||
local ampPos = mw.ustring.find(t, "&", 1, true) | |||
if ampPos then | |||
local left = mw.text.trim(mw.ustring.sub(t, 1, ampPos - 1)) | |||
local right = mw.text.trim(mw.ustring.sub(t, ampPos + 1)) | |||
if left ~= "" then out[left] = true end | |||
if right ~= "" then out[right] = true end | |||
end | |||
return out | |||
end | |||
local function getPageWikitext() | local function getPageWikitext() | ||
Line 24: | Line 65: | ||
end | end | ||
return out | return out | ||
end | |||
local function buildPresentSet(headings) | |||
-- set of normalized headings; for any '&' title, also record left/right parts | |||
local present = {} | |||
for _, h in ipairs(headings) do | |||
local n = norm(h) | |||
present[n] = true | |||
for v, _ in pairs(ampVariants(n)) do | |||
present[v] = true | |||
end | |||
end | |||
return present | |||
end | |||
local function hasSection(present, name) | |||
-- accepts full or left/right if '&' (also treats "and" as '&') | |||
for v, _ in pairs(ampVariants(name)) do | |||
if present[v] then return true end | |||
end | |||
return false | |||
end | |||
local function countMatches(present, list) | |||
local c = 0 | |||
for _, name in ipairs(list) do | |||
if hasSection(present, name) then c = c + 1 end | |||
end | |||
return c | |||
end | end | ||
Line 33: | Line 103: | ||
local found = l2Headings(pageContent) | local found = l2Headings(pageContent) | ||
local present = buildPresentSet(found) | |||
local | -- Rule 1: Check for missing CORE sections first, as this is the most critical error. | ||
local missing = {} | |||
for _, req in ipairs(coreRequired) do | |||
if not hasSection(present, req) then | |||
table.insert(missing, req) | |||
end | end | ||
end | |||
if #missing > 0 then | |||
local msg = "This article is missing required section(s): '''" .. table.concat(missing, "''', '''") .. "'''." | |||
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]' | |||
end | end | ||
if | -- Rule 2: Now check if there are too few sections overall. | ||
local msg = "This article | -- This check is now secondary to the core section requirement. | ||
if #found < MIN_SECTIONS then | |||
local msg = "This article has too few sections. Add more sections to match the typical structure." | |||
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]' | return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]' | ||
end | end | ||
-- All checks passed. | |||
return "" | return "" | ||
end | end | ||
return p | return p |
Latest revision as of 22:03, 17 September 2025
Documentation for this module may be created at Module:TrickValidator/doc
local p = {}
local requiredSections = {
"Description",
"Use Cases & Purpose",
"Conditions and limits",
"How to Perform",
"Origin & History",
"Variations & Related Tricks",
"Examples in Practice"
}
-- Core sections that MUST exist (case-insensitive; '&' titles accept left/right/full)
local coreRequired = {
"Description",
"Conditions and limits",
"How to Perform",
}
-- Too-few-sections threshold.
local MIN_SECTIONS = 3
-- Utils
local function norm(s)
if not s then return "" end
s = mw.text.trim(s)
s = mw.ustring.gsub(s, "&", "&")
s = mw.ustring.lower(s)
-- unify "and" with "&" for matching
s = mw.ustring.gsub(s, "%s+and%s+", " & ")
-- normalize spaces around ampersand
s = mw.ustring.gsub(s, "%s*&%s*", " & ")
-- collapse spaces/underscores
s = mw.ustring.gsub(s, "[%s_]+", " ")
return mw.text.trim(s)
end
local function ampVariants(title)
-- returns full + left/right variants if '&' present after normalization
local out = {}
local t = norm(title)
out[t] = true
local ampPos = mw.ustring.find(t, "&", 1, true)
if ampPos then
local left = mw.text.trim(mw.ustring.sub(t, 1, ampPos - 1))
local right = mw.text.trim(mw.ustring.sub(t, ampPos + 1))
if left ~= "" then out[left] = true end
if right ~= "" then out[right] = true end
end
return out
end
local function getPageWikitext()
local t = mw.title.getCurrentTitle()
return t and t:getContent() or nil
end
local function l2Headings(wikitext)
local out = {}
if not wikitext then return out end
wikitext = "\n" .. wikitext .. "\n"
for h in mw.ustring.gmatch(wikitext, "\n==%s*([^=\n][^\n]-)%s*==%s*\n") do
table.insert(out, mw.text.trim(h))
end
return out
end
local function buildPresentSet(headings)
-- set of normalized headings; for any '&' title, also record left/right parts
local present = {}
for _, h in ipairs(headings) do
local n = norm(h)
present[n] = true
for v, _ in pairs(ampVariants(n)) do
present[v] = true
end
end
return present
end
local function hasSection(present, name)
-- accepts full or left/right if '&' (also treats "and" as '&')
for v, _ in pairs(ampVariants(name)) do
if present[v] then return true end
end
return false
end
local function countMatches(present, list)
local c = 0
for _, name in ipairs(list) do
if hasSection(present, name) then c = c + 1 end
end
return c
end
function p.validate(frame)
local pageContent = getPageWikitext()
if not pageContent then
return "" -- cannot validate without content
end
local found = l2Headings(pageContent)
local present = buildPresentSet(found)
-- Rule 1: Check for missing CORE sections first, as this is the most critical error.
local missing = {}
for _, req in ipairs(coreRequired) do
if not hasSection(present, req) then
table.insert(missing, req)
end
end
if #missing > 0 then
local msg = "This article is missing required section(s): '''" .. table.concat(missing, "''', '''") .. "'''."
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
end
-- Rule 2: Now check if there are too few sections overall.
-- This check is now secondary to the core section requirement.
if #found < MIN_SECTIONS then
local msg = "This article has too few sections. Add more sections to match the typical structure."
return '<div class="mw-message-box mw-message-box-error">' .. msg .. '</div>[[Category:Trick articles with formatting errors]]'
end
-- All checks passed.
return ""
end
return p