Module:Category handler
Documentation for this module may be created at Module:Category handler/doc
--[=[ <pre>
-- Implements [Template:Ctg]
-- Sorts pages into a category more appropriately than pagename alone
-- Default and custom rules are outlined at [[Template:Category handler/doc]]
--]=]
local p = {}
local ucf = require('Module:Paramtest').ucfirst
local curpage = mw.title.getCurrentTitle()
function p.main(frame)
local ns = curpage.namespace
-- Just don't bother unless we're in content namespaces
if not (ns == 0 or ns == 120 or ns == 116) then
return ''
end
local args = frame:getParent().args
local cats = {}
for _, v in ipairs(args) do
local cat_x = {}
-- Replace underscores with spaces
-- Condense and trim white-space; remove new lines (just in case)
v = mw.text.trim(v)
:gsub('[_%s]+',' ')
:gsub('\n','')
-- Snip category name now, up to the index of the first set of two colons
-- If no colons, just use the whole string
local cat_n = (v:match('^([^:]+)::') or v)
:gsub('[Cc]ategory:%s*','')
-- Set category name to the name just snipped
cat_x.name = cat_n
-- Page title includes matched text
-- Matched text is defined by ::ifmatches[text]
-- or if empty, defaults to category name
if v:find('::ifmatches') then
-- Look for brackets used as delimiters and capture them all
local match_set = v:match('::ifmatches(%[[^:]+%])')
-- If none are found, use the pagename
if not match_set then
cat_x.ifmatch = {cat_n}
else
cat_x.ifmatch = {}
-- Split match into table, delimited by brackets
match_set = mw.text.split(match_set,'[%]%[]+')
-- Add to match table; only if not blank
-- An empty string is created by "[" at the beginning
for _, w in ipairs(match_set) do
if w:find('%S') then
table.insert(cat_x.ifmatch,w)
end
end
end
-- Iterate through and escape all metacharacters
-- Prevents errors when they're passed to string.match() below
-- Make everything lowercase
for i, w in ipairs(cat_x.ifmatch) do
cat_x.ifmatch[i] = w:gsub(
-- Chars: - ^ $ * ( ) + ?
'([%-^$*()+?])',
'%%%1'):lower()
end
end
-- Text to strip from the front of the sort
-- Can be user defined with ::remove[text]
-- Defaults to category name exactly
-- Escape metacharacters to prevent errors when they're passed to string.match() below
cat_x.trim = string.gsub(
v:match('::remove%[%s*([^]:]+)%s*%]') or
cat_n,
-- Chars: - ^ $ * ( ) + ?
'([%-^$*()+?])',
'%%%1')
-- Add category and its rules into the list
table.insert(cats,cat_x)
end
return p._main(cats)
end
function p._main(cat_list)
-- Pagename, exactly, in all lowercase, and escaped (used for matching)
local pagename = curpage.text
local pagelc = pagename:lower()
local pageesc = pagelc:gsub(
-- Chars: - ^ $ * ( ) + ?
'([%-^$*()+?])',
'%%%1')
-- Return table
local ctg = {}
for _, v in ipairs(cat_list) do
-- Category name and in lowercase
local cn = v.name
local cnl = cn:lower()
-- Text to remove
local rmv = v.trim:lower()
-- Little thing that checks pagename against everything in the matches table
-- If there's no table, keep as false (it won't matter)
local pagematches = false
if v.ifmatch then
for _, w in ipairs(v.ifmatch) do
-- Look for exact match, and with faux-singular too
if pagelc:find(w) or
(w:find('s$') and
pagelc:find(w:match('(.*)s$')))
then
pagematches = true
end
end
end
-- Create a second string that counts as the singular of the text to remove
-- If it works as a singular, and the page name is singular, then use it too
-- Otherwise, just make it the same as rmv
local rmvpl = rmv
if rmv:find('s$') then
rmvpl = rmv:match('(.*)s$')
if pagelc:find('^'..rmvpl) and
(not pagelc:find('^'..rmv))
then
-- Nothing
else
rmvpl = rmv
end
end
-- If v.ifmatch is not specified or
-- It is and the pattern matches any part of the pagename
-- Continue to add categories
if (not v.ifmatch) or
(v.ifmatch and pagematches)
then
-- If the pagename matches category name exactly
-- Or either is a simple plural of the other
-- Or the text to remove exactly
-- Sort to front
if pagelc:find('^'..cnl..'$') or
cnl:find('^'..pageesc..'s$') or
pagelc:find('^'..cnl..'s$') or
pagelc:find('^'..rmv..'$')
then
table.insert(ctg,string.format('[[Category:%s| ]]',cn))
-- If the pagename begins with the category name
-- Sort with beginning remove
elseif pagelc:find('^'..rmv) or
pagelc:find('^'..rmvpl)
then
-- Offset by an extra character if it's not plural
-- Or the page starts with plural
if rmvpl == rmv then
offset = 1
else
offset = 0
end
-- Unescape metacharacters for proper length
local key = pagename:sub( #(rmv:gsub('%%','')) + offset )
key = ucf(mw.text.trim(key))
-- Remove punctuation from start if leftover
-- Such as "/" leftover on subpages
-- Or "(" for disambiguated pages
if key:find('^%p') then
key = ucf(key:sub(2))
-- Just in case, remove "s" preceding punctuation
elseif key:find('^S%p') then
key = ucf(key:sub(3))
end
table.insert(ctg,string.format('[[Category:%s|%s]]',cn,key))
-- Everything else just gets the category added plainly
else
table.insert(ctg,string.format('[[Category:%s]]',cn))
end
end
end
return table.concat(ctg)
end
return p