Skip to content

Commit 8865ca4

Browse files
committed
fix: syntax highlighting issues
1 parent 659d475 commit 8865ca4

File tree

1 file changed

+65
-49
lines changed

1 file changed

+65
-49
lines changed

Sources/WebUIMarkdown/WebUIMarkdown.swift

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -309,25 +309,49 @@ public struct HtmlRenderer: MarkupWalker {
309309
let language = codeBlock.language ?? ""
310310
logger.trace("Rendering code block with language: \(language)")
311311
let (filename, codeWithoutFilename) = extractFilename(from: codeBlock.code, language: language)
312-
let codeToRender: String
313-
if enableSyntaxHighlighting {
314-
codeToRender = highlightCode(codeWithoutFilename, language: language)
315-
} else {
316-
codeToRender = codeWithoutFilename
317-
}
318-
let (linesHTML, numbersHTML) = wrapWithLineNumbers(codeToRender)
312+
313+
// Escape HTML in the code to prevent injection
314+
let escapedCode = escapeHTML(codeWithoutFilename)
315+
316+
// Count the number of lines for line numbers
317+
let lines = codeWithoutFilename.components(separatedBy: .newlines)
318+
let lineCount = lines.count
319+
320+
// Build the HTML for the code block
319321
html += "<div class=\"code-block-wrapper\" style=\"position:relative;\">"
322+
323+
// Add filename if available and enabled
320324
if showCodeFilename, let filename = filename {
321325
html += "<div class=\"code-language\">\(filename)</div>"
322326
}
327+
328+
// Add copy button if enabled
323329
if showCopyButton {
324-
html += "<button class=\"copy-button\">Copy</button>"
330+
html += "<button class=\"copy-button\" onclick=\"navigator.clipboard.writeText(this.parentElement.querySelector('code').innerText)\">Copy</button>"
325331
}
332+
326333
if showLineNumbers {
327-
html += "<pre class=\"line-numbers\" style=\"display:flex;\"><div class=\"line-numbers\" style=\"text-align:right;user-select:none;color:#888;padding-right:8px;\">\(numbersHTML)</div><code class=\"language-\(language)\" style=\"flex:1;\">\(linesHTML)</code></pre>"
334+
// Generate line numbers HTML
335+
let lineNumbersHTML = (1...lineCount).map { "<span>\($0)</span>" }.joined(separator: "\n")
336+
337+
html += "<pre class=\"line-numbers\" style=\"display:flex;\"><div class=\"line-numbers\" style=\"text-align:right;user-select:none;color:#888;padding-right:8px;\">\(lineNumbersHTML)</div>"
338+
} else {
339+
html += "<pre>"
340+
}
341+
342+
// Add the code with basic syntax highlighting
343+
html += "<code class=\"language-\(language)\" style=\"flex:1;\">"
344+
345+
// For now, we'll use a very simple approach to avoid HTML escaping issues
346+
// Just display the escaped code without syntax highlighting
347+
if enableSyntaxHighlighting {
348+
// In a future version, implement proper syntax highlighting
349+
html += escapedCode
328350
} else {
329-
html += "<pre><code class=\"language-\(language)\">\(codeToRender)</code></pre>"
351+
html += escapedCode
330352
}
353+
354+
html += "</code></pre>"
331355
html += "</div>"
332356
}
333357

@@ -463,6 +487,11 @@ public struct HtmlRenderer: MarkupWalker {
463487
.replacingOccurrences(of: "\"", with: "&quot;")
464488
.replacingOccurrences(of: "'", with: "&#39;")
465489
}
490+
491+
/// Unescapes HTML span tags used for syntax highlighting while keeping other HTML escaping intact
492+
/// - Parameter string: The string containing escaped HTML span tags
493+
/// - Returns: A string with span tags unescaped but other HTML escaping intact
494+
466495

467496
/// Extracts a filename from the first line of the code block if it matches a comment convention for the language.
468497
///
@@ -502,30 +531,38 @@ public struct HtmlRenderer: MarkupWalker {
502531
/// - code: The code string.
503532
/// - language: The language identifier.
504533
/// - Returns: The HTML string with syntax highlighting.
505-
public func highlightCode(_ code: String, language: String) -> String {
506-
// First escape the code to prevent HTML injection
507-
switch language {
508-
case "sh":
509-
return highlightShell(code)
510-
case "swift":
511-
return highlightSwift(code)
512-
case "yml", "yaml":
513-
return highlightYAML(code)
514-
default:
515-
return code
516-
}
534+
/// This function will be implemented in a future version
535+
/// to provide proper syntax highlighting for Swift code
536+
private func applySwiftHighlighting(_ code: String) -> String {
537+
// Placeholder for future implementation
538+
return code
539+
}
540+
541+
/// For future implementation of proper syntax highlighting
542+
/// Currently, this is a placeholder that will be implemented in a future version
543+
private func applySyntaxHighlighting(_ code: String, language: String) -> String {
544+
// This is a placeholder for future syntax highlighting implementation
545+
// Currently, we just return the code as-is to avoid HTML escaping issues
546+
return code
517547
}
518548

519549
/// Wraps code lines in spans and generates line numbers HTML.
520550
///
521-
/// - Parameter code: The highlighted code string (may contain HTML).
522-
/// - Returns: Tuple of (linesHTML, numbersHTML).
551+
/// - Parameter code: The code to wrap.
552+
/// - Returns: A tuple containing the wrapped code lines and the HTML for line numbers.
523553
public func wrapWithLineNumbers(_ code: String) -> (String, String) {
524554
let lines = code.components(separatedBy: .newlines)
525555
let linesHTML = lines.map { "<span>\($0)</span>" }.joined(separator: "\n")
526556
let numbersHTML = (1...lines.count).map { "<span>\($0)</span>" }.joined(separator: "\n")
527557
return (linesHTML, numbersHTML)
528558
}
559+
560+
/// This function will be implemented in a future version
561+
/// to provide proper syntax highlighting for JavaScript code
562+
private func applyJavaScriptHighlighting(_ code: String) -> String {
563+
// Placeholder for future implementation
564+
return code
565+
}
529566

530567
/// Highlights shell script code.
531568
///
@@ -554,32 +591,11 @@ public struct HtmlRenderer: MarkupWalker {
554591
///
555592
/// - Parameter code: The code string.
556593
/// - Returns: The HTML string with Swift syntax highlighting.
594+
/// Highlights Swift code
595+
/// This will be implemented properly in a future version
557596
public func highlightSwift(_ code: String) -> String {
558-
let keywords = [
559-
"let", "var", "func", "if", "else", "for", "while", "return", "struct", "class", "enum", "import", "public", "private", "internal", "extension", "protocol", "guard", "in", "do", "try", "catch", "switch", "case", "break", "continue", "default", "where", "throw", "throws", "rethrows", "defer", "init", "self", "super", "static", "subscript", "associatedtype", "typealias", "open", "final", "lazy", "weak", "unowned", "override", "mutating", "nonmutating", "convenience", "required", "optional", "nil", "true", "false", "as", "is", "inout", "operator", "precedence", "infix", "prefix", "postfix", "dynamic", "didSet", "willSet", "get", "set", "Type", "Any", "some"
560-
]
561-
let literals = ["true", "false", "nil"]
562-
let builtins = ["print", "abs", "min", "max", "map", "filter", "reduce", "append", "remove", "count", "contains"]
563-
let keywordPattern = "\\b(" + keywords.joined(separator: "|") + ")\\b"
564-
let literalPattern = "\\b(" + literals.joined(separator: "|") + ")\\b"
565-
let builtinPattern = "\\b(" + builtins.joined(separator: "|") + ")\\b"
566-
let typePattern = #"(?<![.\w])([A-Z][A-Za-z0-9_]*)\b"#
567-
let stringPattern = #"("[^"]*"|'[^']*')"#
568-
let commentPattern = #"(?m)(\/\/.*$|\/\*[\s\S]*?\*\/)"#
569-
let numberPattern = #"(?<![\w.])(\d+(\.\d+)?)(?![\w.])"#
570-
let functionPattern = #"(?<=func\s)([a-zA-Z_][a-zA-Z0-9_]*)|([a-zA-Z_][a-zA-Z0-9_]*)\s*\("#
571-
let operatorPattern = #"(\+|-|\*|/|=|==|!=|<=|>=|<|>|\|\||&&|!|\?|:|\.\.\.|\.|%)"#
572-
var html = code // Code is already escaped in highlightCode
573-
html = html.replacingOccurrences(of: commentPattern, with: "<span class=\"hl-comment\">$1</span>", options: .regularExpression)
574-
html = html.replacingOccurrences(of: stringPattern, with: "<span class=\"hl-string\">$1</span>", options: .regularExpression)
575-
html = html.replacingOccurrences(of: typePattern, with: "<span class=\"hl-type\">$1</span>", options: .regularExpression)
576-
html = html.replacingOccurrences(of: keywordPattern, with: "<span class=\"hl-keyword\">$1</span>", options: .regularExpression)
577-
html = html.replacingOccurrences(of: literalPattern, with: "<span class=\"hl-literal\">$1</span>", options: .regularExpression)
578-
html = html.replacingOccurrences(of: builtinPattern, with: "<span class=\"hl-built_in\">$1</span>", options: .regularExpression)
579-
html = html.replacingOccurrences(of: numberPattern, with: "<span class=\"hl-number\">$1</span>", options: .regularExpression)
580-
html = html.replacingOccurrences(of: functionPattern, with: "<span class=\"hl-function\">$1$2</span>", options: .regularExpression)
581-
html = html.replacingOccurrences(of: operatorPattern, with: "<span class=\"hl-operator\">$1</span>", options: .regularExpression)
582-
return html
597+
// Return the code as-is for now to avoid HTML escaping issues
598+
return code
583599
}
584600

585601
/// Highlights YAML code.

0 commit comments

Comments
 (0)