mirror of
https://github.com/httpjamesm/AnonymousOverflow.git
synced 2024-12-06 19:16:43 +01:00
23b8ed8899
* improvement: use lang-* classes declared in snippet markup * fix: don't require snippet lang classes in code blocks * fix: constrain greedy code block match
99 lines
2.6 KiB
Go
99 lines
2.6 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"html"
|
|
"io"
|
|
"regexp"
|
|
"strings"
|
|
|
|
html_formatter "github.com/alecthomas/chroma/formatters/html"
|
|
"github.com/alecthomas/chroma/lexers"
|
|
"github.com/alecthomas/chroma/styles"
|
|
)
|
|
|
|
// highlightSyntaxViaContent uses Chroma to lex code content and apply the appropriate tokenizer engine.
|
|
// If it can't find one, it defaults to JavaScript syntax highlighting.
|
|
func highlightSyntaxViaContent(content string, lang string) (htmlOut string) {
|
|
content = html.UnescapeString(content)
|
|
|
|
fallbackOut := html.EscapeString(content)
|
|
|
|
lexer := lexers.Get(lang)
|
|
if lexer == nil {
|
|
lexer = lexers.Analyse(content)
|
|
}
|
|
if lexer == nil {
|
|
lexer = lexers.Get(".js")
|
|
}
|
|
|
|
style := styles.Get("xcode")
|
|
if style == nil {
|
|
style = styles.Fallback
|
|
}
|
|
|
|
formatter := html_formatter.New(html_formatter.PreventSurroundingPre(true), html_formatter.WithClasses(true))
|
|
|
|
iterator, err := lexer.Tokenise(nil, content)
|
|
if err != nil {
|
|
htmlOut = fallbackOut
|
|
return
|
|
}
|
|
|
|
b := bytes.NewBufferString("")
|
|
w := io.Writer(b)
|
|
|
|
err = formatter.Format(w, style, iterator)
|
|
if err != nil {
|
|
htmlOut = fallbackOut
|
|
return
|
|
}
|
|
|
|
htmlOut = b.String()
|
|
|
|
return
|
|
}
|
|
|
|
var preClassRegex = regexp.MustCompile(`(?s)<pre class=".+">`)
|
|
|
|
// stripBlockTags takes an extracted code block from HTML and strips it of its pre and code tags.
|
|
// What's returned is just the code.
|
|
func stripBlockTags(content string) (result string) {
|
|
// strip all "<code>" tags
|
|
content = strings.Replace(content, "<code>", "", -1)
|
|
content = strings.Replace(content, "</code>", "", -1)
|
|
// and the <pre>
|
|
content = strings.Replace(content, "<pre>", "", -1)
|
|
content = strings.Replace(content, "</pre>", "", -1)
|
|
|
|
content = preClassRegex.ReplaceAllString(content, "")
|
|
|
|
result = content
|
|
|
|
return
|
|
}
|
|
|
|
var codeBlockRegex = regexp.MustCompile(`(?s)<pre(?:[^>]+?lang-(.+?)[\s"'])?.*?><code>(.*?)<\/code><\/pre>`)
|
|
|
|
// HighlightCodeBlocks uses both highlightSyntaxViaContent stripCodeBlocks and returns the newly highlighted code HTML.
|
|
func HighlightCodeBlocks(html string) string {
|
|
// Replace each code block with the highlighted version
|
|
highlightedHTML := codeBlockRegex.ReplaceAllStringFunc(html, func(codeBlock string) string {
|
|
// Extract the code content from the code block
|
|
matches := codeBlockRegex.FindStringSubmatch(codeBlock)
|
|
lang, codeContent := matches[1], matches[2]
|
|
|
|
codeContent = stripBlockTags(codeContent)
|
|
|
|
// Highlight the code content
|
|
highlightedCode := highlightSyntaxViaContent(codeContent, lang)
|
|
|
|
// Replace the original code block with the highlighted version
|
|
highlightedCodeBlock := "<pre>" + highlightedCode + "</pre>"
|
|
|
|
return highlightedCodeBlock
|
|
})
|
|
|
|
return highlightedHTML
|
|
}
|