Modül:Docbunto/renderer
Doclet renderer for Docbunto taglet data.
renderer(data, frame, modname)(function)- Doclet renderer for Docbunto taglet data.
- Parameters:
- Returns: Wikitext documentation output. (string)
type_reference(item)(function • local)- Doclet type reference preprocessor. Formats types with links to the Lua reference manual.
- Parameter:
itemItem documentation data. (table) render_item(stream, item, modname)(function • local)- Doclet item renderer.
- Parameters:
render_tag(stream, name, tag)(function • local)- Doclet tag renderer.
- Parameters:
function_name(item)(function • local)- Doclet function preprocessor. Formats item name as a function call with top-level arguments.
- Parameter:
itemItem documentation data. (table) variable_prefix(item)(function • local)- Doclet parameter/field subitem preprocessor. Indents and wraps variable prefix with
codetag. - Parameter:
itemItem documentation data. (table) usage_highlight(item)(function • local)- Doclet usage subitem preprocessor. Formats usage example with
<syntaxhighlight>tag. - Parameter:
itemItem documentation data. (table) error_line(item)(function • local)- Doclet error subitem preprocessor. Formats line numbers (
{#}) in error tag values. - Parameter:
itemItem documentation data. (table)
--- Doclet renderer for Docbunto taglet data.
-- @script renderer
-- @author [[dev:User:8nml]]
-- @author [[wikipedia:User:Awesome Aasim]]
-- @param {table} data Taglet documentation data.
-- @param {Frame} frame Scribunto frame object.
-- @param {string} modname Module page name.
-- @return {string} Wikitext documentation output.
require('strict')
-- Module dependencies.
local references = mw.loadData('Module:Docbunto/references')
local dtags = mw.loadData('Module:Docbunto/tags')
-- Docbunto private logic.
local function get_tag_name(tag, num)
if tag == 'error' then
if num == 1 then
return 'Error'
else
return 'Errors'
end
elseif tag == 'field' then
if num == 1 then
return 'Field'
else
return 'Fields'
end
elseif tag == 'fixme' then
if num == 1 then
return 'Bug'
else
return 'Bugs'
end
elseif tag == 'note' then
if num == 1 then
return 'Note'
else
return 'Notes'
end
elseif tag == 'param' then
if num == 1 then
return 'Parameter'
else
return 'Parameters'
end
elseif tag == 'return' then
return 'Returns'
elseif tag == 'see' then
return 'See also'
elseif tag == 'todo' then
return 'TODO'
elseif tag == 'usage' then
return 'Usage'
elseif tag == 'warning' then
if num == 1 then
return 'Warning'
else
return 'Warnings'
end
end
end
--- Doclet type reference preprocessor.
-- Formats types with links to the [[mw:Extension:Scribunto/Lua reference manual|Lua reference manual]].
-- @function type_reference
-- @param {table} item Item documentation data.
-- @local
local function type_reference(item)
if item.value and item.value:match('^%S+') == '<code>...</code>' then
item.value = item.value:gsub('^(%S+)', mw.text.tag{
name = 'code',
content = '[[mw:Extension:Scribunto/Lua reference manual#varargs|...]]'
})
end
if not item.type then
return
end
item.type = item.type:gsub(' ', '\26')
local space_ptn = '[;|][%s\26]*'
local types, t = mw.text.split(item.type, space_ptn)
local spaces = {}
for space in item.type:gmatch(space_ptn) do
table.insert(spaces, space)
end
for index, type in ipairs(types) do
t = types[index]
local data = references.types[type]
local name = data and data.name or t
if data then
types[index] = '[[' .. data.link .. '|' .. name .. ']]'
elseif not t:find('^line') and not dtags._generic_tags[t] then
types[index] = '[[#' .. t .. '|' .. name .. ']]'
end
end
for index, space in ipairs(spaces) do
types[index] = types[index] .. space
end
item.type = table.concat(types)
if item.alias then
mw.log(item.type)
end
item.type = item.type:gsub('\26', ' ')
end
--- Doclet item renderer.
-- @function render_item
-- @param {table} stream Wikitext documentation stream.
-- @param {table} item Item documentation data.
-- @param {string} modname Module page name.
-- @local
local function render_item(stream, item, modname)
local item_id = item.alias or item.name
local item_name = item.alias or item.name
type_reference(item)
local item_type = item.type
for _, name in ipairs(dtags._subtype_hierarchy) do
if item.tags[name] then
item_type = item_type .. ' • ' .. name
end
end
item_type = ' (' .. item_type .. ')'
if modname == mw.title.getCurrentTitle().fullText then
stream:wikitext(';[[#L-' .. item.lineno .. '|<code id="' .. item_id .. '">' .. item_name .. '</code>]]' .. item_type):newline()
else
stream:wikitext(';[[' .. modname .. '#L-' .. item.lineno .. '|<code id="' .. item_id .. '">' .. item_name .. '</code>]]' .. item_type):newline()
end
if (#(item.summary or '') + #item.description) ~= 0 then
local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
and (item.description:find('^[{:#*]+%s+') and '\n' or ' ')
or ''
local intro = (item.summary or '') .. separator .. item.description
stream:wikitext(':' .. intro:gsub('\n([{:#*])', '\n:%1'):gsub('\n\n([^=])', '\n:%1')):newline()
end
end
--- Doclet tag renderer.
-- @function render_tag
-- @param {table} stream Wikitext documentation stream.
-- @param {string} name Item tag name.
-- @param {table} tag Item tag data.
-- @local
local function render_tag(stream, name, tag)
if tag.value then
type_reference(tag)
local tag_name = get_tag_name(name, 1)
stream:wikitext(':<b>' .. tag_name .. '</b>: ' .. mw.text.trim(tag.value):gsub('\n([{:#*])', '\n:%1'))
if tag.value:find('\n[{:#*]') and (tag.type or (tag.modifiers or {})['opt']) then
stream:newline():wikitext(':')
end
if tag.type and (tag.modifiers or {})['opt'] then
stream:wikitext(' (' .. tag.type .. '; optional)')
elseif tag.type then
stream:wikitext(' (' .. tag.type .. ')')
elseif (tag.modifiers or {})['opt'] then
stream:wikitext(' (optional)')
end
stream:newline()
else
local tag_name = get_tag_name(name, #tag)
stream:wikitext(':<b>' .. tag_name .. '</b>: '):newline()
for _, tag_el in ipairs(tag) do
type_reference(tag_el)
stream:wikitext(':*' .. tag_el.value:gsub('\n([{:#*])', '\n:*%1'))
if tag_el.value:find('\n[{:#*]') and (tag_el.type or (tag_el.modifiers or {})['opt']) then
stream:newline():wikitext(':*' .. (tag_el.value:match('^[*:]+') or ''))
end
if tag_el.type and (tag_el.modifiers or {})['opt'] then
stream:wikitext(' (' .. tag_el.type .. '; optional)')
elseif tag_el.type then
stream:wikitext(' (' .. tag_el.type .. ')')
elseif (tag_el.modifiers or {})['opt'] then
stream:wikitext(' (optional)')
end
stream:newline()
end
end
end
--- Doclet function preprocessor.
-- Formats item name as a function call with top-level arguments.
-- @function function_name
-- @param {table} item Item documentation data.
-- @local
local function function_name(item)
local target = item.alias and 'alias' or 'name'
item[target] = item[target] .. '('
if
item.tags['param'] and
item.tags['param'].value and
not item.tags['param'].value:find('^[%w_]+[.[]')
then
if (item.tags['param'].modifiers or {})['opt'] then
item[target] = item[target] .. '<span style="opacity: 0.65;">'
end
item[target] = item[target] .. item.tags['param'].value:match('^(%S+)')
if (item.tags['param'].modifiers or {})['opt'] then
item[target] = item[target] .. '</span>'
end
elseif item.tags['param'] then
for index, tag in ipairs(item.tags['param']) do
if not tag.value:find('^[%w_]+[.[]') then
if (tag.modifiers or {})['opt'] then
item[target] = item[target] .. '<span style="opacity: 0.65;">'
end
item[target] = item[target] .. (index > 1 and ', ' or '') .. tag.value:match('^(%S+)')
if (tag.modifiers or {})['opt'] then
item[target] = item[target] .. '</span>'
end
end
end
end
item[target] = item[target] .. ')'
return item
end
--- Doclet parameter/field subitem preprocessor.
-- Indents and wraps variable prefix with <code>code</code> tag.
-- @function variable_prefix
-- @param {table} item Item documentation data.
-- @local
local function variable_prefix(item)
local indent_level, indentation
if item.value then
indent_level = item.value:match('^%S+') == '...'
and 0
or select(2, item.value:match('^%S+'):gsub('[.[]', ''))
indentation = ('*'):rep(indent_level)
item.value = indentation .. item.value:gsub('^(%S+)', '<code>%1</code>')
elseif item then
for _, item_el in ipairs(item) do
variable_prefix(item_el)
end
end
return item
end
--- Doclet usage subitem preprocessor.
-- Formats usage example with <code><syntaxhighlight></code> tag.
-- @function usage_highlight
-- @param {table} item Item documentation data.
-- @local
local function usage_highlight(item)
if item.value then
item.value = mw.text.trim(item.value)
item.value =
'<syntaxhighlight lang="lua"'.. (item.value:find('\n') and '' or ' inline') ..'>' ..
item.value ..
'</syntaxhighlight>'
elseif item then
for _, item_el in ipairs(item) do
usage_highlight(item_el)
end
end
return item
end
--- Doclet error subitem preprocessor.
-- Formats line numbers (<code>{#}</code>) in error tag values.
-- @function error_line
-- @param {table} item Item documentation data.
local function error_line(item)
if item.name then
local line
for mod in pairs(item.modifiers or {}) do
if mod:find('^%d+$') then line = mod end
end
if line then
if item.type then
item.type = item.type .. '; line ' .. line
else
item.type = 'line ' .. line
end
end
elseif item then
for _, item_el in ipairs(item) do
error_line(item_el)
end
end
return item
end
-- Docbunto package items.
return function(data, frame, modname)
local documentation = mw.html.create()
local namespace = '^' .. mw.site.namespaces[828].name .. ':'
local codepage = data.filename:gsub(namespace, '')
-- Documentation lede.
if (#(data.summary or '') + #data.description) ~= 0 then
local separator = #data.summary ~= 0 and #data.description ~= 0
and (data.description:find('^[{|!}:#*=]+[%s-}]+') and '\n\n' or ' ')
or ''
local intro = (data.summary or '') .. separator .. data.description
intro = frame:preprocess(intro:gsub('^(' .. codepage .. ')', '<b>%1</b>'))
documentation:wikitext(intro):newline():newline()
end
-- Start code documentation.
local codedoc = mw.html.create()
local function_module = data.tags['param'] or data.tags['return']
local header_type =
documentation.type == 'classmod'
and 'Package class'
or function_module
and 'Package function'
or 'Package items'
if (function_module or #data.items ~= 0) then
codedoc:wikitext('== Documentation =='):newline()
codedoc:wikitext('=== ' .. header_type .. ' ==='):newline()
end
-- Function module support.
if function_module then
data.type = 'function'
data.description = ''
render_item(codedoc, function_name(data), modname)
if data.tags['param'] then
render_tag(codedoc, 'param', variable_prefix(data.tags['param']))
end
if data.tags['error'] then
render_tag(codedoc, 'error', error_line(data.tags['error']))
end
if data.tags['return'] then
render_tag(codedoc, 'return', data.tags['return'])
end
end
-- Render documentation items.
local other_header = false
local private_header = false
local inaccessible
for _, item in ipairs(data.items) do
inaccessible = item.tags['local'] or item.tags['private']
if
not other_header and item.type ~= 'section' and item.type ~= 'type' and
not item.export and not item.hierarchy and not inaccessible
then
codedoc:wikitext('=== Other items ==='):newline()
other_header = true
end
if not private_header and inaccessible then
codedoc:wikitext('=== Private items ==='):newline()
private_header = true
end
if item.type == 'section' then
codedoc:wikitext('=== ' .. mw.ustring.gsub(item.summary or item.alias or item.name, '[.։。।෴۔።]$', '') .. ' ==='):newline()
if #item.description ~= 0 then
codedoc:wikitext(item.description):newline()
end
elseif item.type == 'type' then
codedoc:wikitext('=== <code>' .. (item.alias or item.name) .. '</code> ==='):newline()
if (#(item.summary or '') + #item.description) ~= 0 then
local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
and (item.description:find('^[{:#*=]+[%s-}]+') and '\n\n' or ' ')
or ''
codedoc:wikitext((item.summary or '') .. separator .. item.description):newline()
end
elseif item.type == 'function' then
render_item(codedoc, function_name(item), modname)
if item.tags['param'] then
render_tag(codedoc, 'param', variable_prefix(item.tags['param']))
end
if item.tags['error'] then
render_tag(codedoc, 'error', error_line(item.tags['error']))
end
if item.tags['return'] then
render_tag(codedoc, 'return', item.tags['return'])
end
elseif
item.type == 'table' or
item.type ~= nil and (
item.type:find('^member') or
item.type:find('^variable')
) and (item.alias or item.name)
then
render_item(codedoc, item, modname)
if item.tags['field'] then
render_tag(codedoc, 'field', variable_prefix(item.tags['field']))
end
end
if item.type ~= 'section' and item.type ~= 'type' then
if item.tags['note'] then
render_tag(codedoc, 'note', item.tags['note'])
end
if item.tags['warning'] then
render_tag(codedoc, 'warning', item.tags['warning'])
end
if item.tags['fixme'] then
render_tag(codedoc, 'fixme', item.tags['fixme'])
end
if item.tags['todo'] then
render_tag(codedoc, 'todo', item.tags['todo'])
end
if item.tags['usage'] then
render_tag(codedoc, 'usage', usage_highlight(item.tags['usage']))
end
if item.tags['see'] then
render_tag(codedoc, 'see', item.tags['see'])
end
end
end
-- Render module-level annotations.
local header_text
for _, tag_name in ipairs{'warning', 'fixme', 'note', 'todo', 'see'} do
if data.tags[tag_name] then
header_text = get_tag_name(tag_name, data.tags[tag_name].value and 1 or 2)
header_text = '== ' .. header_text .. ' =='
codedoc:newline():wikitext(header_text):newline()
if data.tags[tag_name].value then
codedoc:wikitext(data.tags[tag_name].value):newline()
else
for _, tag_el in ipairs(data.tags[tag_name]) do
codedoc:wikitext('* ' .. tag_el.value):newline()
end
end
end
end
-- Code documentation formatting.
codedoc = frame:preprocess(tostring(codedoc))
documentation:wikitext(codedoc)
return tostring(documentation)
end