Jump to content

Module:CodeToText: Difference between revisions

// via Wikitext Extension for VSCode
// via Wikitext Extension for VSCode
Line 13: Line 13:


local p = {}
local p = {}
-- Cache for previously extracted snippets (persists during a single page render)
local snippetCache = {}
-- Helper function to create a cache key from parameters
local function createCacheKey(pageName, hook, fromLine, toLine)
    if hook then
        return pageName .. "|hook=" .. hook
    else
        return pageName .. "|from=" .. tostring(fromLine) .. "|to=" .. tostring(toLine)
    end
end


function p.showSnippet(frame)
function p.showSnippet(frame)
Line 25: Line 37:
     local fromLine = tonumber(frame.args.from) or 1
     local fromLine = tonumber(frame.args.from) or 1
     local toLine  = tonumber(frame.args.to)  or fromLine
     local toLine  = tonumber(frame.args.to)  or fromLine
   
    -- Check cache first for previously extracted snippets
    local cacheKey = createCacheKey(pageName, hook, fromLine, toLine)
    if snippetCache[cacheKey] then
        return snippetCache[cacheKey]
    end


     local titleObj = mw.title.new(pageName)
     local titleObj = mw.title.new(pageName)
Line 36: Line 54:
     end
     end


     local lines = {}
     local result
    for line in content:gmatch("[^\r\n]+") do
        table.insert(lines, line)
    end
 
    local snippet = {}
      
      
     -- If hook is provided, use it to extract the snippet
     -- If hook is provided, use it to extract the snippet
     if hook then
     if hook then
         -- First, check if any line contains multiple instances of the hook
         -- First, check for inline hooks (hooks on the same line)
        local inlineHookFound = false
        -- This is more efficient than splitting into lines first
       
        local inlinePattern = hook .. "(.-)" .. hook
        for i, line in ipairs(lines) do
        local inlineMatch = content:match(inlinePattern)
            -- Find first occurrence of hook in the line
            local startPos = line:find(hook, 1, true)
            if startPos then
                -- Find second occurrence of hook in the same line
                local endPos = line:find(hook, startPos + #hook, true)
               
                if endPos then
                    -- We found a line with multiple hooks - extract content between them
                    inlineHookFound = true
                   
                    -- Extract the content between the hooks (excluding the hooks themselves)
                    local extractedContent = line:sub(startPos + #hook, endPos - 1)
                    table.insert(snippet, extractedContent)
                   
                    break
                end
            end
        end
          
          
         -- If we didn't find inline hooks, look for hooks on different lines
         if inlineMatch then
        if not inlineHookFound then
            -- We found inline hooks - use the content between them
            result = inlineMatch
        else
            -- Look for hooks on different lines
            local lines = {}
            local lineCount = 0
             local hookPositions = {}
             local hookPositions = {}
              
              
             -- Find all instances of the hook
             -- Find all instances of the hook and count lines
             for i, line in ipairs(lines) do
            local lineNumber = 0
             for line in content:gmatch("[^\r\n]+") do
                lineNumber = lineNumber + 1
                lineCount = lineNumber
               
                 if line:find(hook, 1, true) then
                 if line:find(hook, 1, true) then
                     table.insert(hookPositions, i)
                     table.insert(hookPositions, lineNumber)
                 end
                 end
               
                -- Store all lines for later use
                lines[lineNumber] = line
             end
             end
              
              
Line 91: Line 98:
             local endIndex = endLine - 1
             local endIndex = endLine - 1
              
              
             for i = startIndex, endIndex do
             -- Pre-allocate snippet table with known size
                 table.insert(snippet, lines[i])
            local snippetSize = math.max(0, endIndex - startIndex + 1)
            local snippet = {}
           
            if snippetSize > 0 then
                for i = 1, snippetSize do
                    snippet[i] = lines[startIndex + i - 1]
                end
                 result = table.concat(snippet, "\n")
            else
                result = ""
             end
             end
         end
         end
     else
     else
         -- Fallback to line number-based extraction
         -- Fallback to line number-based extraction
         for i = fromLine, math.min(toLine, #lines) do
        -- For line-based extraction, we can be more efficient by only processing the needed lines
             table.insert(snippet, lines[i])
        local lineNumber = 0
        local snippet = {}
        local snippetIndex = 0
       
        -- Only process lines up to toLine
         for line in content:gmatch("[^\r\n]+") do
             lineNumber = lineNumber + 1
           
            -- Skip lines before fromLine
            if lineNumber >= fromLine then
                -- Stop once we've reached toLine
                if lineNumber > toLine then
                    break
                end
               
                snippetIndex = snippetIndex + 1
                snippet[snippetIndex] = line
            end
         end
         end
       
        result = table.concat(snippet, "\n")
     end
     end
 
   
     return table.concat(snippet, "\n")
    -- Cache the result before returning
    snippetCache[cacheKey] = result
     return result
end
end


return p
return p

Revision as of 21:01, 7 April 2025

Documentation for this module may be created at Module:CodeToText/doc

-- Module:TranscludeModuleCode
-- Extracts a snippet of content from a specified page using either:
-- 1. A defined line range (from/to parameters)
-- 2. Hook markers like emojis (hook parameter)
-- 
-- The module finds content between matching instances of the same hook,
-- either on different lines or within the same line. Any emoji can be used
-- as a hook (🪝, 📌, 🔖, 🏷️, 🚩, ⭐, etc.) - just place the same emoji at
-- the beginning and end of the content you want to extract.
-- 
-- Example:
--   {{#invoke:TranscludeModuleCode|showSnippet|page=Module:Example|hook=⭐}}

local p = {}

-- Cache for previously extracted snippets (persists during a single page render)
local snippetCache = {}

-- Helper function to create a cache key from parameters
local function createCacheKey(pageName, hook, fromLine, toLine)
    if hook then
        return pageName .. "|hook=" .. hook
    else
        return pageName .. "|from=" .. tostring(fromLine) .. "|to=" .. tostring(toLine)
    end
end

function p.showSnippet(frame)
    local pageName = frame.args.page
    if not pageName then
        return "Error: 'page' parameter is required"
    end
    
    local hook = frame.args.hook
    
    -- Fallback to line numbers if hook isn't provided
    local fromLine = tonumber(frame.args.from) or 1
    local toLine   = tonumber(frame.args.to)   or fromLine
    
    -- Check cache first for previously extracted snippets
    local cacheKey = createCacheKey(pageName, hook, fromLine, toLine)
    if snippetCache[cacheKey] then
        return snippetCache[cacheKey]
    end

    local titleObj = mw.title.new(pageName)
    if not titleObj then
        return "Failed to create mw.title object for '" .. pageName .. "'"
    end

    local content = titleObj:getContent()
    if not content then
        return "No content for '" .. pageName .. "'"
    end

    local result
    
    -- If hook is provided, use it to extract the snippet
    if hook then
        -- First, check for inline hooks (hooks on the same line)
        -- This is more efficient than splitting into lines first
        local inlinePattern = hook .. "(.-)" .. hook
        local inlineMatch = content:match(inlinePattern)
        
        if inlineMatch then
            -- We found inline hooks - use the content between them
            result = inlineMatch
        else
            -- Look for hooks on different lines
            local lines = {}
            local lineCount = 0
            local hookPositions = {}
            
            -- Find all instances of the hook and count lines
            local lineNumber = 0
            for line in content:gmatch("[^\r\n]+") do
                lineNumber = lineNumber + 1
                lineCount = lineNumber
                
                if line:find(hook, 1, true) then
                    table.insert(hookPositions, lineNumber)
                end
                
                -- Store all lines for later use
                lines[lineNumber] = line
            end
            
            if #hookPositions < 2 then
                return "Error: Need at least 2 instances of hook '" .. hook .. "' in '" .. pageName .. "'"
            end
            
            -- Extract content between first two hook instances (excluding the hook lines)
            local startLine = hookPositions[1]
            local endLine = hookPositions[2]
            
            -- Always exclude the hook lines
            local startIndex = startLine + 1
            local endIndex = endLine - 1
            
            -- Pre-allocate snippet table with known size
            local snippetSize = math.max(0, endIndex - startIndex + 1)
            local snippet = {}
            
            if snippetSize > 0 then
                for i = 1, snippetSize do
                    snippet[i] = lines[startIndex + i - 1]
                end
                result = table.concat(snippet, "\n")
            else
                result = ""
            end
        end
    else
        -- Fallback to line number-based extraction
        -- For line-based extraction, we can be more efficient by only processing the needed lines
        local lineNumber = 0
        local snippet = {}
        local snippetIndex = 0
        
        -- Only process lines up to toLine
        for line in content:gmatch("[^\r\n]+") do
            lineNumber = lineNumber + 1
            
            -- Skip lines before fromLine
            if lineNumber >= fromLine then
                -- Stop once we've reached toLine
                if lineNumber > toLine then
                    break
                end
                
                snippetIndex = snippetIndex + 1
                snippet[snippetIndex] = line
            end
        end
        
        result = table.concat(snippet, "\n")
    end
    
    -- Cache the result before returning
    snippetCache[cacheKey] = result
    return result
end

return p