Мазмунга өтүү

Модуль:module documentation

Wiktionary дан

Бул модул башка модулдар үчүн документтерди автоматтык түрдө жаратат. Ал Луа кодундагы саптагы комментарийлерди алат жана аларды {{module documentation}} аркылуу документация бетинде колдонула турган формага айлантат. Чынында, бул модуль документациясы бул иш-аракеттин мисалы!

Документтерди ушундай жол менен жасоо пайдалуу, анткени функция/ыкма документтери эки жерде жеткиликтүү: модулдун бетинин жогору жагында (шарттуу Wikitext катары) жана функциянын үстүндө (Lua комментарийи катары). Ар бири түзөтүү стилинин башка түрүнө туура келет жана муну ушундай кылуу аларды синхрондоштурууну камсыз кылат.

Документтердин бөлүмү Луанын көп саптуу комментарий синтаксисин колдонуу менен берилет, ал төмөнкүдөй көрүнөт: --[==[ ... ]==]. Документтөө туура болушу үчүн бирдей белгилердин саны адатта экиден болушу керек {{module documentation}} тарабынан бурмаланган. Документтин ичинде төмөнкү конвенциялар колдонулушу мүмкүн:

  1. Узун саптарды (кадимки абзацтарда да, тизмедеги пункттарда да) жаңы саптар, андан кийин боштуктар менен үзүүгө болот же өтмөктөр (өзгөчө тизмелерде пайдалуу, чийки комментарийди окула турган кылуу үчүн). Мындай учурда, жаңы сап өзгөртүлөт мейкиндикке. Абзацтарды бузуу үчүн катары менен эки жаңы сапты колдонуңуз. Жалпысынан алганда, сызыктарды эң көп болгондон кийин үзүү сунушталат 120 белги, чийки комментарий окууну жеңилдетүү үчүн.
  2. Шаблон чалуулары (эки кашаа аркылуу) түз мааниде киргизилиши мүмкүн жана кеңейтилет.
  3. Жалгыз кашаалар түз текстти курчоого колдонсо болот жана автоматтык түрдө Луа коду катары синтаксисте баса белгиленет. Уюшкан бул түз тексттин ичиндеги кашаалар тең салмактуу болсо, туура иштетилет. Эгерде биринчи каарман түзмө-түз текст кашаа, анын алдына боштук коюңуз (бирок аягында эмес) жана ага көңүл бурулбайт.
  4. Арткы тырмакчалар <code>...</code> аркылуу көрсөтүлө турган түз текстти курчоого колдонсо болот. Ичиндеги заттар арткы тырмакчалар арткы тырмакчаны камтышы мүмкүн эмес (эки арткы тырмакча менен толтургуч өзгөрмө аттарынан тышкары) же бир нечеге чейин узартылышы керек сызыктар.
  5. Кош арткы тырмакчалар <var>...</var> аркылуу көрсөтүлө турган толтургучтун өзгөрмө аттарын курчоо үчүн колдонулушу мүмкүн. Арткы цитаталардын ичиндеги нерселер тамгаларды, сандарды, астынкы сызыктарды, дефистерди жана чекиттерди гана камтышы мүмкүн. Адатта, мындай толтургучтар <code>...</code> менен курчалгандай, мономейкиндик шрифтинде көрсөтүлүшү керек. Бул таасир болушу мүмкүн бир арткы тырмакчанын ичиндеги кош арткы синтаксисти эффективдүү колдонгон үчтүк артка тырмакчаларды колдонуу менен жетишилген синтаксис.

Кээ бир атайын директивалар, эгер менен бир сапта жайгаштырылса, ачылуучу көп саптык комментарий индикаторун ээрчий алат көрсөткүч. Атап айтканда, азыркы учурда төмөнкү директивалар таанылат:

  • Intro: директивасы өз алдынча киришүү текстин билдирет, ал функциянын алдында, башында жайгаштырылат документтер. Бул модулга жалпы киришүү/сезүү берүү үчүн пайдалуу.
  • func: export.function(arg1, arg2, ...) директивасын документтештирүүдө колдонсо болот. стандарттуу эмес түрдө жарыяланган функция (мисалы, метатаблица аркылуу, анонимдүү же локалдык түрдө жарыяланган функция аркылуу "экспорт" таблицасына дайындалган ж.б.). Директива функциянын пайда болушунун каалаган жолун көрсөтөт жана комментарийдин калган бөлүгү адаттагыдай эле функциянын ишин сүрөттөйт.

export.show

[түзөтүү]

function export.show(frame)

The main entrypoint for {{module documentation}}. The frame object can contain 3 optional arguments:

  • |comment_level=: The number of equals signs (=) a given section uses. Default: 2 (i.e. --[==[ ... (comment block) ]==])
    e.g. The value 4 means --[====[ ... (comment block) ]====].
  • |section_level=: The header level used for each function/method. Default: 2 (i.e. L2: == ... ==).
  • |identifier=: A Lua string pattern. Only the comments of functions whose names match this pattern are used. When not given, all function are accepted.
    This is useful when giving object methods, using a pattern such as ^object_name:.

local m_str_utils = require("Module:string utilities")

local codepoint = m_str_utils.codepoint
local concat = table.concat
local insert = table.insert
local u = m_str_utils.char
local rsplit = m_str_utils.split

local export = {}

--[===[ intro:
Бул модул башка модулдар үчүн документтерди автоматтык түрдө жаратат. Ал Луа кодундагы саптагы комментарийлерди алат жана
аларды {{tl|module documentation}} аркылуу документация бетинде колдонула турган формага айлантат. Чынында, бул
модуль документациясы бул иш-аракеттин мисалы!

Документтерди ушундай жол менен жасоо пайдалуу, анткени функция/ыкма документтери эки жерде жеткиликтүү:
модулдун бетинин жогору жагында (шарттуу Wikitext катары) жана функциянын үстүндө (Lua комментарийи катары). Ар бири
түзөтүү стилинин башка түрүнө туура келет жана муну ушундай кылуу аларды синхрондоштурууну камсыз кылат.

Документтердин бөлүмү Луанын көп саптуу комментарий синтаксисин колдонуу менен берилет, ал төмөнкүдөй көрүнөт:
{--[==[ ... ]==]}. Документтөө туура болушу үчүн бирдей белгилердин саны адатта экиден болушу керек
{{tl|module documentation}} тарабынан бурмаланган. Документтин ичинде төмөнкү конвенциялар колдонулушу мүмкүн:
# Узун саптарды (кадимки абзацтарда да, тизмедеги пункттарда да) жаңы саптар, андан кийин боштуктар менен үзүүгө болот
 же өтмөктөр (өзгөчө тизмелерде пайдалуу, чийки комментарийди окула турган кылуу үчүн). Мындай учурда, жаңы сап өзгөртүлөт
 мейкиндикке. Абзацтарды бузуу үчүн катары менен эки жаңы сапты колдонуңуз. Жалпысынан алганда, сызыктарды эң көп болгондон кийин үзүү сунушталат
 120 белги, чийки комментарий окууну жеңилдетүү үчүн.
# Шаблон чалуулары (эки кашаа аркылуу) түз мааниде киргизилиши мүмкүн жана кеңейтилет.
# Жалгыз кашаалар түз текстти курчоого колдонсо болот жана автоматтык түрдө Луа коду катары синтаксисте баса белгиленет. Уюшкан
 бул түз тексттин ичиндеги кашаалар тең салмактуу болсо, туура иштетилет. Эгерде биринчи каарман
 түзмө-түз текст кашаа, анын алдына боштук коюңуз (бирок аягында эмес) жана ага көңүл бурулбайт.
# Арткы тырмакчалар {<code>...</code>} аркылуу көрсөтүлө турган түз текстти курчоого колдонсо болот. Ичиндеги заттар
 арткы тырмакчалар арткы тырмакчаны камтышы мүмкүн эмес (эки арткы тырмакча менен толтургуч өзгөрмө аттарынан тышкары) же бир нечеге чейин узартылышы керек
 сызыктар.
# Кош арткы тырмакчалар {<var>...</var>} аркылуу көрсөтүлө турган толтургучтун өзгөрмө аттарын курчоо үчүн колдонулушу мүмкүн.
 Арткы цитаталардын ичиндеги нерселер тамгаларды, сандарды, астынкы сызыктарды, дефистерди жана чекиттерди гана камтышы мүмкүн. Адатта, мындай
 толтургучтар {<code>...</code>} менен курчалгандай, мономейкиндик шрифтинде көрсөтүлүшү керек. Бул таасир болушу мүмкүн
 бир арткы тырмакчанын ичиндеги кош арткы синтаксисти эффективдүү колдонгон үчтүк артка тырмакчаларды колдонуу менен жетишилген
 синтаксис.

Кээ бир атайын директивалар, эгер менен бир сапта жайгаштырылса, ачылуучу көп саптык комментарий индикаторун ээрчий алат
көрсөткүч. Атап айтканда, азыркы учурда төмөнкү директивалар таанылат:

* `Intro:` директивасы өз алдынча киришүү текстин билдирет, ал функциянын алдында, башында жайгаштырылат
 документтер. Бул модулга жалпы киришүү/сезүү берүү үчүн пайдалуу.
* `func: export.<var>function</var>(<var>arg1</var>, <var>arg2</var>, ...)` директивасын документтештирүүдө колдонсо болот.
 стандарттуу эмес түрдө жарыяланган функция (мисалы, метатаблица аркылуу, анонимдүү же локалдык түрдө жарыяланган функция аркылуу
 "экспорт" таблицасына дайындалган ж.б.). Директива функциянын пайда болушунун каалаган жолун көрсөтөт жана
 комментарийдин калган бөлүгү адаттагыдай эле функциянын ишин сүрөттөйт.
]===]

local TEMP_LEFT_BRACE = u(0xFFF0)
local TEMP_NEWLINE = u(0xFFF1)

local function format_doc(str)
	local code_blocks = {}
	local code_blocks_i = 0
	local private_use_start = 0x100000
	local subbed_str = (str
		-- Multiline literal text between backquotes; you can't use <pre> or <syntaxhighlight> because that
		-- disables Wikitext parsing for <var>...</var>, italics, <span>...</span> etc. Instead use the trick of
		-- putting a space at the beginning of each line, which yields monospace text without disabling Wikitext
		-- interpretation.
		:gsub("```(.-)```", function(inside)
			return inside
				 -- placeholder variable names between double backquotes; we need to repeat this here to avoid
				 -- the following pattern for single backquotes from clobbering double backquotes
				:gsub("``([A-Za-z0-9_%-. ]+)``", "<var>%1</var>")
				 -- single backquotes undo monospacing
				:gsub("`([^`\n]+)`", '<span style="font-family: sans-serif;">%1</span>')
				 -- text on the first line should be monospaced
				:gsub("^([^\n])", " %1")
				 -- text after a newline should be monospaced, and temp-escape the newline so later replacements
				 -- to join continued lines in a paragraph don't take effect
				:gsub("\n", TEMP_NEWLINE .. " ")
				-- escape { so it won't be interpreted as a code block
				:gsub("{", TEMP_LEFT_BRACE)
		end)
		:gsub("``([A-Za-z0-9_%-. ]+)``", "<var>%1</var>") -- placeholder variable names between double backquotes
		:gsub("`([^`\n]+)`", function(inside) -- literal text between backquotes, set using <code>...</code>
			-- Escape { so it won't be interpreted as a code block.
			inside = inside:gsub("{", TEMP_LEFT_BRACE)
			return "<code>" .. inside .. "</code>"
		end)
		 -- {} blocks: blocks of code
		 -- Escape to avoid removing line breaks.
		:gsub("%b{}", function(m0)
			local next_char = m0:sub(2, 2)
			if next_char == "|" then
				-- Wikitable; don't try to parse it as code. But we do want to parse special syntax in them (in
				-- particular {...} syntax for embedded code snippets), and if we return nil that won't happen.
				-- Instead, we call format_doc() recursively on the innards.
				return "{" .. format_doc(m0:sub(2, -2)) .. "}"
			end
			if next_char == "{" and m0:sub(-2, -2) == "}" then return nil end
			local text = "<syntaxhighlight lang=lua" .. (m0:match("\n") and "" or " inline") .. ">" .. m0:sub(2, -2):gsub("^ +", "") .. "</syntaxhighlight>"
			-- Prevent any further processing by storing the desired text into the `code_blocks` array and replacing
			-- the whole thing with a single private-use-area character.
			code_blocks_i = code_blocks_i + 1
			code_blocks[code_blocks_i] = text
			return u(private_use_start + code_blocks_i)
		end)
		-- undo escaping of left brace to prevent code block interpretation
		:gsub(TEMP_LEFT_BRACE, "{")
		-- Join continued lines in a paragraph. We don't want to do that if there are two newlines in a row,
		-- and not if the second line begins with whitespace or a certain special characters (#, * or : indicating
		-- a list item; | indicating a wikitable item; semicolon for bolded items).
    	:gsub("([^\n])\n[ \t]*([^ \t\n#*:;|])", "%1 %2")
    	-- Repeat the previous in case of a single-character line (admittedly rare).
    	:gsub("([^\n])\n[ \t]*([^ \t\n#*:;|])", "%1 %2")
		:gsub("\n[ \t]+%f[*#:;]", "\n") -- remove indentation for list items
		:gsub("%f[\n,{]\n%f[^\n*#:;]", "\n\n") -- wiki newlines
		:gsub("(\n[ *#:]*)(|?[_%w]+=?):", "%1<code><b>%2</b></code>:") -- parameter names
		-- double-underline to indicate types (displayed as italicized underlined)
		:gsub("__(.-)__", function(inside)
			return "<u><i>" .. inside .. "</i></u>"
		end)
		-- undo escaping of newline to prevent joining of continued lines
		:gsub(TEMP_NEWLINE, "\n"))

	-- Put <code>...</code> around <var>...</var> invocations that don't already occur inside of
	-- <code>...</code> blocks.
	local split_on_code = rsplit(subbed_str, "(<code>.-</code>)")
	for i = 1, #split_on_code, 2 do
		split_on_code[i] = split_on_code[i]:gsub("(<var>.-</var>)", "<code>%1</code>")
	end
	subbed_str = concat(split_on_code)
	return (subbed_str
		-- Undo code-block stashing.
		:gsub("\244[\128-\191][\128-\191][\128-\191]", function(char)
			return code_blocks[codepoint(char) - private_use_start]
		end))
end

--[===[
The main entrypoint for {{tl|module documentation}}. The frame object can contain 3 optional arguments:
* |comment_level=: The number of equals signs (=) a given section uses. Default: 2 (i.e. {--[==[ ... (comment block) ]==]})
*: e.g. The value 4 means {--[====[ ... (comment block) ]====]}.
* |section_level=: The header level used for each function/method. Default: 2 (i.e. L2: {== ... ==}).
* |identifier=: A Lua string pattern. Only the comments of functions whose names match this pattern are used. When not given, all function are accepted.
*: This is useful when giving object methods, using a pattern such as {^object_name:}.
]===]
function export.show(frame)
	local args = frame:getParent().args or {}
	
	local comment_level = tonumber(args["comment_level"]) or 2
	local function make_comment_pattern(typeid)
		if typeid then
			typeid = "%s*" .. typeid
		else
			typeid = ""
		end
		return "%-%-%[" .. ("="):rep(comment_level) .. "%[" .. typeid .. "\n?(.-)[\t\n]*]" .. ("="):rep(comment_level) .. "]()"
	end
	local fn_comment_pattern = make_comment_pattern(nil)
	local intro_comment_pattern = make_comment_pattern("intro:")
	local metafunc_comment_pattern = make_comment_pattern("func:%s*(([^\n(]+)[^\n)]+%))")
	local section_mark = ("="):rep(tonumber(args["section_level"]) or 2)
	local pattern_identifier = args["identifier"] or ""
	
	local mod_title = mw.title.getCurrentTitle()
	if mod_title.text:match("/documentation$") then return "(<i>The generated documentation is located at the module page.</i>)" end
	local mod_text = mod_title:getContent()
	if not mod_text then return "(<i>The module page does not exist now.</i>)" end

	-- This contains function and intro documentation. Each element is a two-element list of {POSITION, DOCS} specifying
	-- the generated documentation for a function and the character position in the file where it was found (for sorting
	-- purposes).
	local docs
	
	local intro_comment = mod_text:match("^.-" .. intro_comment_pattern)
	if intro_comment then
		docs = { {1, format_doc(intro_comment) }}
	else
		docs = {}
	end

	-- Look for actual functions.
	for p0, f, fn in mod_text:gmatch("()\n[ \t]*function +(([^\n(]+)[^\n)]+%))") do
		if fn:match(pattern_identifier) then			
			local c = mod_text:sub(1, p0 - 1):match("^.*" .. fn_comment_pattern .. "%s*$")
			insert(docs, {p0, section_mark .. fn .. section_mark .. "\n\n" .. "<syntaxhighlight lang=lua inline>function " ..
				f .. "</syntaxhighlight>\n\n" .. format_doc(c or
				'<strong class="error">This function lacks documentation. Please add a description of its usages, inputs and outputs, ' ..
				"or its difference from similar functions, or make it local to remove it from the function list.</strong>" ..
				"[[Category:Templates and modules needing documentation]]")})
		end
	end

	-- Now look for comments with the function declaration inside them (used for metatable functions etc.).
	for p0, f, fn, comment in mod_text:gmatch("()" .. metafunc_comment_pattern) do
		insert(docs, {p0, section_mark .. fn .. section_mark .. "\n\n" .. "<syntaxhighlight lang=lua inline>function " .. f .. "</syntaxhighlight>\n\n" .. format_doc(comment)})
	end

	table.sort(docs, function(a, b) return a[1] < b[1] end)
	
	local chunks = {}
	for i, decl in ipairs(docs) do
		insert(chunks, decl[2])
	end

	return frame:preprocess(concat(chunks, "\n\n"))
end

return export