Module:DependencyList: Difference between revisions

mNo edit summary
mNo edit summary
 
(8 intermediate revisions by the same user not shown)
Line 5: Line 5:
local yn = require( 'Module:Yesno' )
local yn = require( 'Module:Yesno' )
local param = require( 'Module:Paramtest' )
local param = require( 'Module:Paramtest' )
local dpl = require( 'Module:DPLlua' )
local tooltip = require( 'Module:Tooltip' )
local tooltip = require( 'Module:Tooltip' )
local moduleIsUsed = false
local moduleIsUsed = false
Line 12: Line 13:


--- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
--- Used in case 'require( varName )' is found. Attempts to find a string value stored in 'varName'.
---@param content string    @The content of the module to look in as a string
---@param content string    The content of the module to search in
---@param varName string
---@param varName string
---@return string
---@return string
local function substVarValue( content, varName )
local function substVarValue( content, varName )
     if varName:find( "%b''" ) or varName:find( '%b""' ) then -- Look for balanced quotes
     local res = content:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or content:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
         return varName -- Is already a string
    if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) and not res:find( '%%%a' ) then
         return mw.text.trim( res )
     else
     else
         local res = content:match( varName .. '%s*=%s*(%b""%s-%.*)' ) or content:match( varName .. "%s*=%s*(%b''%s-%.*)" ) or ''
         return ''
        if res:find( '^(["\'])[Mm]odule:[%S]+%1' ) and not res:find( '%.%.' ) then
    end
            return mw.text.trim( res )
end
        else
 
            return ''
---@param capture string
        end
---@param content string    The content of the module to search in
---@return string
local function extractModuleName( capture, content )
    capture = capture:gsub( '^%(%s*(.-)%s*%)$', '%1' )
 
    if capture:find( '^(["\']).-%1$' ) then -- Check if it is already a pure string
        return capture
    elseif capture:find( '^[%a_][%w_]*$' ) then -- Check if if is a single variable
        return substVarValue( content, capture )
     end
     end
    return capture
end
---@param str string
---@return string
local function formatModuleName( 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
        :gsub( '_', ' ' )
        :gsub( '^.', string.upper )
        :gsub( ':(.)', function(x) return ':'..x:upper() end ))
end
end


Line 31: Line 52:
--- Will return a list of pages which satisfy this pattern where 'isTheBest' can take any value.
--- Will return a list of pages which satisfy this pattern where 'isTheBest' can take any value.
---@param query string
---@param query string
---@return string[]    @Sequence of strings
---@return string[]    Sequence of strings
local function getDynamicRequireList( query )
local function getDynamicRequireList( query )
     query = mw.text.split( query, '..', true )
     if query:find( '%.%.' ) then
    query = enum.map( query, function(x) return mw.text.trim(x) end )
        query = mw.text.split( query, '..', true )
    query = enum.map( query, function(x) return (x:match('^[\'\"]([^\'\"]+)[\'\"]$') or '%') end )
        query = enum.map( query, function(x) return mw.text.trim(x) end )
    query = table.concat( query )
        query = enum.map( query, function(x) return (x:match('^[\'\"](.-)[\'\"]$') or '%') end )
        query = table.concat( query )
    else
        _, query = query:match( '(["\'])(.-)%1' )
        query = query:gsub( '%%%a', '%%' )
    end
     query = query:gsub( '^[Mm]odule:', '' )
     query = query:gsub( '^[Mm]odule:', '' )


     if query:lower():find( '^exchange/' ) then
     if query:find( '^[Ee]xchange/' ) or query:find( '^[Dd]ata/' ) then
         return { 'Module:' .. query }  -- This format will later be used by formatDynamicQueryLink()
         return { 'Module:' .. query }  -- This format will later be used by formatDynamicQueryLink()
     end
     end
Line 50: Line 76:
         namespace = 'Module',
         namespace = 'Module',
         titlematch = query,
         titlematch = query,
         nottitlematch = '%/doc|%sandbox%%|'..query..'/%',
         nottitlematch = '%/doc|'..query..'/%',
         distinct = 'strict',
         distinct = 'strict',
         ignorecase = true,
         ignorecase = true,
Line 93: Line 119:
     end
     end


     for match in dualGmatch( content, 'require%s*%(([^%)]+)', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
     for match in dualGmatch( content, 'require%s*(%b())', 'require%s*((["\'])%s*[Mm]odule:.-%2)' ) do
         match = mw.text.trim( match )
         match = mw.text.trim( match )
         match = substVarValue( content, match )
         match = extractModuleName( match, content )


         if match:find( '%.%.' ) then
         if match:find( '%.%.' ) or match:find( '%%%a' ) then
             for _, x in ipairs( getDynamicRequireList( match ) ) do
             for _, x in ipairs( getDynamicRequireList( match ) ) do
                 table.insert( dynamicRequirelist, x )
                 table.insert( dynamicRequirelist, x )
             end
             end
         elseif match ~= '' then
         elseif match ~= '' then
             match = match:gsub( '[\"\']', '' ):gsub( '_', ' ' )
             match = formatModuleName( match )


             if match == 'libraryUtil' then
             if match == 'LibraryUtil' then
                 match = 'Module:LibraryUtil'
                 match = 'Module:LibraryUtil'
             end
             end
Line 112: Line 138:
     end
     end


     for match in dualGmatch( content, 'mw%.loadData%s*%(([^%)]+)', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)' ) do
     for match in dualGmatch( content, 'mw%.loadData%s*(%b())', 'mw%.loadData%s*((["\'])%s*[Mm]odule:.-%2)' ) do
         match = mw.text.trim( match )
         match = mw.text.trim( match )
         match = substVarValue( content, match )
         match = extractModuleName( match, content )


         if match:find( '%.%.' ) then
         if match:find( '%.%.' ) or match:find( '%%%a' ) then
             for _, x in ipairs( getDynamicRequireList( match ) ) do
             for _, x in ipairs( getDynamicRequireList( match ) ) do
                 table.insert( dynamicLoadDataList, x )
                 table.insert( dynamicLoadDataList, x )
             end
             end
         elseif match ~= '' then
         elseif match ~= '' then
             match = match:gsub( '[\"\']', '' ):gsub( '_', ' ' )
             match = formatModuleName( match )
             table.insert( loadDataList, match )
             table.insert( loadDataList, match )
         end
         end
Line 171: Line 197:
                         end
                         end
                     else
                     else
                         if name == name:upper() then
                         if name:match( '^%u+$' ) or name == '!' then
                             table.insert( usedTemplateList, name ) -- Probably a magic word
                             table.insert( usedTemplateList, name ) -- Probably a magic word
                         else
                         else
Line 194: Line 220:


     return requireList, loadDataList, usedTemplateList
     return requireList, loadDataList, usedTemplateList
end
--- Makes the first letter of the input string upper case
---@param str string
---@return string
local function ucfirst( str )
    return str:gsub( '^.', function(c) return c:upper() end )
end
end


Line 213: Line 232:


     for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
     for moduleName, funcName in string.gmatch( content, '{{[{|safeubt:}]-#[Ii]nvoke:([^|]+)|([^}|]+)[^}]*}}' ) do
        moduleName = ucfirst( moduleName )
         moduleName = string.format( 'Module:%s', mw.text.trim( moduleName ) )
         moduleName = string.format( 'Module:%s', moduleName )
         moduleName = formatModuleName( moduleName )
         moduleName = moduleName:gsub( '_', ' ' )
        funcName = mw.text.trim( funcName )
         table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
         table.insert( invokeList, {moduleName=moduleName, funcName=funcName} )
     end
     end
Line 228: Line 247:
---@param addCategories boolean
---@param addCategories boolean
---@return string
---@return string
local function messageBoxUnused( pageName, addCategories )
    local html = mw.html.create( 'table' ):addClass( 'messagebox obsolete plainlinks' )
    html:tag( 'td' )
        :attr( 'width', '40xp' )
        :wikitext( '[[File:Iron full helm detail old.png|center|30px|link=]]' )
    :done()
    :tag( 'td' )
        :wikitext( "'''This module is unused.'''" )
        :tag( 'div' )
            :css{ ['font-size']='0.85em', ['line-height']='1.45em' }
            :wikitext( string.format( 'This module is neither invoked by a template nor required/loaded by another module. If this is in error, make sure to add <code>{{[[Template:Documentation|Documentation]]}}</code>/<code>{{[[Template:No documentation|No&nbsp;documentation]]}}</code> to the calling template\'s or parent\'s module documentation.', pageName ) )
            :wikitext( addCategories and '[[Category:Unused modules]]' or '' )
        :done()
    :done()
    return tostring( html )
end
local function collapseList( list, id, listType )
local function collapseList( list, id, listType )
     local text = string.format( '%d %s', #list, listType )
     local text = string.format( '%d %s', #list, listType )
Line 295: Line 296:
     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 [[GSWiki:Lua|Lua]]''.</div>",
             templateName,
             templateName,
             item.funcName,
             item.funcName,
Line 315: Line 316:
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 templateData = enum.map( whatLinksHere, function(x) return {templateName=x, invokeList=getInvokeCallList(x)} end )
     templateData = enum.filter( templateData, function(x) return enum.any( x.invokeList, function(y) return y.moduleName==moduleName end ) end )
     templateData = enum.filter( templateData, function(x)
        return enum.any( x.invokeList, function(y)
            return y.moduleName:lower() == moduleName:lower()
        end )
    end )


     local invokedByList = {}
     local invokedByList = {}
Line 364: Line 369:


     local requiredByList = enum.map( childModuleData, function ( item )
     local requiredByList = enum.map( childModuleData, function ( item )
         if enum.any( item.requireList, function(x) return x==moduleName end ) then
         if enum.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 374: Line 379:


     local loadedByList = enum.map( childModuleData, function ( item )
     local loadedByList = enum.map( childModuleData, function ( item )
         if enum.any( item.loadDataList, function(x) return x==moduleName end ) then
         if enum.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 501: Line 506:
     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 or data groups
     if param.is_empty( currentPageName ) and (
     if param.is_empty( currentPageName ) and (
    ( title.nsText ~= 'Module' and title.nsText ~= 'Template' and title.nsText ~= 'Calculator') or
        ( not enum.contains( {'Module', 'Template', 'Calculator'}, title.nsText ) ) or
    ( title.nsText == 'Module' and ( title.text:find( '^Exchange/' ) or title.text:find( '^Exchange historical/' ) or title.text:find( '^Sandbox/' ) ) ) ) then
        ( title.nsText == 'Module' and ( enum.contains( {'Exchange', 'Exchange historical', 'Data'}, title.text:match( '^(.-)/' ) ) ) )
    ) then
         return ''
         return ''
     end
     end
 
   
     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 = string.gsub( currentPageName, '_', ' ' )
     currentPageName = formatModuleName( currentPageName )
    currentPageName = ucfirst( currentPageName:gsub( ':(.)', function(c) return ':'..c:upper() end ) )
     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 ) )
Line 530: Line 536:
         namespace = 'Module',
         namespace = 'Module',
         linksto = currentPageName,
         linksto = currentPageName,
         nottitlematch = '%/doc|%sandbox%|Exchange/%|Exchange historical/%|' .. currentPageName:gsub( 'Module:', '' ),
         nottitlematch = '%/doc|Exchange/%|Exchange historical/%|Data/%|' .. currentPageName:gsub( 'Module:', '' ),
         distinct = 'strict',
         distinct = 'strict',
         ignorecase = true,
         ignorecase = true,
Line 557: Line 563:


     usedTemplateList = enum.map( usedTemplateList, function( templateName )
     usedTemplateList = enum.map( usedTemplateList, function( templateName )
         if mw.title.new( templateName ).exists then
         if string.find( templateName, ':' ) then -- Real templates are prefixed by a namespace, magic words are not
             return '[['..templateName..']]'
             return '[['..templateName..']]'
         else
         else
Line 571: Line 577:
     table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) )
     table.insert( res, formatUsedTemplatesList( currentPageName, addCategories, usedTemplateList ) )
     table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) )
     table.insert( res, formatRequiredByList( currentPageName, addCategories, whatModulesLinkHere ) )
    if not moduleIsUsed then
        table.insert( res, 1, messageBoxUnused( currentPageName:gsub( 'Module:', '' ), addCategories ) )
    end


     return table.concat( res )
     return table.concat( res )