Module:Introspection utils: Difference between revisions

Ganaram inukshuk (talk | contribs)
add page-existence function; add todo
Ganaram inukshuk (talk | contribs)
No edit summary
 
(18 intermediate revisions by the same user not shown)
Line 3: Line 3:


-- TODO:
-- TODO:
-- - Detect whether a template invokes a specific template
-- Add support for detecting modules providing data, not just functions.
-- Address edge case of lazy-loaded modules (loaded within a function)


-- Common functions for module and template introspection.
-- Common functions for module and template introspection.
Line 14: Line 15:


-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- ----------------------- LUA MODULE INTROSPECTION ----------------------------
-- --------------- CODE EXTRACTION AND PREPROCESSING FUNCTIONS -----------------
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------


Line 56: Line 57:
end
end
end
end
table.insert(lines, processed)
end
return table.concat(lines, "\n")
end
-- Helper function: preprocess wikitext so that:
-- - Only text inside includeonly tags is captured.
-- - That captured text has normalized spacing and line breaks
function p.preprocess_wikitext(raw_wikitext)
if not raw_wikitext or raw_wikitext == "" then
return ""
end
-- Gather all includeonly sections, if any exist
local includeonly_blocks = {}
for block in raw_wikitext:gmatch("<includeonly>(.-)</includeonly>") do
table.insert(includeonly_blocks, block)
end
local text_to_scan
if #includeonly_blocks > 0 then
text_to_scan = table.concat(includeonly_blocks, " ")
else
text_to_scan = raw_wikitext
end
-- Normalize tags
text_to_scan = text_to_scan
:gsub("<noinclude>.-</noinclude>", "")
:gsub("<noinclude%s*/>", "")
:gsub("<includeonly>", "")
:gsub("</includeonly>", "")
:gsub("<onlyinclude>", "")
:gsub("</onlyinclude>", "")
-- Normalize subst/safesubst
text_to_scan = text_to_scan
    :gsub("{{+%s*[Ss]afesubst:", "{{")
    :gsub("{{+%s*[Ss]ubst:", "{{")
-- Normalize spaces
text_to_scan = text_to_scan
:gsub("[\r\n]", " ")
:gsub("%s+", " ")
:match("^%s*(.-)%s*$")
return text_to_scan
end
function p.get_and_preprocess_content(namespace, pagename)
local title = mw.title.new(string.format("%s:%s", namespace, pagename))
local content = title:getContent()
table.insert(lines, processed)
if string.lower(namespace) == "module" then
return p.preprocess_code(content)
elseif string.lower(namespace) == "template" then
return p.preprocess_wikitext(content)
else
return ""
end
end
end
--[[
function p.get_and_preprocess_content(fullpagename)
if not fullpagename then return "" end
local title = mw.title.new(fullpagename)
local content = title and title:getContent() or ""


return table.concat(lines, "\n")
local lowercase_name = string.lower(fullpagename)
if string.match(lowercase_name, "^module:") then
return p.preprocess_code(content)
elseif string.match(lowercase_name, "^template:") then
return p.preprocess_wikitext(content)
else
return ""
end
end
end
]]--
-- -----------------------------------------------------------------------------
-- ----------------------- LUA MODULE INTROSPECTION ----------------------------
-- -----------------------------------------------------------------------------


-- Helper function
-- Helper function
Line 200: Line 278:
-- ------------------------ TEMPLATE INTROSPECTION -----------------------------
-- ------------------------ TEMPLATE INTROSPECTION -----------------------------
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- Helper function: preprocess wikitext so that:
-- - Only text inside includeonly tags is captured.
-- - That captured text has normalized spacing and line breaks
function p.preprocess_wikitext(raw_wikitext)
if not raw_wikitext or raw_wikitext == "" then
return ""
end
-- Gather all includeonly sections, if any exist
local includeonly_blocks = {}
for block in raw_wikitext:gmatch("<includeonly>(.-)</includeonly>") do
table.insert(includeonly_blocks, block)
end
local text_to_scan
if #includeonly_blocks > 0 then
text_to_scan = table.concat(includeonly_blocks, " ")
else
text_to_scan = raw_wikitext
end
-- Normalize:
-- 1. Convert all newlines to spaces
-- 2. Collapse multiple spaces/tabs/newlines
-- 3. Trim leading/trailing whitespace
local normalized = text_to_scan
:gsub("[\r\n]", " ")
:gsub("%s+", " ")
:match("^%s*(.-)%s*$")
-- Normalize more by removing subst, safesubst, and any extra curly brackets
normalized = normalized
:gsub("{{{|safesubst:}}}", "")  -- Remove {{{{|safesubst:}}} if used
:gsub("{{{|subst:}}}", "")      -- Remove {{{{|subst:}}} if used
:gsub("{{{{", "{{")            -- Collapse excessive curly brackets
:gsub("}}}}", "}}")            -- Collapse excessive curly brackets
return normalized
end


-- Find all invokes in a wikitext string
-- Find all invokes in a wikitext string
Line 255: Line 293:
return results
return results
end
-- Produce a link to an invoked module
function p.make_module_link(raw_module_name)
if not raw_module_name or raw_module_name == "" then
return nil
end
-- Trim and replace spaces with underscores
local raw_module_name = mw.text.trim(raw_module_name):gsub(" ", "_")
-- Add namespace if missing
if not raw_module_name:find(":", 1, true) then
raw_module_name = "Module:" .. raw_module_name
end
-- Create a normalized title object
local title = mw.title.new(raw_module_name)
if not title then
return nil -- Invalid title (contains illegal characters)
end
-- Format a wiki link: [[Module:Name|Name]]
return string.format("[[%s|%s]]", title.fullText, title.text)
end
end


-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------
-- --------------------- EXISTENCE CHECKING FUNCTIONS --------------------------
-- ------------------ FUNCTION AND PAGE CHECKING FUNCTIONS ---------------------
-- -----------------------------------------------------------------------------
-- -----------------------------------------------------------------------------


Line 266: Line 328:
return title and title.exists
return title and title.exists
end
local function normalize_module_name(name)
if not name then return "" end
-- Trim, replace spaces with underscores, remove namespace, lowercase first
-- letter
name = mw.text.trim(name)
name = name:gsub(" ", "_")
name = name:gsub("^[Mm]odule:", "")
name = name:gsub("^%a", string.lower)
return name
end
-- Check whether a template invokes a function from a module. Input is a table
-- of invocations, from find_invokes, and mod_name is the module name.
function p.invocation_exists(invokes, mod_name)
if type(invokes) ~= "table" then
return false
end
local target_mod = normalize_module_name(mod_name)
for _, invoke in ipairs(invokes) do
if normalize_module_name(invoke["module"]) == target_mod then
return true
end
end
return false
end
function p.tester()
local wikitext = p.get_and_preprocess_content("Template", "MOS intro")
local invokes = p.find_invokes(wikitext)
return p.invocation_exists(invokes, "MOS intro")
end
end


return p
return p