Module:DependencyList: Difference between revisions

osrsw>Shayani
knew i would mess this up
osrsw>CephHunter
Sync with rsw
Line 2: Line 2:
local p = {}
local p = {}
local libraryUtil = require( 'libraryUtil' )
local libraryUtil = require( 'libraryUtil' )
local enum = require( 'Module:Enum' )
local arr = require( 'Module:Enum' )
local yn = require( 'Module:Yesno' )
local yn = require( 'Module:Yesno' )
local param = require( 'Module:Paramtest' )
local param = require( 'Module:Paramtest' )
Line 42: Line 42:
---@param str string
---@param str string
---@return string
---@return string
local function formatModuleName( str )
local function formatPageName( str )
     return (str:gsub( '^([\'\"])(.-)%1$', function(_, x) return x end ) -- Only remove quotes at start and end of string if both are the same type
     local name = mw.text.trim(str)
        :gsub( '^([\'\"])(.-)%1$', function(_, x) return x end ) -- Only remove quotes at start and end of string if both are the same type
         :gsub( '_', ' ' )
         :gsub( '_', ' ' )
         :gsub( '^.', string.upper )
         :gsub( '^.', string.upper )
         :gsub( ':(.)', function(x) return ':'..x:upper() end ))
         :gsub( ':.', string.upper )
 
    return name
end
 
---@param str string
---@return string
local function formatModuleName( str )
    local module = formatPageName( str )
 
    if not string.find( module, '^[Mm]odule:' ) then
        module = 'Module:' .. module
    end
 
    return module
end
 
local function dualGmatch( str, pat1, pat2 )
    local f1 = string.gmatch( str, pat1 )
    local f2 = string.gmatch( str, pat2 )
    return function()
        return f1() or f2()
    end
end
end


Line 56: Line 79:
     if query:find( '%.%.' ) then
     if query:find( '%.%.' ) then
         query = mw.text.split( query, '..', true )
         query = mw.text.split( query, '..', true )
         query = enum.map( query, function(x) return mw.text.trim(x) end )
         query = arr.map( query, function(x) return mw.text.trim(x) end )
         query = enum.map( query, function(x) return (x:match('^[\'\"](.-)[\'\"]$') or '%') end )
         query = arr.map( query, function(x) return (x:match('^[\'\"](.-)[\'\"]$') or '%') end )
         query = table.concat( query )
         query = table.concat( query )
     else
     else
Line 101: Line 124:
local function getRequireList( moduleName, searchForUsedTemplates )
local function getRequireList( moduleName, searchForUsedTemplates )
     local content = mw.title.new( moduleName ):getContent()
     local content = mw.title.new( moduleName ):getContent()
     local requireList = {}
     local requireList = arr{}
     local loadDataList = {}
     local loadDataList = arr{}
     local usedTemplateList = {}
     local usedTemplateList = arr{}
     local dynamicRequirelist = {}
     local dynamicRequirelist = arr{}
     local dynamicLoadDataList = {}
     local dynamicLoadDataList = arr{}


     assert( content ~= nil, string.format( '%s does not exist', moduleName ) )
     assert( content ~= nil, string.format( '%s does not exist', moduleName ) )


     content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
     content = content:gsub( '%-%-%[(=-)%[.-%]%1%]', '' ):gsub( '%-%-[^\n]*', '' ) -- Strip comments
    local function dualGmatch( str, pat1, pat2 )
        local f1 = string.gmatch( str, pat1 )
        local f2 = string.gmatch( str, pat2 )
        return function()
            return f1() or f2()
        end
    end


     for match in dualGmatch( content, 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
     for match in dualGmatch( content, 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
Line 129: Line 144:
         elseif match ~= '' then
         elseif match ~= '' then
             match = formatModuleName( match )
             match = formatModuleName( match )
            if match == 'LibraryUtil' then
                match = 'Module:LibraryUtil'
            end
             table.insert( requireList, match )
             table.insert( requireList, match )
         end
         end
Line 191: Line 201:
                     if name:find( ':' ) then
                     if name:find( ':' ) then
                         local ns = name:match( '^(.-):' )
                         local ns = name:match( '^(.-):' )
                         if enum.contains( {'', 'template', 'calculator', 'user'}, ns:lower() ) then
                         if arr.contains( {'', 'template', 'calculator', 'user'}, ns:lower() ) then
                             table.insert( usedTemplateList, name )
                             table.insert( usedTemplateList, name )
                         elseif ns == ns:upper() then
                         elseif ns == ns:upper() then
Line 208: Line 218:
     end
     end


     dynamicRequirelist = enum.reject( dynamicRequirelist, function(x) return (enum.contains(requireList, x) or enum.contains(loadDataList, x)) end )
     requireList = requireList .. dynamicRequirelist:reject( loadDataList )
    dynamicLoadDataList = enum.reject( dynamicLoadDataList, function(x) return (enum.contains(requireList, x) or enum.contains(loadDataList, x)) end )
     requireList = requireList:unique()
     requireList = enum.insert( requireList, dynamicRequirelist )
     loadDataList = loadDataList .. dynamicLoadDataList:reject( requireList )
    requireList = enum.unique( requireList )
     loadDataList = loadDataList:unique()
     loadDataList = enum.insert( loadDataList, dynamicLoadDataList )
     usedTemplateList = usedTemplateList:unique()
     loadDataList = enum.unique( loadDataList )
     usedTemplateList = enum.unique( usedTemplateList )
     table.sort( requireList )
     table.sort( requireList )
     table.sort( loadDataList )
     table.sort( loadDataList )
Line 232: Line 240:


     for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
     for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
        moduleName = string.format( 'Module:%s', mw.text.trim( moduleName ) )
         moduleName = formatModuleName( moduleName )
         moduleName = formatModuleName( moduleName )
         funcName = mw.text.trim( funcName )
         funcName = mw.text.trim( funcName )
        if string.find( funcName, '^{{{' ) then
        funcName = funcName ..  '}}}'
        end
         table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
         table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
     end
     end


     invokeList = enum.unique( invokeList, function(x) return x.moduleName..x.funcName end )
    -- For form calcs invoking the module directly
    for config in dualGmatch( content, '<pre%s+class%s*=%s*["\']jcConfig["\'](.-)</pre>', '{{[Ff]orm calculator%s*|(.+)}}' ) do
        local moduleName = string.match( config, 'module%s*=%s*(.-)[\n|]' )
        if param.has_content( moduleName ) then
            moduleName = formatModuleName( moduleName )
            local funcName = string.match( config, 'modulefunc%s*=%s*(.-)[\n|]' ) or 'main'
            table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
        end
    end
 
     invokeList = arr.unique( invokeList, function(x) return x.moduleName..x.funcName end )
     table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName end )
     table.sort( invokeList, function(x, y) return x.moduleName..x.funcName < y.moduleName..y.funcName end )


Line 251: Line 271:
     html:tag( 'td' )
     html:tag( 'td' )
         :attr( 'width', '40xp' )
         :attr( 'width', '40xp' )
         :wikitext( '[[File:Old boot detail.png|center|30px|link=]]' )
         :wikitext( '[[File:Iron full helm detail old.png|center|30px|link=]]' )
     :done()
     :done()
     :tag( 'td' )
     :tag( 'td' )
Line 268: Line 288:
     local text = string.format( '%d %s', #list, listType )
     local text = string.format( '%d %s', #list, listType )
     local button = tooltip._span{ name=id, alt=text }
     local button = tooltip._span{ name=id, alt=text }
     list = enum.map( list, function(x) return '\n# '..x end )
     list = arr.map( list, function(x) return '\n# '..x end )
     local content = tooltip._div{ name=id, content='\n'..table.concat( list )..'\n\n' }
     local content = tooltip._div{ name=id, content='\n'..table.concat( list )..'\n\n' }


Line 314: Line 334:
     for _, item in ipairs( invokeList ) do
     for _, item in ipairs( invokeList ) do
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' invokes function '''%s''' in [[%s]] using [[RuneScape:Lua|Lua]].</div>",
             "<div class='seealso'>'''%s''' invokes function '''%s''' in [[%s]] using [[RuneScape:Lua|Lua]].</div>",
             templateName,
             templateName,
             item.funcName,
             item.funcName,
Line 333: Line 353:
---@return string
---@return string
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
local function formatInvokedByList( moduleName, addCategories, whatLinksHere )
     local templateData = enum.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
local function lcfirst( str )
     templateData = enum.filter( templateData, function(x)
return string.gsub( str, '^[Mm]odule:.', string.lower )
         return enum.any( x.invokeList, function(y)
end
             return y.moduleName:lower() == moduleName:lower()
 
     local templateData = arr.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
     templateData = arr.filter( templateData, function(x)
         return arr.any( x.invokeList, function(y)
             return lcfirst(y.moduleName) == lcfirst(moduleName)
         end )
         end )
     end )
     end )
Line 354: Line 378:
     if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
     if #invokedByList > COLLAPSE_LIST_LENGTH_THRESHOLD then
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' is invoked by %s.</div>",
             "<div class='seealso'>'''%s''' is invoked by %s.</div>",
             moduleName,
             moduleName,
             collapseList( invokedByList, 'invokedBy', 'templates' )[1]
             collapseList( invokedByList, 'invokedBy', 'templates' )[1]
Line 361: Line 385:
         for _, item in ipairs( invokedByList ) do
         for _, item in ipairs( invokedByList ) do
             table.insert( res, string.format(
             table.insert( res, string.format(
                 "<div class=\"seealso\">'''%s's''' %s.</div>",
                 "<div class='seealso'>'''%s's''' %s.</div>",
                 moduleName,
                 moduleName,
                 item
                 item
Line 381: Line 405:
---@return string
---@return string
local function formatRequiredByList( moduleName, addCategories, whatLinksHere )
local function formatRequiredByList( moduleName, addCategories, whatLinksHere )
     local childModuleData = enum.map( whatLinksHere, function ( title )
     local childModuleData = arr.map( whatLinksHere, function ( title )
         local requireList, loadDataList = getRequireList( title )
         local requireList, loadDataList = getRequireList( title )
         return {name=title, requireList=requireList, loadDataList=loadDataList}
         return {name=title, requireList=requireList, loadDataList=loadDataList}
     end )
     end )


     local requiredByList = enum.map( childModuleData, function ( item )
     local requiredByList = arr.map( childModuleData, function ( item )
         if enum.any( item.requireList, function(x) return x:lower()==moduleName:lower() end ) then
         if arr.any( item.requireList, function(x) return x:lower()==moduleName:lower() end ) then
             if item.name:find( '%%' ) then
             if item.name:find( '%%' ) then
                 return formatDynamicQueryLink( item.name )
                 return formatDynamicQueryLink( item.name )
Line 396: Line 420:
     end )
     end )


     local loadedByList = enum.map( childModuleData, function ( item )
     local loadedByList = arr.map( childModuleData, function ( item )
         if enum.any( item.loadDataList, function(x) return x:lower()==moduleName:lower() end ) then
         if arr.any( item.loadDataList, function(x) return x:lower()==moduleName:lower() end ) then
             if item.name:find( '%%' ) then
             if item.name:find( '%%' ) then
                 return formatDynamicQueryLink( item.name )
                 return formatDynamicQueryLink( item.name )
Line 422: Line 446:
     for _, requiredByModuleName in ipairs( requiredByList ) do
     for _, requiredByModuleName in ipairs( requiredByList ) do
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' is required by %s.</div>",
             "<div class='seealso'>'''%s''' is required by %s.</div>",
             moduleName,
             moduleName,
             requiredByModuleName
             requiredByModuleName
Line 434: Line 458:
     for _, loadedByModuleName in ipairs( loadedByList ) do
     for _, loadedByModuleName in ipairs( loadedByList ) do
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' is loaded by %s.</div>",
             "<div class='seealso'>'''%s''' is loaded by %s.</div>",
             moduleName,
             moduleName,
             loadedByModuleName
             loadedByModuleName
Line 456: Line 480:
     for _, requiredModuleName in ipairs( requireList ) do
     for _, requiredModuleName in ipairs( requireList ) do
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' requires %s.</div>",
             "<div class='seealso'>'''%s''' requires %s.</div>",
             currentPageName,
             currentPageName,
             requiredModuleName
             requiredModuleName
Line 478: Line 502:
     for _, loadedModuleName in ipairs( loadDataList ) do
     for _, loadedModuleName in ipairs( loadDataList ) do
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' loads data from %s.</div>",
             "<div class='seealso'>'''%s''' loads data from %s.</div>",
             currentPageName,
             currentPageName,
             loadedModuleName
             loadedModuleName
Line 500: Line 524:
     for _, templateName in ipairs( usedTemplateList ) do
     for _, templateName in ipairs( usedTemplateList ) do
         table.insert( res, string.format(
         table.insert( res, string.format(
             "<div class=\"seealso\">'''%s''' transcludes %s using <samp>frame:preprocess()</samp>.</div>",
             "<div class='seealso'>'''%s''' transcludes %s using <samp>frame:preprocess()</samp>.</div>",
             currentPageName,
             currentPageName,
             templateName
             templateName
Line 524: Line 548:
     local title = mw.title.getCurrentTitle()
     local title = mw.title.getCurrentTitle()


     -- Leave early if not in module, template or calculator namespace or if module is part of exchange groups
     -- Leave early if not in module, template or calculator namespace or if module is part of exchange or data groups
     if param.is_empty( currentPageName ) and (
     if param.is_empty( currentPageName ) and (
         ( not enum.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
         ( not arr.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
         ( title.nsText == 'Module' and ( enum.contains( {'Exchange', 'Exchange historical'}, title.text:match( '^(.-)/' ) ) ) )
         ( title.nsText == 'Module' and ( arr.contains( {'Exchange', 'Exchange historical', 'Data'}, title.text:match( '^(.-)/' ) ) ) )
     ) then
     ) then
         return ''
         return ''
Line 534: Line 558:
     currentPageName = param.default_to( currentPageName, title.fullText )
     currentPageName = param.default_to( currentPageName, title.fullText )
     currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
     currentPageName = string.gsub( currentPageName, '/[Dd]oc$', '' )
     currentPageName = formatModuleName( currentPageName )
     currentPageName = formatPageName( currentPageName )
     addCategories = yn( param.default_to( addCategories, title.subpageText~='doc' ) )
     addCategories = yn( param.default_to( addCategories, title.subpageText~='doc' ) )
     moduleIsUsed = yn( param.default_to( isUsed, false ) )
     moduleIsUsed = yn( param.default_to( isUsed, false ) )
   
 
     if title.text:lower():find( 'sandbox' ) then
     if title.text:lower():find( 'sandbox' ) then
     moduleIsUsed = true -- Don't show sandbox modules as unused
     moduleIsUsed = true -- Don't show sandbox modules as unused
Line 558: Line 582:
         namespace = 'Module',
         namespace = 'Module',
         linksto = currentPageName,
         linksto = currentPageName,
         nottitlematch = '%/doc|Exchange/%|Exchange historical/%|' .. currentPageName:gsub( 'Module:', '' ),
         nottitlematch = '%/doc|Exchange/%|Exchange historical/%|Data/%|' .. currentPageName:gsub( 'Module:', '' ),
         distinct = 'strict',
         distinct = 'strict',
         ignorecase = true,
         ignorecase = true,
Line 568: Line 592:
     local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName, true )
     local requireList, loadDataList, usedTemplateList = getRequireList( currentPageName, true )


     requireList = enum.map( requireList, function ( moduleName )
     requireList = arr.map( requireList, function ( moduleName )
         if moduleName:find( '%%' ) then
         if moduleName:find( '%%' ) then
             return formatDynamicQueryLink( moduleName )
             return formatDynamicQueryLink( moduleName )
Line 576: Line 600:
     end )
     end )


     loadDataList = enum.map( loadDataList, function ( moduleName )
     loadDataList = arr.map( loadDataList, function ( moduleName )
         if moduleName:find( '%%' ) then
         if moduleName:find( '%%' ) then
             return formatDynamicQueryLink( moduleName )
             return formatDynamicQueryLink( moduleName )
Line 584: Line 608:
     end )
     end )


     usedTemplateList = enum.map( usedTemplateList, function( templateName )
     usedTemplateList = arr.map( usedTemplateList, function( templateName )
         if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
         if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
             return '[['..templateName..']]'
             return '[['..templateName..']]'