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
mNo edit summary
mNo edit summary
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
-- =================================================================
-- Module:Markdown
-- Description: Converts a string of Markdown text into wikitext.
-- Author: Gemini
-- License: CC BY-SA 4.0
-- =================================================================
local p = {}
local p = {}


-- Utility: unescape common HTML entities
-- Helper function to trim whitespace from a string
local function htmlUnescape(s)
local function trim(s)
s = s:gsub("\\u003C", "<")
    return s:match('^%s*(.-)%s*$')
s = s:gsub("\\u003E", ">")
end
s = s:gsub("&lt;", "<")
 
s = s:gsub("&gt;", ">")
-- =================================================================
s = s:gsub("&amp;", "&")
-- Core Conversion Functions
s = s:gsub("&quot;", '"')
-- =================================================================
s = s:gsub("&#39;", "'")
 
return s
-- 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
 
local function decodeUnicode(text)
return text:gsub("\\u(%x%x%x%x)", function(hex)
local code = tonumber(hex, 16)
if code and code < 256 then
return string.char(code)
else
return "" -- skip or replace with placeholder if outside ASCII range
end
end)
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


-- Utility: strip inline HTML tags (like iframe, br, strong, etc)
-- Converts Markdown-style headers to wikitext headers.
local function stripHTML(s)
-- This function is designed to work on a single line of text.
-- Remove <iframe ...>...</iframe>
local function convertHeaders(text)
s = s:gsub("<iframe.-</iframe>", "")
    for i = 6, 1, -1 do
-- Remove all remaining HTML tags
        local h_md = string.rep('#', i)
s = s:gsub("<.->", "")
        local h_wiki = string.rep('=', i)
return s
        -- 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
end


-- Main converter
-- Converts bold and italic syntax.
function p.convertMarkdownToWikitext(markdown)
-- Handles nested cases by doing bold first.
if not markdown then return "(No content)" end
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


-- Decode escapes
-- Converts Markdown links and images.
markdown = htmlUnescape(markdown)
local function convertLinksAndImages(text)
markdown = markdown:gsub("\\n", "\n")     -- convert escaped newlines
    -- Images must be processed first: ![alt](url) -> [[File:url|alt]]
markdown = markdown:gsub("\\%-", "–")    -- hyphen escape
    text = text:gsub('!%[([^\n%]]*)%]%((.-)%)', '[[File:%2|%1]]')
markdown = markdown:gsub("\\!", "!")     -- unescape exclamation
     -- Links: [text](url) -> [url text]
markdown = markdown:gsub("\\_", "_")      -- keep underscores
    text = text:gsub('%[([^\n%]]+)%]%((.-)%)', '[%2 %1]')
markdown = markdown:gsub("\\+", "+")     -- unescape pluses
    return text
markdown = markdown:gsub("\\*", "*")     -- unescape asterisks
end
markdown = markdown:gsub("\\\\", "\\")    -- backslashes


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


-- Headings
-- =================================================================
markdown = markdown:gsub("\n###### (.-)\n", "\n====== %1 ======\n")
-- Main Public Function
markdown = markdown:gsub("\n##### (.-)\n", "\n===== %1 =====\n")
-- =================================================================
markdown = markdown:gsub("\n#### (.-)\n", "\n==== %1 ====\n")
markdown = markdown:gsub("\n### (.-)\n", "\n=== %1 ===\n")
markdown = markdown:gsub("\n## (.-)\n", "\n== %1 ==\n")
markdown = markdown:gsub("\n# (.-)\n", "\n= %1 =\n")


-- Bold/Italic
---
markdown = markdown:gsub("%*%*%*(.-)%*%*%*", "'''''%1'''''")
-- The main function to be called from wikitext.
markdown = markdown:gsub("%*%*(.-)%*%*", "'''%1'''")
-- It takes a string of Markdown text and converts it to wikitext.
markdown = markdown:gsub("%*(.-)%*", "''%1''")
--
-- Usage in wikitext:
-- {{#invoke:Markdown|markdown|1={{{1}}}}}
---
function p.markdown(frame)
    local inputText = frame.args[1] or frame.args.source or ''
   
    -- New 1 - Convert unicode back to tags.
    local decoded = decodeUnicode(inputText)


-- Nested list conversion: convert '- ' with leading spaces into nested '*'s
    -- 1. First, strip any HTML-like tags from the entire block of text.
     markdown = markdown:gsub('\n([ \t]*)%- ', function(indent)
     local text = stripHtml(decoded)
local level = math.floor(#indent / 2) + 1 -- every 2 spaces = one level
return '\n' .. string.rep('*', level) .. ' '
    end)


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


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


-- Clean up excess line breaks
        -- Inline Transformations
markdown = markdown:gsub("\r", "")
        currentLine = convertEmphasis(currentLine)
markdown = markdown:gsub("\n\n\n+", "\n\n")
        currentLine = convertLinksAndImages(currentLine)
        currentLine = convertInlineCode(currentLine)


return markdown
        table.insert(processedLines, currentLine)
end
    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')


-- Pull markdown from API via ExternalData
    return wikitext
function p.renderFromAPI(frame)
local data = mw.ext.externalData.getData{
url = 'https://api.vaulthunters.gg/patch-notes?limit=1',
data = { markdown = 'text' }
}
local markdown = data[1] and data[1].markdown or '(No data)'
return p.convertMarkdownToWikitext(markdown)
end
end


return p
return p

Latest revision as of 17:57, 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

local function decodeUnicode(text)
	return text:gsub("\\u(%x%x%x%x)", function(hex)
	local code = tonumber(hex, 16)
	if code and code < 256 then
		return string.char(code)
	else
		return "" -- skip or replace with placeholder if outside ASCII range
	end
	end)
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 ''
    
    -- New 1 - Convert unicode back to tags.
    local decoded = decodeUnicode(inputText)

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

    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