Jump to content

Module:Annotated link: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
add filter for single word descriptions to exclude
case insensitive matching equality check for single word descs
Line 108: Line 108:
if notEmpty( not_wikidata_descriptions_including ) then
if notEmpty( not_wikidata_descriptions_including ) then
-- Case insentive matching of terms to filter
-- Case insentive matching of terms to filter
local upper_case_short_description = short_description:lower()
local lower_case_short_description = short_description:lower()
for exclusion in hashDelimitedList( not_wikidata_descriptions_including:lower() ) do
for exclusion in hashDelimitedList( not_wikidata_descriptions_including:lower() ) do
if upper_case_short_description:match( exclusion ) then short_description = '' break end
if lower_case_short_description:match( exclusion ) then short_description = '' break end
end
end
end
end
Line 202: Line 202:
local not_single_word = args.not_single_word
local not_single_word = args.not_single_word
if notEmpty( not_single_word ) then
if notEmpty( not_single_word ) then
local lower_case_short_description = short_description:lower()
for single_word in hashDelimitedList( not_single_word ) do
if single_word == short_description then short_description = '' break end
for single_word in hashDelimitedList( not_single_word:lower() ) do
if single_word == lower_case_short_description then short_description = '' break end
end
end
end
end

Revision as of 03:22, 8 February 2023

local getShortDescription = require( 'Module:GetShortDescription' ).main
local getArgs = require( 'Module:Arguments' ).getArgs
local mLang = require( 'Module:Lang' )

local function pipedLink( name, display ) return '[[:' .. name ..  '|' .. display .. ']]' end

local function isEmpty( value ) return value == nil or value == '' end

local function notEmpty( value ) return not isEmpty( value ) end

-- Unescape functionality grabbed from https://stackoverflow.com/a/14899740/1832568
local function unescape( str )
	str = string.gsub( str, '&#(%d+);', string.char )
	str = string.gsub( str, '&#x(%d+);', function( d ) return string.char( tonumber( d, 16 ) ) end )
	return str
end

local function hashDelimitedList( list_string ) return mw.text.gsplit( unescape( list_string ), '%s*#%s*' ) end

local function alarmingMessage( message )
	return '<span style="color:#d33">[[Module:Annotated link]] ' .. message .. '.</span>'
		.. '[[Category:Pages displaying alarming messages about Module:Annotated link]]'
end

local function optionallyVisibleCategory( class, category )
	return '<span style="display:none" class="' .. class .. '">' .. category .. '</span>[[Category:' .. category .. ']]'
end

local function handleFirstLetterCase( short_description, case )
	return mw.ustring.gsub( short_description, '^([^%d])', function( first_char )
		if case == 'upper' then return mw.ustring.upper( first_char ) end
		return mw.ustring.lower( first_char ) end
	)
end

local function langify( args )
	local lang = args.lang
	local text = args.text
	if isEmpty( lang ) or lang == 'en' then return text end
	return mLang._lang {
		lang,
		text,
		italic = args.italic,
		nocat = args.nocat,
		size = args.size,
		cat = args.cat,
		rtl = args.rtl
	}
end

local function annotatedLink( args )
	local name = args.name
	if isEmpty( name ) then return alarmingMessage( 'requires a page name (including namespace)' ) end
	
	-- In order to handle an attempt to annotate a template link,
	-- already formatted with the likes of {{tl|<template name>}};
	-- unescape name to make sense of braces in lua patern matching.
	name = unescape( name )
	
	if name:match( '^{%b{}}$' ) then
		-- The possibility to extract useful data exists here: e.g. {{tl*|Template}}.
		return alarmingMessage( 'requires only a page name (including namespace) without markup. ' ..
			'If an attempt is being made to annotate a link to a template, provide only the template name with namespace e.g. "Template:Example"'
		)
	end
	
	-- If a literal link was provided as name; extract the content and apply it to name and display as appropriate.
	local wikilink = mw.ustring.match( name, '^%[%[%s*:*%s*(.-)%s*%]%]$' )
	if wikilink then
		local link_name, link_display = unpack( mw.text.split( wikilink, '%s*|%s*' ) )
		if link_name then name = link_name end
		if link_display and not args.display then args.display = link_display end
	end
	
	-- Exclude wikidata fallback for any specified list of link titles, unless explicity instructed that it's okay.
	local not_wikidata_for_links_starting_with  = args.not_wikidata_for_links_starting_with 
	if isEmpty( args.wikidata ) and notEmpty( not_wikidata_for_links_starting_with  ) then
		for only_explicit in hashDelimitedList( not_wikidata_for_links_starting_with  ) do
			if name:match( '^' .. only_explicit ) then args.only = 'explicit' break end
		end
	end
	
	-- Get the short description from Module:GetShortDescription.
	local short_description = getShortDescription( {
		objectify_alarm = true,
		report_redlinks = true,
		lang_italic = args.desc_lang_italic,
		lang_nocat = args.desc_lang_nocat,
		lang_size = args.desc_lang_size,
		lang_cat = args.desc_lang_cat,
		lang_rtl = args.desc_lang_rtl,
		lang_no = args.desc_lang_no,
		fallback = args.fallback,
		prefer = args.prefer,
		only = args.only,
		name = name
	} )
	
	local redlink
	local wikidata
	if type( short_description ) == 'table' then
		if short_description.alarm then return short_description.alarm end
		redlink = short_description.redlink
		wikidata = short_description
		if wikidata and isEmpty( redlink ) then
			short_description = short_description.wikidata
			local not_wikidata_descriptions_including = args.not_wikidata_descriptions_including
			if notEmpty( not_wikidata_descriptions_including ) then
				-- Case insentive matching of terms to filter
				local lower_case_short_description = short_description:lower()
				for exclusion in hashDelimitedList( not_wikidata_descriptions_including:lower() ) do
					if lower_case_short_description:match( exclusion ) then short_description = '' break end
				end
			end
		end
	end
	
	local result
	
	local is_template = name:match( '^Template:(.+)$' )
	local template_link = args.template_link
	if is_template and template_link ~= 'no' then
		result = '{{' .. pipedLink( name, is_template ) .. '}}'
		if template_link == 'code' then
			result = '<code>' .. result .. '</code>'
		end
	else
		local display = args.display
		if isEmpty( display ) then display = name end
		
		result = langify( {
			lang = args.link_lang,
			text = pipedLink( name, display ),
			italic = args.link_lang_italic,
			nocat = args.link_lang_nocat,
			size = args.link_lang_size,
			cat = args.link_lang_cat,
			rtl = args.link_lang_rtl
		} )
		
		if notEmpty( args.quote ) then result = '"' .. result .. '"' end
		
		local abbr = args.abbr
		if notEmpty( abbr ) then
			result = result .. ' (<abbr'
			local abbr_title = args.abbr_title
			if notEmpty( abbr_title ) then result = result .. ' title="' .. abbr_title .. '"' end
			result = result .. '>' .. abbr .. '</abbr>)'
		end
	end
	
	if isEmpty( result ) then return alarmingMessage( 'could not create a link for "' .. name .. '"' ) end
	
	local aka = args.aka
	if notEmpty( aka ) then
		result = result .. ', also known as ' .. langify( {
			lang = args.aka_lang,
			text = aka,
			italic = args.aka_lang_italic,
			nocat = args.aka_lang_nocat,
			size = args.aka_lang_size,
			cat = args.aka_lang_cat,
			rtl = args.aka_lang_rtl
		} )
	end
	
	local wedge = args.wedge
	if notEmpty( wedge ) then
		result = result .. ', ' .. langify( {
			lang = args.wedge_lang,
			text = wedge,
			italic = args.wedge_lang_italic,
			nocat = args.wedge_lang_nocat,
			size = args.wedge_lang_size,
			cat = args.wedge_lang_cat,
			rtl = args.wedge_lang_rtl
		} )
	end
	
	-- If Module:GetShortDescription indicates that name is for a nonexistent page; redlink will be true and there will be no short_description.
	if redlink then
		-- red_cat_no may be provided as a '#' delimited list of titles to not categorise;
		-- the titles may be incomplete prefixes e.g. 'List of'.
		local red_cat_no = args.red_cat_no
		local current_title = mw.title.getCurrentTitle().prefixedText
		-- If instructed to not categorise pages matching the current title:
		if current_title and notEmpty( red_cat_no ) then
			for no_cat in hashDelimitedList( red_cat_no ) do
				if current_title:match( '^' .. no_cat ) then args.red_cat = 'no' break end
			end
		end
		-- If the invocation has not stated a value for red_cat; return the annotation we have thus far with an appended maintenance category.
		if isEmpty( args.red_cat ) then
			return result .. optionallyVisibleCategory( 'category-annotated-redlinks', 'Pages displaying redlinks processed by Module:Annotated link' )
		end
		return result
	end
	
	local maintenance = ''
	
	if not short_description:match( ' ' ) then
		local not_single_word = args.not_single_word
		if notEmpty( not_single_word ) then
			local lower_case_short_description = short_description:lower()
			for single_word in hashDelimitedList( not_single_word:lower() ) do
				if single_word == lower_case_short_description then short_description = '' break end
			end
		end
		if notEmpty( short_description ) and isEmpty( args.space_cat ) then
			maintenance = maintenance .. optionallyVisibleCategory( 'category-spaceless-annotation', 'Pages displaying short descriptions with no spaces via Module:Annotated link' )
		end
	end

	if isEmpty( short_description ) then return result end
	
-- Short descriptions on en Wikipedia should be formatted with an uppercase first letter, but
-- the typical application of this module will require the first character to be lowercase, but
-- some descriptions may start with proper names and should start with an uppercase letter even if used in an annotaion.
-- By default; this module will not affect the first letter case of descriptions retrieved by Module:GetShortDescription, but
-- the first letter case may be transformed explicitly if required.
	local desc_first_letter_case = args.desc_first_letter_case
	if desc_first_letter_case == 'upper' or desc_first_letter_case == 'lower' then
		short_description = handleFirstLetterCase( short_description, desc_first_letter_case )
	end
	
	if wikidata then
		maintenance = maintenance .. optionallyVisibleCategory( 'category-wikidata-fallback-annotation', 'Pages displaying wikidata descriptions as a fallback via Module:Annotated link' )
		if wikidata.none then
			maintenance = maintenance .. optionallyVisibleCategory( 'category-wikidata-fallback-to-none-annotation', 'Pages displaying wikidata descriptions as a fallback to none via Module:Annotated link' )
		end
	end
	
	if name:lower() == short_description:lower() then
		maintenance = maintenance .. optionallyVisibleCategory( 'category-annotation-matches-name', 'Pages displaying short descriptions matching their page name via Module:Annotated link' )
	end
	
	local dash = args.dash
	if isEmpty( dash ) then dash = '&nbsp;–' end
	
	return result .. dash .. ' ' .. short_description .. maintenance
end

local p = {}

function p.main( frame )
	local args = getArgs( frame )
	if isEmpty( args ) then return alarmingMessage( 'could not getArgs' ) end -- This really would be alarming
	return annotatedLink( args )
end

return p