• Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

Module:Example

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索
Template-info.svg 模块文档  [创建] [刷新]
local module = {}

local insert = table.insert
local concat = table.concat
local getArgs = require('Module:Arguments').getArgs
local NOWIKI_STRIP_MARKER_PATTERN = '\127\'"`UNIQ%-%-nowiki%-%x%x%x%x%x%x%x%x%-QINU`"\'\127'


local function _main(args, frame)
	local codes = args[1]
	local isPrint = args[2] == 'print'

	local preTag = frame:extensionTag('pre', codes)
	local executedResult = frame:preprocess(
		mw.text.decode(
			mw.text.unstripNoWiki(codes)
		)
	)

	if isPrint then
		return preTag..mw.text.trim(executedResult)
	else
		frame:callParserFunction('#vardefine', 'example-view', executedResult)
		return preTag
	end
end


local unstripNoWiki = mw.text.unstripNoWiki
local entityToChar = {['&lt;'] = '<', ['&gt;'] = '>'}
local charToEntity = {
	['<'] = '&lt;',
	['>'] = '&gt;',
	['&'] = '&amp;',
	[':'] = '&#58;',  -- 防止自动超链接
	['['] = '&#91;',
	[']'] = '&#93;',
	['{'] = '&#123;',
	['|'] = '&#124;',
	['}'] = '&#125;',
}

--- 从文本中提取出用于显示的代码(经过转义)和用于执行的代码
local function extractCodeForDisplayAndForExec(text)
	local codeForDisplay = {}
	local codeForExec = {}

	local function appendWikitext(normalText)
		codeForDisplay[#codeForDisplay + 1] = normalText
	end

	local function appendNowiki(nowikiStripMarker)
		-- 妈的mw.text.unstripNoWiki(<nowiki>< > &lt; &gt;</nowiki>)会得到'&lt; &gt; &lt; &gt;',
		-- 这导致<nowiki>< ></nowiki>与<nowiki>&lt; &gt;</nowiki>之间的区别丢失了。
		-- 因此,我们只能择其一:
		-- - 将unstripNoWiki得到的所有'&lt;'和'&gt;'替换为'<'和'>',在调用模板时以其他方式表达'&lt;'和'&gt;'
		-- - 不转换unstripNoWiki得到的'&lt;'和'&gt;',在调用模板时以其他方式表达'<'和'>'
		-- 考虑到'<'、'>'的使用频率更高,此模块选择了前者。
		local encodedSrcCode
		local srcCode = unstripNoWiki(nowikiStripMarker):gsub('&[lg]t;', entityToChar)
		do
			-- <_nowiki>和</_nowiki>转换为<nowiki>和</nowiki>
			-- <__nowiki>和</__nowiki>转换为<_nowiki>和</_nowiki>
			-- 以此类推
			local matchTimes = 0
			srcCode, matchTimes =
				srcCode:gsub('<(/?_-)_([nN][oO][wW][iI][kK][iI]%s*)>', '<%1%2>')
			if matchTimes > 0 then
				encodedSrcCode = srcCode
			end
		end
		if encodedSrcCode or srcCode:find('[<>&]') then
			encodedSrcCode = (encodedSrcCode or srcCode):gsub('[<>&:%[%]{|}]', charToEntity)
		end
		codeForDisplay[#codeForDisplay + 1] = encodedSrcCode or nowikiStripMarker
		codeForExec[#codeForExec + 1] = srcCode
	end

	local last = 1
	local nowikiStart, nowikiEnd = text:find(NOWIKI_STRIP_MARKER_PATTERN)
	local beginsWithNowiki = nowikiStart == 1  -- 这个值用来稍后去除多余换行

	while nowikiStart do
		if nowikiStart > last then
			-- 补上<nowiki>之前的部分
			appendWikitext(text:sub(last, nowikiStart - 1))
		end
		appendNowiki(text:sub(nowikiStart, nowikiEnd))
		last = nowikiEnd + 1
		nowikiStart, nowikiEnd = text:find(NOWIKI_STRIP_MARKER_PATTERN, last)
	end
	if last <= #text then
		appendWikitext(text:sub(last))
	else
		local len = #codeForExec
		codeForExec[len] = codeForExec[len]:gsub('\n$', '', 1)
	end

	-- 删除多余换行
	if beginsWithNowiki then
		codeForExec[1] = codeForExec[1]:gsub('^\n', '', 1)
	end

	return concat(codeForDisplay), concat(codeForExec)
end


local function canary(args, frame)
	local tag, code, seperator
	if args.pre then
		tag = 'pre'
		code = args.pre
		seperator = '\n'..(args[1] or '')
	elseif args.code then
		tag = 'code'
		code = args.code
		seperator = args[1] or ''
	elseif args.bare then
		tag = nil
		code = args.bare
		seperator = args[1] or ''
	elseif args[1] then
		tag = 'pre'
		code = args[1]
		seperator = '\n'..(args[2] or '')
	else
		return '<strong class="error">{{[[Template:Example|Example]]}}调用错误</strong>'
	end
	assert(code)
	assert(seperator)

	local codeForDisplay, codeForExec = extractCodeForDisplayAndForExec(code)
	local result = tag and {'<', tag, '>', codeForDisplay, '</', tag, '>'} or {codeForDisplay}

	if args.echo == '' then
		return concat(result)
	end

	result[#result + 1] = seperator
	result[#result + 1] = frame:preprocess(codeForExec)
	return concat(result)
end


function module.main(frame)
	local parent = frame:getParent()
	if parent and parent:getTitle() == 'Template:Example' then
		-- frame = parent
	else
		parent = nil
		-- 为了兼容旧函数才这样写
		-- canary状态结束后改掉
	end
	local args = (parent or frame).args
	if args.pre or args.code or args.bare then
		return canary(args, parent or frame)
	end

	return _main(getArgs(frame), frame)
end


return module