Module:Category handler: Difference between revisions
Jump to navigation
Jump to search
m (1 revision imported) |
No edit summary |
||
Line 1: | Line 1: | ||
-- | --[=[ <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 | |||
local ucf = require('Module:Paramtest').ucfirst | |||
local | local curpage = mw.title.getCurrentTitle() | ||
function p.main(frame) | |||
local ns = curpage.namespace | |||
-- Just don't bother unless we're in content namespaces | |||
if | if not (ns == 0 or ns == 120) then | ||
return | return '' | ||
end | 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 | 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 | ||
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 | |||
end | |||
return p._main(cats) | |||
return | |||
end | end | ||
function | 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 | 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 | ||
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 | |||
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)) | |||
end | -- 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 | else | ||
table.insert(ctg,string.format('[[Category:%s]]',cn)) | |||
end | end | ||
end | end | ||
end | |||
return | |||
return table.concat(ctg) | |||
end | end | ||
return p | return p |
Revision as of 19:33, 7 November 2021
Template loop detected: Template:No documentation
Module:Category handler requires Module:Paramtest.
Module:Category handler is required by Module:Userbox.
--[=[ <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) 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