Модуль:TemplateDataDoc

Материал из Тептар
Перейти к навигации Перейти к поиску

Модуль для работы с TemplateData и автоматической генерации заготовок для копирования и примеров использования шаблонов на основе её.

Для корректной сортировки параметров в TemplateData должен содержаться массив paramOrder. При редактировании через визуальный интерфейс он добавляется в момент перетаскивания параметров вверх-вниз в списке.

Методы

[править код]

Внешние

[править код]
  • generateBlank( frame ) — вывод заготовки для вставки шаблона в статью (см. {{заготовка шаблона}})
  • generateExample( frame ) — вывод примера использования шаблона (см. {{пример шаблона}})

Внутренние

[править код]
  • getTemplateData( pageName ) — парсинг TemplateData с указанной страницы

require( 'strict' );
local docSubPage = mw.message.new( 'Templatedata-doc-subpage' ):plain();

local p = {};

local lastNumber = 0;

-- Enable/disable additional spacing for block-formatted templates
local formatBlockSpaces = true;

-- Params that should not be shown in code
local deprecatedParams = {
	'Commons',
	'commons',
	'nocat',
	'from',
	'nocoord',
	'nocatcoord',
	'Автооформление заголовка',
	'автооформление заголовка',
	'Тептарсклад',
	'тептарсклад',
	'Категория в Commons',
	'категория в Commons',
	'Категория на Тептарскладе',
	'категория на Тептарскладе',
	'на тептарскладе',
	'страница на Тептарскладе',
	'Ширина',
	'ширина',
	'Ширина изображения',
	'ширина изображения',
	'Ширина логотипа',
	'ширина логотипа',
	'Ширина автографа',
	'ширина автографа',
};

local coordinateRows = {
	{ 'lat_dir', 'lat_deg', 'lat_min', 'lat_sec' },
	{ 'lon_dir', 'lon_deg', 'lon_min', 'lon_sec' },
};

local noDocNote = 'TemplateDataDoc: Запишите страницу для отображения заполненного шаблона.';

function p.parseParamList( value )
	local params = {};
	for param in mw.text.gsplit( value or '', ',', true ) do
		param = mw.text.trim( param );
		if param ~= '' then
			params[ param ] = true;
		end
	end
	return params;
end

function p.processJson( json, shownDeprecatedParams )
	local status, data = pcall( mw.text.jsonDecode, json );
	if status == false then
		return nil;
	end
	shownDeprecatedParams = shownDeprecatedParams or {};

	if not data[ 'paramOrder' ] then
		data[ 'paramOrder' ] = {};
		for paramName, paramData in pairs( data[ 'params' ] ) do
			table.insert( data[ 'paramOrder' ], paramName );
		end
	end
	
	for _, param in ipairs( deprecatedParams ) do
		if data[ 'params' ][ param ] ~= nil and not shownDeprecatedParams[ param ] then
			data[ 'params' ][ param ][ 'deprecated' ] = '-';
		end
	end

	return data;
end

function p.getTemplateData( pageName, shownDeprecatedParams )
	local title = mw.title.makeTitle( 0, pageName );
	if not title or not title.exists then
		return false
	end

	local content = title:getContent()
	if not content then
		return false;
	end;

	local json = mw.ustring.match( content, '<[Tt]emplate[Dd]ata%s*>(.*)</[Tt]emplate[Dd]ata%s*>' );
	if not json then
		return nil;
	end
	
	return p.processJson( json, shownDeprecatedParams or {} )
end

function p.getValue( data, key )
	if data[ key ] then
		return data[ key ];
	end
	
	-- Numbered keys return as numbers
	local nkey = tonumber( key );
	if nkey ~= nil and data[ nkey ] then
		return data[ nkey ];
	end
	
	return {};
end

-- See https://phabricator.wikimedia.org/diffusion/ETDA/browse/master/Specification.md?as=remarkup
-- We need a global format value for the 'block' and 'inline': [[phab:T205438]]
function p.convertFormatString( rawTemplateFormat )
	local formatType = rawTemplateFormat or 'inline';
	local templateFormat = formatType;
	local isBlockFormatted = false;
	if formatType == 'block' then
		templateFormat = '{{_\n| _ = _\n}}';
		isBlockFormatted = true;
	elseif formatType == 'inline' then
		templateFormat = '{{_|_=_}}';
	end
	
	return templateFormat, isBlockFormatted, formatType;
end

function p.getFormatParts( rawTemplateFormat, templateName )
	local templateFormat, isBlockFormatted, formatType = p.convertFormatString( rawTemplateFormat );
	local nameFormat = mw.ustring.match( templateFormat, '^[^|]+' );
	local paramKeyFormat = mw.ustring.match( templateFormat, '%|[^=]+=' );
	local paramValueFormat = mw.ustring.match( templateFormat, '=[^}]+' );
	paramValueFormat = mw.ustring.sub( paramValueFormat, 2 );
	local endFormat = mw.ustring.match( templateFormat, '%}%}.*$' );
	local startFormat = mw.ustring.gsub( nameFormat, '_', templateName );

	return isBlockFormatted, formatType, startFormat, endFormat, paramKeyFormat, paramValueFormat;
end

function p.formatKeyValue( key, parameterData, formatData )
	if parameterData[ 'deprecated' ] then
		return '';
	end
	local args = formatData.args;
	local parameterName = key;
	local nkey = tonumber( key );
	
	-- Add additional spacing to string keys
	if formatBlockSpaces and formatData.parameterLength and formatData.formatType ~= 'inline' and ( nkey == nil or lastNumber ~= nkey - 1 ) then
		while mw.ustring.len( key ) < formatData.parameterLength do
			key = key .. ' ';
		end
	end
	
	-- Remove numbering for adjacent numbered keys
	if nkey ~= nil and lastNumber == nkey - 1 then
		key = '';
		lastNumber = nkey;
	end

	local value = '';
	if formatData.valueKey == 'example' and parameterData[ 'example' ] then
		-- Example
		value = parameterData[ 'example' ];
	else
		if formatData.valueKey == 'description' and parameterData[ 'description' ] then
			-- Description
			value = parameterData[ 'description' ];
			if value ~= '' then
				value = '<!-- ' .. value .. ' -->';
			end
		elseif parameterData[ 'autovalue' ] then
			-- Autovalue
			value = parameterData[ 'autovalue' ];
		end
		if args[ '$' .. parameterName ] and args[ '$' .. parameterName ] ~= '' then
			-- Custom values from template call
			value = args[ '$' .. parameterName ];
		end
	end

	local formattedKey = mw.ustring.gsub( formatData.paramKeyFormat, '_+', key, 1 );
	if key == '' then
		formattedKey = mw.ustring.gsub( formattedKey, '=', '' );
	end

	return formattedKey .. mw.ustring.gsub( formatData.paramValueFormat, '_', value, 1 );
end

function p.formatCoordinateRow( row, templateData, formatData )
	local formattedParams = {};
	for _, parameterName in ipairs( row ) do
		if templateData[ 'params' ][ parameterName ] ~= nil then
			local parameterData = p.getValue( templateData[ 'params' ], parameterName );
			if parameterData[ 'inherits' ] then
				parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
			end

			if not parameterData[ 'deprecated' ] then
				local formatted = p.formatKeyValue( parameterName, parameterData, formatData );
				if formatted ~= '' then
					table.insert( formattedParams, formatted );
				end
			end
		end
	end

	for i = 1, #formattedParams - 1 do
		formattedParams[ i ] = mw.ustring.gsub( formattedParams[ i ], '%s+$', '' );
	end
	return table.concat( formattedParams, ' ' );
end

function p.generateBlankCode( templateData, templateName, args )
	if templateData == false then
		return '{{' .. templateName .. '}}';
	end
	
	local parameterLength = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if not parameterData[ 'deprecated' ] then
			local length = mw.ustring.len( parameterName );
			if length > parameterLength then
				parameterLength = length;
			end
		end
	end

	local isBlockFormatted, formatType, startFormat, endFormat, paramKeyFormat, paramValueFormat = p.getFormatParts( templateData[ 'format' ], templateName );
	local out = startFormat;
	local coordinateParamRows = {};
	for rowIndex, row in ipairs( coordinateRows ) do
		for _, parameterName in ipairs( row ) do
			coordinateParamRows[ parameterName ] = rowIndex;
		end
	end
	local formattedCoordinateRows = {};

	lastNumber = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local formatData = {
			args = args,
			valueKey = ( args[ 'description' ] and 'description' or nil ),
			formatType = formatType,
			isBlockFormatted = isBlockFormatted,
			parameterLength = parameterLength,
			paramKeyFormat = paramKeyFormat,
			paramValueFormat = paramValueFormat,
		};
		local coordinateRow = coordinateParamRows[ parameterName ];
		if coordinateRow then
			if not formattedCoordinateRows[ coordinateRow ] then
				formattedCoordinateRows[ coordinateRow ] = true;
				formatData.parameterLength = nil;
				out = out .. p.formatCoordinateRow( coordinateRows[ coordinateRow ], templateData, formatData );
			end
		else
			local parameterData = p.getValue( templateData[ 'params' ], parameterName );
			if parameterData[ 'inherits' ] then
				parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
			end
			out = out .. p.formatKeyValue( parameterName, parameterData, formatData );
		end
	end
	
	return out .. endFormat;
end

function p.generateBlank( frame )
	local getArgs = require( 'Module:Arguments' ).getArgs;
	local args = getArgs( frame );
	local templateName = frame.args[ 1 ];
	table.remove( args, 1 );
	local shownDeprecatedParams = p.parseParamList( args[ 'show' ] or args[ 'показывать' ] );
	
	local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
	local templateData = p.getTemplateData( docPage, shownDeprecatedParams );
	local out = p.generateBlankCode( templateData, templateName, args );

	local previewNote = ''
	if templateData == false and frame:preprocess('{{REVISIONID}}') == '' then
		previewNote = '<div class="warningbox">' .. noDocNote .. '</div>';
	end
	
	return previewNote .. frame:extensionTag{
		name = 'syntaxhighlight',
		args = { lang = 'html', copy = true },
		content = out,
	};
end

function p.generateExampleCode( templateData, templateName, args )
	if templateData == false then
		return '{{' .. templateName .. '}}';
	end
	
	local parameterLength = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if parameterData[ 'example' ] and not parameterData[ 'deprecated' ] then
			local length = mw.ustring.len( parameterName );
			if length > parameterLength then
				parameterLength = length;
			end
		end
	end

	local isBlockFormatted, formatType, startFormat, endFormat, paramKeyFormat, paramValueFormat = p.getFormatParts( templateData[ 'format' ], templateName );
	local out = startFormat;

	lastNumber = 0;
	for i, parameterName in ipairs( templateData[ 'paramOrder' ] ) do
		local parameterData = p.getValue( templateData[ 'params' ], parameterName );
		if parameterData[ 'inherits' ] then
			parameterData = p.getValue( templateData[ 'params' ], parameterData[ 'inherits' ] );
		end
		
		if parameterData[ 'example' ] then
			out = out .. p.formatKeyValue( parameterName, parameterData, {
				args = args,
				valueKey = 'example',
				formatType = formatType,
				isBlockFormatted = isBlockFormatted,
				parameterLength = parameterLength,
				paramKeyFormat = paramKeyFormat,
				paramValueFormat = paramValueFormat,
			} );
		end
	end
	
	return out .. endFormat;
end	

function p.generateExample( frame )
	local args = frame.args;
	local templateName = frame.args[ 1 ];
	local shownDeprecatedParams = p.parseParamList( args[ 'show' ] or args[ 'показывать' ] );
	local docPage = 'Template:' .. templateName .. '/' .. docSubPage;
	local templateData = p.getTemplateData( docPage, shownDeprecatedParams );
	local out = p.generateExampleCode( templateData, templateName, args );
	
	local previewNote = ''
	if templateData == false and frame:preprocess('{{REVISIONID}}') == '' then
		previewNote = '<div class="warningbox">' .. noDocNote .. '</div>';
	end
	
	return previewNote .. frame:preprocess( out ) .. frame:extensionTag{
		name = 'syntaxhighlight',
		args = { lang = 'html' },
		content = out,
	};
end

return p;