Informationen zu dieser Dokumentation
Modul LuaDokumentation

Modulbeschreibung

Modul mit Templates zur (automatischen) Erstellung von Dokumentationen für Lua-Module. Dieses Modul verwendet Vorlage:LuaDokumentationsseite zur Erzeugung der Dokumentation. Siehe Funktion erzeugeDokumentation, wie dieses Modul in Wikibooks verwendet werden kann.

Funktionen

erzeugeDokumentation

Lua-Funktion, um aus den Kommentaren eines Moduls automatisch eine Dokumentationsseite zu erzeugen.

Wenn "Modul: ..../Doku" als Modul angegeben wird, wird nur "Modul: ...." verwendet. Module, die dieses Template benutzen, müssen als letzte Anweisung den Ausdruck return modulobjekt besitzen (mit genau diesen Abständen).

Kommentare

Bearbeiten

Kommentare, die später in der Dokumentation erscheinen sollen, müssen mit --- (3 Minusstriche) beginnen. Der darauf folgende Block von Kommentaren beginnend jeweils mit -- wird als ein Dokumentationsblock interpretiert. Der jeweils erste ist der Dokumentationsblock für das gesamte Modul, die anderen für Funktionen. Die Funktionsdeklaration muss dem Dokumentationsblock direkt folgen. (Bitte auf der Dokumentationsseite dieses Moduls melden, wenn eine Dunktionsdeklaration nicht erkannt wird).

Tags bezeichnen den Kommentar näher. Das sind @... Anweisungen. Alles vor dem ersten Tag wird als Beschreibung des Moduls, der Funktion interpretiert. Wiki-Formatierungen können dabei verwendet werden.

Folgende Tags gibt es

  • @example – Tag zum Starten eines Code-Beispiels. Dieses wird später über Vorlage:Codebeispiel eingebunden.
    • @code Code – der Code des Code-Beispiels
    • @description Beschreibung – Beschreibungstext des Code-Beispiels
    • @lang Sprache – Sprache des Code-Beispiels (entspricht Argument lang von Vorlage:Codebeispiel)
    • @dont_show_result – Wenn es sich beim Code um Mediawiki Text handelt, wird dieser nicht automatisch evaluiert.
  • @param Name Beschreibung – Beschreibung eines Arguments einer Funktion
    • @optional – zeigt an, dass der Parameter optional ist
    • @default_value Standardwert – Standardwert des Arguments, wenn dieser nicht angegeben ist (impliziert @optional)

Beispiele

Beispiel 1:

Folgender Lua-Aufruf erzeugt die Dokumentation für diese Seite
{{#invoke:LuaDokumentation|erzeugeDokumentation|modul=LuaDokumentation}}

Beispiel 2:

Folgender Lua-Aufruf erzeugt die Dokumentation auf einer "Modul: ..../Doku" Unterseite
{{#invoke:LuaDokumentation|erzeugeDokumentation}}

Parameter

  • modul (optional) – Modulname (Standardwert: aktuelle Seite in der {{#invoke:}} aufgerufen wird)

getModuleName

Funktion, um den Modulnamen einer Seite (also ohne Präfix Modul: und ohne Suffix /Doku) zu extrahieren.

Beispiele

Beispiel 1:

LuaDokumentation.getModuleName("Modul:HalloWelt")

Beispiel 2:

LuaDokumentation.getModuleName("Modul:LuaDokumentation/Doku")

Beispiel 3:

LuaDokumentation.getModuleName({args = { modul = "HalloWelt/Doku" } })

Parameter

  • arg (optional) – Seitentitel des Moduls oder einer Unterseite des Moduls als String oder das aktuelle Frame-Objekt (dann wird in args nach dem Parameter modul gesucht) (Standardwert: aktueller Seitentitel)

getModuleSourceCode

Funktion, die den Source-Code eines Lua-Modules zurückgibt

Parameter

  • module_name – Name des Moduls oder aktuelles Frame-Objekt (bei einem Frame-Objekt dann wird das Argument modul verwendet)

parseModule

Funktion, um die Dokumentation eines Lua-Moduls zu parsen. Es wird eine table zurückgegeben, die folgende Struktur aufweist

{
    description = "....",                              -- Modulbeschreibung
    examples = {                                       -- alle @examples der Modulbeschreibung (nil wenn keine @examples vorhanden sind)
        code = "...." or nil,
        description = "..." or nil,
        lang = "..." or nil,
        dont_show_result = true or nil
    },
    functions = {                                      -- Dokumentation aller Funktionen
        {
            name = "...",
            descriptions = "..."
            examples = { ... }                         -- wie bei Modulbeschreibung
            params = {                                 -- Liste aller @param Anweisungen (nil, wenn keine @param Anweisung vorhanden ist)
                 {
                     name = "...",
                     description = "...",
                     optional = true or nil,
                     default_value = "...." or nil
                 },
                 ...
            }
        },
        ...
    }
}

Parameter

  • module_name – Name des Moduls oder aktuelles Frame-Objekt (bei einem Frame-Objekt dann wird das Argument modul verwendet)

Information


--- Modul mit Templates zur (automatischen) Erstellung von Dokumentationen für Lua-Module. Dieses Modul verwendet [[Vorlage:LuaDokumentationsseite]] zur Erzeugung der Dokumentation. Siehe Funktion <code>[[#erzeugeDokumentation|erzeugeDokumentation]]</code>, wie dieses Modul in Wikibooks verwendet werden kann.

local LuaDokumentation = {}

local utils = require("Modul:Utils")

string.starts = utils.starts
string.ends = utils.ends

-- Templates for erzeugeDokumentation()
local baseTemplate = [[== Modulbeschreibung ==
%s%s%s]]

local funcTemplate = [[<h3>%s</h3>
%s]]

--- Lua-Funktion, um aus den Kommentaren eines Moduls automatisch eine Dokumentationsseite zu erzeugen.
--
-- Wenn "Modul: ..../Doku" als Modul angegeben wird, wird nur "Modul: ...." verwendet.
-- Module, die dieses Template benutzen, müssen als letzte Anweisung den Ausdruck <code>return modulobjekt</code> besitzen (mit genau diesen Abständen).
--
-- ==== Kommentare ====
--
-- Kommentare, die später in der Dokumentation erscheinen sollen, müssen mit <code>---</code> (3 Minusstriche) beginnen.
-- Der darauf folgende Block von Kommentaren beginnend jeweils mit <code>--</code> wird als ein Dokumentationsblock interpretiert.
-- Der jeweils erste ist der Dokumentationsblock für das gesamte Modul, die anderen für Funktionen.
-- Die Funktionsdeklaration muss dem Dokumentationsblock direkt folgen. (Bitte auf der Dokumentationsseite dieses Moduls melden, wenn eine 
-- Dunktionsdeklaration nicht erkannt wird).
--
-- Tags bezeichnen den Kommentar näher. Das sind <code>@...</code> Anweisungen. Alles vor dem ersten Tag wird als Beschreibung des Moduls,
-- der Funktion interpretiert. Wiki-Formatierungen können dabei verwendet werden.
--
-- ==== Tags ====
--
-- Folgende Tags gibt es
--
-- * <code>@example</code> – Tag zum Starten eines Code-Beispiels. Dieses wird später über [[Vorlage:Codebeispiel]] eingebunden.
-- ** <code>@code</code> ''Code'' – der Code des Code-Beispiels
-- ** <code>@description</code> ''Beschreibung'' – Beschreibungstext des Code-Beispiels
-- ** <code>@lang</code> ''Sprache'' – Sprache des Code-Beispiels (entspricht Argument <code>lang</code> von [[Vorlage:Codebeispiel]])
-- ** <code>@dont_show_result</code> – Wenn es sich beim Code um Mediawiki Text handelt, wird dieser nicht automatisch evaluiert.
-- * <code>@param</code> ''Name'' ''Beschreibung'' – Beschreibung eines Arguments einer Funktion
-- ** <code>@optional</code> – zeigt an, dass der Parameter optional ist
-- ** <code>@default_value</code> ''Standardwert'' – Standardwert des Arguments, wenn dieser nicht angegeben ist (impliziert <code>@optional</code>)
--
-- @example 
--     @description Folgender Lua-Aufruf erzeugt die Dokumentation für diese Seite
--     @code {{#invoke:LuaDokumentation|erzeugeDokumentation|modul=LuaDokumentation}}
--     @dont_show_result
-- @example 
--     @description Folgender Lua-Aufruf erzeugt die Dokumentation auf einer "Modul: ..../Doku" Unterseite
--     @code {{#invoke:LuaDokumentation|erzeugeDokumentation}}
--     @dont_show_result
--
-- @param modul Modulname
--     @optional
--     @default_value aktuelle Seite in der {{#invoke:}} aufgerufen wird
function LuaDokumentation.erzeugeDokumentation(frame)
    -- compute all examples
    local create_examples = function(examples)
        if examples ~= nil and #examples > 0 then
            local result = "\n==== Beispiele ====\n"
            
            for i, example_doc in ipairs(examples) do
                example_doc.name = "Beispiel " .. i
                example_doc.beschreibung = example_doc.description
                
                if example_doc.dont_show_result then example_doc.kein_ergebnis = "ja" end
                
                result = result .. frame:expandTemplate {
                    title = 'Codebeispiel',
                    args = example_doc
                }
            end
            
            return result
        else
            return ""
        end
    end
    
    local module_name = LuaDokumentation.getModuleName((frame.args or {})["modul"])
    local module_doc = LuaDokumentation.parseModule(module_name)
    
    if #module_doc.doc_functions > 0 then
        result_func = "\n\n<h2>Funktionen</h2>\n"
        
        for i, func_doc in ipairs(module_doc.doc_functions) do
            result_func = result_func .. string.format(funcTemplate, func_doc.name, frame:preprocess(func_doc.description))
            result_func = result_func .. create_examples(func_doc.examples)
            
            if func_doc.params then
                result_func = result_func .. "\n==== Parameter ====\n"
                
                for i, param_doc in ipairs(func_doc.params) do
                    local optional = ""
                    if param_doc.optional ~= nil and param_doc.optional then optional = "''(optional)'' " end
                    
                    local default_value = ""
                    if param_doc.default_value ~= nil then default_value = " ''(Standardwert: " .. mw.text.nowiki(param_doc.default_value) .. ")''" end
                    
                    result_func = result_func .. string.format("* <code>%s</code> %s– %s%s\n", param_doc.name, optional, param_doc.description, default_value)    
                end
            end
        end
    else
        result_func = ''
    end
    local content
    local desc
    if module_doc.description ~= nil then
        desc = frame:preprocess(module_doc.description)
    else
        desc = ""
    end
    if desc ~= "" or create_examples(module_doc.examples) ~= "" or result_func ~= "" then
        content = string.format(baseTemplate, frame:preprocess(module_doc.description), create_examples(module_doc.examples), result_func)
    else
        content = "''Es existiert keine Dokumentation. Wenn du eine anlegen willst, solltest du keine „/Doku“-Unteseite benutzen, sondern [[Modul:LuaDokumentation|LuaDokumentation]].''";
    end
    
    return content
end

--- Funktion, um den Modulnamen einer Seite (also ohne Präfix <code>Modul:</code> und ohne Suffix <code>/Doku</code>) zu extrahieren.
--
-- @example
--    @code LuaDokumentation.getModuleName("Modul:HalloWelt")
--    @result HalloWelt
--    @lang lua
-- @example
--    @code LuaDokumentation.getModuleName("Modul:LuaDokumentation/Doku")
--    @result LuaDokumentation
--    @lang lua
-- @example
--    @code LuaDokumentation.getModuleName({args = { modul = "HalloWelt/Doku" } })
--    @result HalloWelt
--    @lang lua
--
-- @param arg Seitentitel des Moduls oder einer Unterseite des Moduls als String oder das aktuelle Frame-Objekt (dann wird in args nach dem Parameter <code>modul</code> gesucht)
--     @default_value aktueller Seitentitel
function LuaDokumentation.getModuleName(arg)
    local module_name =  ((arg or {}).args or {})["modul"] or mw.title.getCurrentTitle().fullText
    
    if type(arg) == "string" and #arg > 0 then
        module_name = arg
    end
    
    module_name = string.match(module_name, "^Modul%:(.*)$") or module_name -- strip namespace token "Modul:"
    module_name = string.match(module_name, "^(.*)%/Doku$")  or module_name -- strip suffix "/Doku"
    
    return module_name
end

--- Funktion, die den Source-Code eines Lua-Modules zurückgibt
--
-- @param module_name Name des Moduls oder aktuelles Frame-Objekt (bei einem Frame-Objekt dann wird das Argument <code>modul</code> verwendet)
function LuaDokumentation.getModuleSourceCode(module_name)
    module_name = LuaDokumentation.getModuleName(module_name)
    
    local title = assert(mw.title.makeTitle("Modul", module_name), "no module with name " .. module_name)
    
    return title:getContent()
end

--- Funktion, um die Dokumentation eines Lua-Moduls zu parsen. Es wird eine <code>table</code> zurückgegeben, die folgende Struktur aufweist
--
-- <syntaxhighlight lang="lua">
-- {
--     description = "....",                              -- Modulbeschreibung
--     examples = {                                       -- alle @examples der Modulbeschreibung (nil wenn keine @examples vorhanden sind)
--         code = "...." or nil,
--         description = "..." or nil,
--         lang = "..." or nil,
--         dont_show_result = true or nil
--     },
--     functions = {                                      -- Dokumentation aller Funktionen
--         {
--             name = "...",
--             descriptions = "..."
--             examples = { ... }                         -- wie bei Modulbeschreibung
--             params = {                                 -- Liste aller @param Anweisungen (nil, wenn keine @param Anweisung vorhanden ist)
--                  {
--                      name = "...",
--                      description = "...",
--                      optional = true or nil,
--                      default_value = "...." or nil
--                  },
--                  ...
--             }
--         },
--         ...
--     }
-- }
-- </syntaxhighlight>
--
-- @param module_name Name des Moduls oder aktuelles Frame-Objekt (bei einem Frame-Objekt dann wird das Argument <code>modul</code> verwendet)
function LuaDokumentation.parseModule(module_name)
    -- variable sourc contains the modules source code
    local source = assert(LuaDokumentation.getModuleSourceCode(module_name), "no module with name " .. module_name)
   
    -- variable containing the result of this function
    local result = {
        doc_module = {},
        doc_functions = {}
    }
    
    -- name of module variable, which is returned in the last statement
    result.module_var = assert(string.match(source, "\nreturn ([%w_]+)%s*$"), 
    	'no module name found near "' .. string.match(source, '\n([^\r\n]*)%s*$') .. '"')
    
    local current_doc       -- table with information of current docstring block (`nil` iff currently no docstring is parsed)
    local current_tag       -- table with information of current tag in docstring
    local first_doc = true  -- `true`, iff current docstring is the first parsed docstring and therefore 
    local description       -- current docstring description (`nil` iff no docstring is parsed)
    local current_tag_prop  -- current tag property
    
    -- to be called, iff parsing of description shall be terminated
    local end_description = function() 
        if description ~= nil then
            current_doc.description = mw.text.trim(description)
            description = nil
        end
    end

    -- inserts a new tag to tag list `tag_list` of `current_doc` 
    local insert_tag_to = function(tag_list) 
        if current_doc[tag_list] == nil then
            current_doc[tag_list] = {}
        end
        
        current_tag = {}
        
        table.insert(current_doc[tag_list], current_tag)
    end

    -- iterate over all lines
    for line in string.gmatch(source, "[^\r\n]+") do
        line = mw.text.trim(line)
        
        if string.starts(line, "--") then
            -- line is a comment:
            
            if string.starts(line, "---") and current_doc == nil then
                -- line starts a new lua docstring block
                current_doc = {}
                description = ""
            end
            
            -- just continue, iff currently a docstring block is parsed
            if current_doc ~= nil then
                -- trim `-` characters of line string
                line = string.match(line, '^-*%s?(.*)$')
                
                if string.match(line, "^%s*@") ~= nil then 
                    -- docstring line with tags:
                    end_description()
                    current_tag_prop = nil
                    
                    -- parse tag and its parameters
                    tag = string.match(line, "^%s*@([%w_]+)%s*.*$")
                    param = string.match(line, "^%s*@[%w_]+%s*(.*)$")
                    
                    -- append information of current tag to `current_tag` and `current_doc`
                    if tag == "example" then
                        -- tag: @example
                        insert_tag_to("examples")
                    elseif tag == "description" then
                        -- tag: @description description
                        current_tag.description = param
                    elseif tag == "code" then
                        -- tag @code code
                        current_tag.code = param
                        current_tag_prop = "code"
                    elseif tag == "lang" then
                        -- tag @lang lang
                        current_tag.lang = param
                    elseif tag == "optional" then
                        -- tag @optional
                        current_tag.optional = true
                    elseif tag == "dont_show_result" then
                        -- tag @dont_show_result
                        current_tag.dont_show_result = true
                    elseif tag == "default_value" then
                        -- tag @default_value default_value
                        -- implies @optional
                        current_tag.optional = true
                        current_tag.default_value = param
                    elseif tag == "result" then
                        -- tag: @result result_description
                        current_tag.result = param
                    elseif tag == "param" then
                        -- tag: @param name param_description
                        insert_tag_to("params")
                        
                        name, des = string.match(param, "^([%w_]+)%s+(.*)$")
                        
                        current_tag.name = name
                        current_tag.description = des
                    end
                else -- no param line, maybe description line?
                    
                    -- test, if currently a description is parsed and append line to `description`
                    if description ~= nil then
                        description = description .. "\n" .. line
                    elseif current_tag_prop ~= nil then
                        current_tag[current_tag_prop] = current_tag[current_tag_prop] .. "\n" .. mw.text.trim(line)
                    end
                end
            end
        elseif current_doc ~= nil then
            -- next line of a parsed docstring block:
            
            end_description()
            current_tag = nil
            
            if first_doc then
                -- docstring block was a docstring for the module:
                for k,v in pairs(current_doc) do result[k] = v end
                current_doc = nil
                first_doc = false
            else
                -- docstring block was a docstring for a function
                -- try to parse function name:
                
                -- possible regex of function declaration
                function_regexes = {
                    'function%s+' .. result.module_var .. '%s*%.%s*([%w_]+)%(',
                    'function%s+' .. result.module_var .. '%s*%:%s*([%w_]+)%(',
                    result.module_var .. '%.([%w_]+)%s*=%s*function%s*%(',
                    result.module_var .. '%:([%w_]+)%s*=%s*function%s*%(',
                }
                
                for i, re in ipairs(function_regexes) do
                    function_name = string.match(line, re)
                    
                    if function_name ~= nil then
                        -- function name found! -> add to result.doc_functions
                        current_doc.name = function_name
                        
                        table.insert(result.doc_functions, current_doc)
                        
                        break
                    end
                end
            
                current_doc = nil
            end
        end
    end

    return result
end

return LuaDokumentation