Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:MarkdownToWikitext: Difference between revisions

From Vault Hunters Official Wiki
No edit summary
No edit summary
Line 17: Line 17:
-- =================================================================
-- =================================================================


-- Function to strip any existing HTML tags from the input to prevent
-- Function to strip any existing HTML tags from the input.
-- conflicts and potential security issues. This version is more robust.
-- This version uses a non-greedy match to be safer.
local function stripHtml(text)
local function stripHtml(text)
     -- This pattern is more specific and safer than a simple greedy match.
     -- The non-greedy pattern '<.->' matches a '<', then the fewest
    -- It looks for what looks like a valid tag name after the '<'.
     -- characters possible (.-) until it finds the next '>', preventing
     text = text:gsub('<%/?%w+[^>]*>', '')
     -- it from consuming large chunks of text if a tag is malformed.
     -- A second pass to remove HTML comments, which the above pattern won't catch.
     text = text:gsub('<.->', '')
     text = text:gsub('<!%-%-.-%-%->', '')
     return text
     return text
end
-- Removes backslashes that escape markdown punctuation.
local function handleEscapes(text)
    -- e.g., turns `\+` into `+` and `\*` into `*`.
    -- Wikitext generally doesn't require these characters to be escaped in prose.
    return text:gsub('\\([!%#%*+%-%._`%[%]()])', '%1')
end
end


-- Converts Markdown-style headers to wikitext headers.
-- Converts Markdown-style headers to wikitext headers.
-- This function is designed to work on a single line of text.
-- This function is designed to work on a single line of text.
-- e.g., ## Header 2 -> == Header 2 ==
local function convertHeaders(text)
local function convertHeaders(text)
     for i = 6, 1, -1 do
     for i = 6, 1, -1 do
Line 45: Line 50:
local function convertEmphasis(text)
local function convertEmphasis(text)
     -- Bold: **text** or __text__ -> '''text'''
     -- Bold: **text** or __text__ -> '''text'''
    -- The pattern '[^%*]+' is used to handle multiple bold sections on one line correctly.
     text = text:gsub('%*%*([^\n%*]-)%*%*', "'''%1'''")
     text = text:gsub('%*%*([^%*]+)%*%*', "'''%1'''")
     text = text:gsub('__([^\n_]-)__', "'''%1'''")
     text = text:gsub('__([^_]+)__', "'''%1'''")
     -- Italic: *text* or _text_ -> ''text''
     -- Italic: *text* or _text_ -> ''text''
     text = text:gsub('%*([^%*]+)%*', "''%1''")
     text = text:gsub('%*([^\n%*]-)%*', "''%1''")
     text = text:gsub('_([^_]+)_', "''%1''")
     text = text:gsub('_([^\n_]-)_', "''%1''")
     return text
     return text
end
end


-- Converts Markdown links and images.
-- Converts Markdown links and images.
-- e.g., [text](url) -> [url text]
-- e.g., ![alt](url) -> [[File:url|alt]]
local function convertLinksAndImages(text)
local function convertLinksAndImages(text)
     -- Images must be processed first, as they are a superset of the link syntax.
     -- Images must be processed first: ![alt](url) -> [[File:url|alt]]
    -- Pattern: !%[(.-)%]%((.-)%)
    -- Captures alt text and URL for images.
     text = text:gsub('!%[([^\n%]]*)%]%((.-)%)', '[[File:%2|%1]]')
     text = text:gsub('!%[([^\n%]]*)%]%((.-)%)', '[[File:%2|%1]]')
 
     -- Links: [text](url) -> [url text]
     -- Links
    -- Pattern: %[(.-)%]%((.-)%)
    -- Captures link text and URL.
     text = text:gsub('%[([^\n%]]+)%]%((.-)%)', '[%2 %1]')
     text = text:gsub('%[([^\n%]]+)%]%((.-)%)', '[%2 %1]')
     return text
     return text
end
end


-- Converts inline code blocks.
-- Converts inline code blocks: `code` -> <code>code</code>
-- e.g., `code` -> <code>code</code>
local function convertInlineCode(text)
local function convertInlineCode(text)
    -- Pattern: `(.-)`
    -- Lazily captures text between backticks.
     return text:gsub('`([^`\n]+)`', '<code>%1</code>')
     return text:gsub('`([^`\n]+)`', '<code>%1</code>')
end
end
Line 87: Line 81:
--
--
-- Usage in wikitext:
-- Usage in wikitext:
-- {{#invoke:Markdown|markdown|source=...}}
-- or when used with ExternalData:
-- {{#invoke:Markdown|markdown|1={{{1}}}}}
-- {{#invoke:Markdown|markdown|1={{{1}}}}}
---
---
function p.markdown(frame)
function p.markdown(frame)
    -- Get the input text. It can be passed as the first argument (e.g., from ExternalData)
    -- or from a named 'source' argument.
     local inputText = frame.args[1] or frame.args.source or ''
     local inputText = frame.args[1] or frame.args.source or ''


Line 105: Line 95:


         -- Apply transformations. The order of these operations is important.
         -- Apply transformations. The order of these operations is important.
 
         currentLine = handleEscapes(currentLine)
         -- A. Block Transformations (Headers, Lists)
         currentLine = convertHeaders(currentLine)
         currentLine = convertHeaders(currentLine)
         -- List conversion is handled directly here.
       
         -- List conversion
         currentLine = currentLine:gsub('^%s*[%*%-]%s+', '* ') -- Unordered lists
         currentLine = currentLine:gsub('^%s*[%*%-]%s+', '* ') -- Unordered lists
         currentLine = currentLine:gsub('^%s*%d+%.%s+', '# ')  -- Ordered lists
         currentLine = currentLine:gsub('^%s*%d+%.%s+', '# ')  -- Ordered lists


         -- B. Inline Transformations (Emphasis, Links, Code)
         -- Inline Transformations
         currentLine = convertEmphasis(currentLine)
         currentLine = convertEmphasis(currentLine)
         currentLine = convertLinksAndImages(currentLine)
         currentLine = convertLinksAndImages(currentLine)

Revision as of 17:37, 24 July 2025

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

-- =================================================================
-- Module:Markdown
-- Description: Converts a string of Markdown text into wikitext.
-- Author: Gemini
-- License: CC BY-SA 4.0
-- =================================================================

local p = {}

-- Helper function to trim whitespace from a string
local function trim(s)
    return s:match('^%s*(.-)%s*$')
end

-- =================================================================
-- Core Conversion Functions
-- =================================================================

-- Function to strip any existing HTML tags from the input.
-- This version uses a non-greedy match to be safer.
local function stripHtml(text)
    -- The non-greedy pattern '<.->' matches a '<', then the fewest
    -- characters possible (.-) until it finds the next '>', preventing
    -- it from consuming large chunks of text if a tag is malformed.
    text = text:gsub('<.->', '')
    return text
end

-- Removes backslashes that escape markdown punctuation.
local function handleEscapes(text)
    -- e.g., turns `\+` into `+` and `\*` into `*`.
    -- Wikitext generally doesn't require these characters to be escaped in prose.
    return text:gsub('\\([!%#%*+%-%._`%[%]()])', '%1')
end

-- Converts Markdown-style headers to wikitext headers.
-- This function is designed to work on a single line of text.
local function convertHeaders(text)
    for i = 6, 1, -1 do
        local h_md = string.rep('#', i)
        local h_wiki = string.rep('=', i)
        -- The pattern uses '^' to ensure it only matches at the start of the string (line).
        text = text:gsub('^' .. h_md .. '%s+(.-)%s*$', h_wiki .. ' %1 ' .. h_wiki)
    end
    return text
end

-- Converts bold and italic syntax.
-- Handles nested cases by doing bold first.
local function convertEmphasis(text)
    -- Bold: **text** or __text__ -> '''text'''
    text = text:gsub('%*%*([^\n%*]-)%*%*', "'''%1'''")
    text = text:gsub('__([^\n_]-)__', "'''%1'''")
    -- Italic: *text* or _text_ -> ''text''
    text = text:gsub('%*([^\n%*]-)%*', "''%1''")
    text = text:gsub('_([^\n_]-)_', "''%1''")
    return text
end

-- Converts Markdown links and images.
local function convertLinksAndImages(text)
    -- Images must be processed first: ![alt](url) -> [[File:url|alt]]
    text = text:gsub('!%[([^\n%]]*)%]%((.-)%)', '[[File:%2|%1]]')
    -- Links: [text](url) -> [url text]
    text = text:gsub('%[([^\n%]]+)%]%((.-)%)', '[%2 %1]')
    return text
end

-- Converts inline code blocks: `code` -> <code>code</code>
local function convertInlineCode(text)
    return text:gsub('`([^`\n]+)`', '<code>%1</code>')
end

-- =================================================================
-- Main Public Function
-- =================================================================

---
-- The main function to be called from wikitext.
-- It takes a string of Markdown text and converts it to wikitext.
--
-- Usage in wikitext:
-- {{#invoke:Markdown|markdown|1={{{1}}}}}
---
function p.markdown(frame)
    local inputText = frame.args[1] or frame.args.source or ''

    -- 1. First, strip any HTML-like tags from the entire block of text.
    local text = stripHtml(inputText)

    local processedLines = {}
    -- 2. Process the text line by line
    for line in text:gmatch("([^\n]*)\n?") do
        local currentLine = trim(line)

        -- Apply transformations. The order of these operations is important.
        currentLine = handleEscapes(currentLine)
        currentLine = convertHeaders(currentLine)
        
        -- List conversion
        currentLine = currentLine:gsub('^%s*[%*%-]%s+', '* ') -- Unordered lists
        currentLine = currentLine:gsub('^%s*%d+%.%s+', '# ')   -- Ordered lists

        -- Inline Transformations
        currentLine = convertEmphasis(currentLine)
        currentLine = convertLinksAndImages(currentLine)
        currentLine = convertInlineCode(currentLine)

        table.insert(processedLines, currentLine)
    end

    -- 3. Re-assemble the processed lines into a single string.
    local wikitext = table.concat(processedLines, '\n')

    -- 4. Handle paragraph breaks for proper wikitext rendering.
    -- Replace two or more consecutive newlines with just two (a single blank line).
    wikitext = wikitext:gsub('\n\n+', '\n\n')

    return wikitext
end

return p