Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tags from JSDoc #363

Open
serisitas opened this issue Aug 19, 2021 · 1 comment
Open

Add tags from JSDoc #363

serisitas opened this issue Aug 19, 2021 · 1 comment

Comments

@serisitas
Copy link

serisitas commented Aug 19, 2021

Inspired by something I stumbled on - https://dev.fandom.com/wiki/Global_Lua_Modules/Docbunto#Tags

Tags that could be added:

  • @member/@property for class members
  • @variable for local variables (accepts type)
  • @note developer notes
  • @image for adding an image to a doc item
  • @example for example files
  • @file for non executable generics or datastores
  • @require for dependencies

Features to add:

  • @error line number can be specified by [number] affixed to the tag.
@serisitas
Copy link
Author

serisitas commented Aug 27, 2021

@WikiaUsers a patch to expose flag tags and improve exports in your documentation, probably broken:
diff --git a/docbunto.lua b/docbunto.lua
index 246fee0..2ec3252 100644
--- a/docbunto.lua
+++ b/docbunto.lua
@@ -3,17 +3,17 @@
 --  the form of MediaWiki markup, using `@tag`-prefixed comments embedded
 --  in the source code of a Scribunto module. The taglet parser & doclet
 --  renderer Docbunto uses are also publicly exposed to other modules.
---  
+--
 --  Docbunto code items are introduced by a block comment (`--[[]]--`), an
 --  inline comment with three hyphens (`---`), or a inline `@tag` comment.
 --  The module can use static code analysis to infer variable names, item
 --  privacy (`local` keyword), tables (`{}` constructor) and functions
 --  (`function` keyword). MediaWiki and Markdown formatting is supported.
---  
+--
 --  Items are usually rendered in the order they are defined, if they are
 --  public items, or emulated classes extending the Lua primitives. There
 --  are many customisation options available to change Docbunto behaviour.
---  
+--
 --  @module             docbunto
 --  @alias              p
 --  @require            Module:I18n
@@ -329,14 +328,14 @@ end
 --- Item export utility.
 --  @function           export_item
 --  @param              {table} documentation Package documentation data.
---  @param              {string} name Identifier name for item.
---  @param              {string} item_no Identifier name for item.
---  @param              {string} alias Export alias for item.
---  @param              {boolean} factory Whether the documentation item is a factory function.
+--  @param              {string} item_reference Identifier name for item.
+--  @param              {string} item_index Identifier name for item.
+--  @param              {string} item_alias Export alias for item.
+--  @param              {boolean} factory_item Whether the documentation item is a factory function.
 --  @local
-local function export_item(documentation, name, item_no, alias, factory)
+local function export_item(documentation, item_reference, item_index, item_alias, factory_item)
     for _, item in ipairs(documentation.items) do
-        if name == item.name then
+        if item_reference == item.name then
             item.tags['local'] = nil
             item.tags['private'] = nil
 
@@ -348,17 +347,17 @@ local function export_item(documentation, name, item_no, alias, factory)
 
             item.type = item.type:gsub('variable', 'member')
 
-            if factory then
+            if factory_item then
                 item.alias =
-                    documentation.items[item_no].tags['factory'].value ..
-                    (alias:find('^%[') and '' or (not item.tags['static'] and ':' or '.')) ..
-                    alias
+                    documentation.items[item_index].tags['factory'].value ..
+                    (item_alias:find('^%[') and '' or (not item.tags['static'] and ':' or '.')) ..
+                    item_alias
             else
 
                 item.alias =
                     ((documentation.tags['alias'] or {}).value or documentation.name) ..
-                    (alias:find('^%[') and '' or (documentation.type == 'classmod' and not item.tags['static'] and ':' or '.')) ..
-                    alias
+                    (item_alias:find('^%[') and '' or (documentation.type == 'classmod' and not item.tags['static'] and ':' or '.')) ..
+                    item_alias
             end
 
             item.hierarchy = mw.text.split((item.alias:gsub('["\']?%]', '')), '[.:%[\'""]+')
@@ -564,19 +563,28 @@ local function render_item(stream, item, options, preop)
     if preop then preop(item, options) end
     local item_name = item.alias or item.name
 
+    local item_type = item.type
+
+    for _, name in ipairs(p.tags._subtype_hierarchy) do
+        if item.tags[name] then
+            item_type = item_type .. i18n:msg('separator-dot') .. name
+        end
+    end
+    item_type = i18n:msg('parentheses', item_type)
+
     if options.strip and item.export and item.hierarchy then
         item_name = item_name:gsub('^[%w_]+[.[]?', '')
     end
 
     type_reference(item, options)
 
-    stream:wikitext(';<code id="' .. item_id .. '">' .. item_name .. '</code>' .. i18n:msg('parentheses', item.type)):newline()
+    stream:wikitext(';<code id="' .. item_id .. '">' .. item_name .. '</code>' .. item_type):newline()
 
     if (#(item.summary or '') + #item.description) ~= 0 then
-        local sep = #(item.summary or '') ~= 0 and #item.description ~= 0
+        local separator = #(item.summary or '') ~= 0 and #item.description ~= 0
             and (item.description:find('^[{:#*]+%s+') and '\n' or ' ')
             or  ''
-        local intro = (item.summary or '') .. sep .. item.description
+        local intro = (item.summary or '') .. separator .. item.description
         stream:wikitext(':' .. intro:gsub('\n([{:#*])', '\n:%1'):gsub('\n\n([^=])', '\n:%1')):newline()
     end
 end
@@ -636,7 +644,7 @@ local function render_tag(stream, name, tag, options, preop)
             if tag_el.value:find('\n[{:#*]') and (tag_el.type or (tag_el.modifiers or {})['opt']) then
                 stream:newline():wikitext(':' .. (options.ulist and '*' or ':') .. (tag_el.value:match('^[*:]+') or ''))
             end
-    
+
             if tag_el.type and (tag_el.modifiers or {})['opt'] then
                 stream:wikitext(i18n:msg{
                     key = 'parentheses',
@@ -966,7 +974,7 @@ function p.taglet(modname, options)
     documentation.tags = {}
     documentation.items = {}
     local line_no = 0
-    local item_no = 0
+    local item_index = 0
 
     -- Taglet tracking variables.
     local start_mode = true
@@ -1008,7 +1016,7 @@ function p.taglet(modname, options)
         pragma_mode = tag_name == 'pragma'
         export_mode = tag_name == 'export'
         special_tag = pragma_mode or export_mode
-        local tags, subtokens, sep
+        local tags, subtokens, separator
 
         -- Line counter.
         if t.posFirst == 1 then
@@ -1024,10 +1032,10 @@ function p.taglet(modname, options)
                 table.insert(documentation.comments, t.data)
 
                 if comment_mode and not new_tag and not doctag_mode and not comment_brace and not pretty_comment then
-                    sep = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
+                    separator = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
                         and '\n'
                         or  (#documentation.description ~= 0 and DOCBUNTO_CONCAT or '')
-                    documentation.description = documentation.description .. sep .. mw.text.trim(comment_tail)
+                    documentation.description = documentation.description .. separator .. mw.text.trim(comment_tail)
                 end
 
                 if new_tag and not special_tag then
@@ -1037,10 +1045,10 @@ function p.taglet(modname, options)
                 elseif doctag_mode and not comment_brace and not pretty_comment then
                     tags = documentation.tags
                     if p.tags[tags[#tags].name] == TAG_MULTI then
-                        sep = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
+                        separator = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
                             and '\n'
                             or  DOCBUNTO_CONCAT
-                        tags[#tags].value = tags[#tags].value .. sep .. mw.text.trim(comment_tail)
+                        tags[#tags].value = tags[#tags].value .. separator .. mw.text.trim(comment_tail)
                     elseif p.tags[tags[#tags].name] == TAG_MULTI_LINE then
                         tags[#tags].value = tags[#tags].value .. '\n' .. comment_tail
                     end
@@ -1050,49 +1058,49 @@ function p.taglet(modname, options)
             -- Documentation item detection.
             if not start_mode and (new_item or (new_tag and tokens[i - 1].type ~= 'comment')) and not special_tag then
                 table.insert(documentation.items, {})
-                item_no = item_no + 1
-                documentation.items[item_no].lineno = line_no
-                documentation.items[item_no].code = ''
-                documentation.items[item_no].comments = {}
-                documentation.items[item_no].description = ''
-                documentation.items[item_no].tags = {}
+                item_index = item_index + 1
+                documentation.items[item_index].lineno = line_no
+                documentation.items[item_index].code = ''
+                documentation.items[item_index].comments = {}
+                documentation.items[item_index].description = ''
+                documentation.items[item_index].tags = {}
             end
 
             if not start_mode and comment_mode and not new_tag and not doctag_mode and not comment_brace and not pretty_comment then
-                sep = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
+                separator = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
                     and '\n'
-                    or  (#documentation.items[item_no].description ~= 0 and DOCBUNTO_CONCAT or '')
-                documentation.items[item_no].description =
-                    documentation.items[item_no].description ..
-                    sep ..
+                    or  (#documentation.items[item_index].description ~= 0 and DOCBUNTO_CONCAT or '')
+                documentation.items[item_index].description =
+                    documentation.items[item_index].description ..
+                    separator ..
                     mw.text.trim(comment_tail)
             end
 
             if not start_mode and new_tag and not special_tag then
                 doctag_mode = true
-                table.insert(documentation.items[item_no].tags, process_tag(comment_tail))
+                table.insert(documentation.items[item_index].tags, process_tag(comment_tail))
 
             elseif not start_mode and doctag_mode and not comment_brace and not pretty_comment then
-                tags = documentation.items[item_no].tags
+                tags = documentation.items[item_index].tags
                 if p.tags[tags[#tags].name] == TAG_MULTI then
-                    sep = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
+                    separator = mw.text.trim(comment_tail):find('^[{|!}:#*=]+[%s-}]+')
                         and '\n'
                         or  DOCBUNTO_CONCAT
-                    tags[#tags].value = tags[#tags].value .. sep .. mw.text.trim(comment_tail)
+                    tags[#tags].value = tags[#tags].value .. separator .. mw.text.trim(comment_tail)
                 elseif p.tags[tags[#tags].name] == TAG_MULTI_LINE then
                     tags[#tags].value = tags[#tags].value .. '\n' .. comment_tail
                 end
             end
 
             if not start_mode and (comment_mode or doctag_mode) then
-                table.insert(documentation.items[item_no].comments, t.data)
+                table.insert(documentation.items[item_index].comments, t.data)
             end
 
             -- Export tag support.
             if export_mode then
                 factory_mode = t.posFirst ~= 1
                 if factory_mode then
-                    documentation.items[item_no].exports = true
+                    documentation.items[item_index].exports = true
                 else
                     documentation.exports = true
                 end
@@ -1100,8 +1108,8 @@ function p.taglet(modname, options)
                 subtokens = {}
                 while t and (not factory_mode or (factory_mode and t.data ~= 'end')) do
                     if factory_mode then
-                        documentation.items[item_no].code =
-                            documentation.items[item_no].code ..
+                        documentation.items[item_index].code =
+                            documentation.items[item_index].code ..
                             (t.posFirst == 1 and '\n' or '') ..
                             t.data
                     end
@@ -1114,33 +1122,33 @@ function p.taglet(modname, options)
                     end
                 end
 
-                local sep = {
-                    ['{'] = true, ['}'] = true;
-                    [','] = true, [';'] = true;
-                }
-                local increment = 0
-                for index, subtoken in ipairs(subtokens) do
-                    if
-                        subtoken.type == 'ident' and
-                        sep[subtokens[index + 1].data] and
-                        (subtokens[index - 1].data == '=' or sep[subtokens[index - 1].data])
-                    then
-                        local t2, i2, alias = subtoken, index, ''
-                        if subtokens[index - 1].data == '=' then
-                            t2, i2 = subtokens[i2 - 2], i2 - 2
+                local separator = { [','] = true, [';'] = true }
+                local brace = { ['{'] = true, ['}'] = true }
+
+                local item_reference, item_alias = '', ''
+                local sequence_index, has_key = 0, false
+                local subtoken, index, terminating_index = subtokens[2], 2, #subtokens - 1
+
+                while not brace[subtoken.data] do
+                    if subtoken.data == '=' then
+                        has_key = true
+                        item_reference, item_alias = item_alias, item_reference
+                    elseif not separator[subtoken.data] then
+                        if has_key then
+                            item_reference = item_reference .. subtoken.data
+                        else
+                            item_alias = item_alias .. subtoken.data
                         end
-                        if not sep[subtokens[index - 1].data] then
-	                        while not sep[t2.data] do
-	                            alias = t2.data .. alias
-	                            t2, i2 = subtokens[i2 - 1], i2 - 1
-	                        end
-                        end
-                        if #alias == 0 then
+                    elseif separator[subtoken.data] or index == terminating_index then
+                        if not has_key then
                             increment = increment + 1
+                            item_reference, item_alias = item_alias, item_reference
                             alias = '[' .. tostring(increment) .. ']'
                         end
-                        export_item(documentation, subtoken.data, item_no, alias, factory_mode)
+                        export_item(documentation, item_reference, item_index, item_alias, factory_mode)
+                        item_reference, item_alias, has_key = '', '', false
                     end
+                    subtoken, index = subtokens[index + 1], index + 1
                 end
 
                 if not factory_mode then
@@ -1182,15 +1190,15 @@ function p.taglet(modname, options)
             end
 
             -- Item data post-processing.
-            if item_no ~= 0 then
-                documentation.items[item_no].tags = hash_map(documentation.items[item_no].tags)
-                documentation.items[item_no].name = extract_name(documentation.items[item_no])
-                documentation.items[item_no].type = extract_type(documentation.items[item_no])
-                if #documentation.items[item_no].description ~= 0 then
-                    documentation.items[item_no].summary = match(documentation.items[item_no].description, DOCBUNTO_SUMMARY)
-                    documentation.items[item_no].description = gsub(documentation.items[item_no].description, DOCBUNTO_SUMMARY .. '%s*', '')
+            if item_index ~= 0 then
+                documentation.items[item_index].tags = hash_map(documentation.items[item_index].tags)
+                documentation.items[item_index].name = extract_name(documentation.items[item_index])
+                documentation.items[item_index].type = extract_type(documentation.items[item_index])
+                if #documentation.items[item_index].description ~= 0 then
+                    documentation.items[item_index].summary = match(documentation.items[item_index].description, DOCBUNTO_SUMMARY)
+                    documentation.items[item_index].description = gsub(documentation.items[item_index].description, DOCBUNTO_SUMMARY .. '%s*', '')
                 end
-                documentation.items[item_no].description = documentation.items[item_no].description:gsub('%s%s+', '\n\n')
+                documentation.items[item_index].description = documentation.items[item_index].description:gsub('%s%s+', '\n\n')
                 new_item_code = true
             end
 
@@ -1208,12 +1216,12 @@ function p.taglet(modname, options)
         end
 
         -- Item code concatenation.
-        if item_no ~= 0 and not doctag_mode and not comment_mode and not return_mode then
-            sep = #documentation.items[item_no].code ~= 0 and t.posFirst == 1 and '\n' or ''
-            documentation.items[item_no].code = documentation.items[item_no].code .. sep .. t.data
+        if item_index ~= 0 and not doctag_mode and not comment_mode and not return_mode then
+            separator = #documentation.items[item_index].code ~= 0 and t.posFirst == 1 and '\n' or ''
+            documentation.items[item_index].code = documentation.items[item_index].code .. separator .. t.data
             -- Code analysis on item head.
-            if new_item_code and documentation.items[item_no].code:find('\n') then
-                code_static_analysis(documentation.items[item_no])
+            if new_item_code and documentation.items[item_index].code:find('\n') then
+                code_static_analysis(documentation.items[item_index])
                 new_item_code = false
             end
         end
@@ -1385,10 +1393,10 @@ function p.doclet(data, options)
 
     -- Documentation lede.
     if not options.code and (#(data.summary or '') + #data.description) ~= 0 then
-        local sep = #data.summary ~= 0 and #data.description ~= 0
+        local separator = #data.summary ~= 0 and #data.description ~= 0
             and (data.description:find('^[{|!}:#*=]+[%s-}]+') and '\n\n' or ' ')
             or  ''
-        local intro = (data.summary or '') .. sep .. data.description
+        local intro = (data.summary or '') .. separator .. data.description
         intro = frame:preprocess(maybe_md(intro:gsub('^(' .. codepage .. ')', '<b>%1</b>')))
         documentation:wikitext(intro):newline():newline()
     end
@@ -1462,10 +1470,10 @@ function p.doclet(data, options)
         elseif item.type == 'type' then
             codedoc:wikitext('=== <code>' .. (item.alias or item.name) .. '</code> ==='):newline()
             if (#(item.summary or '') + #item.description) ~= 0 then
-                local sep = #(item.summary or '') ~= 0 and #item.description ~= 0
+                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 '') .. sep .. item.description):newline()
+                codedoc:wikitext((item.summary or '') .. separator .. item.description):newline()
             end
 
         elseif item.type == 'function' then
@@ -1482,10 +1490,9 @@ function p.doclet(data, options)
 
         elseif
             item.type == 'table' or
-            item.type ~= nil and (
-                item.type:find('^member') or
-                item.type:find('^variable')
-            )
+            item.type:find('^member') or
+            item.type:find('^variable')
+
         then
             render_item(codedoc, item, options)
             if not options.simple and item.tags['field'] then
@@ -1662,5 +1669,19 @@ p.tags._generic_tags = {
     ['variable']    = true,
     ['member']      = true
 }
+p.tags._subtype_tags = {
+    ['factory']     = true,
+    ['local']       = true,
+    ['private']     = true,
+    ['constructor'] = true,
+    ['static']      = true
+}
+p.tags._subtype_hierarchy = {
+    'private',
+    'local',
+    'static',
+    'factory',
+    'constructor'
+}
 
 return p

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant