mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-05 00:09:33 +09:00
refactor(markdown): remove goldmark-highlighting (#39751)
Render fenced code blocks with Gitea's own code block renderer instead of `goldmark-highlighting`, using the managed lexer lookup from `modules/highlight`. Indented and fenced blocks now share one rendering path. Rendered output is unchanged. The trailing newline is stripped on copy in the frontend since https://github.com/go-gitea/gitea/pull/39755. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
1 parent
12b20c7c99
commit
5c723ee0fd
11 files changed
+49
-85
No files matched your search
Generated
-5
@@ -964,11 +964,6 @@
|
||||
"path": "github.com/yohcop/openid-go/LICENSE",
|
||||
"licenseText": "Copyright 2015 Yohann Coppel\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/yuin/goldmark-highlighting/v2",
|
||||
"path": "github.com/yuin/goldmark-highlighting/v2/LICENSE",
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2019 Yusuke Inuzuka\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/yuin/goldmark",
|
||||
"path": "github.com/yuin/goldmark/LICENSE",
|
||||
|
||||
@@ -84,7 +84,6 @@ require (
|
||||
github.com/wneessen/go-mail v0.8.1
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.8.6
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.39.1
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6.0.20261013184558-04dd63131525
|
||||
golang.org/x/crypto v0.58.0
|
||||
|
||||
@@ -48,10 +48,8 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0 h1:LvK7+C6qgz8BPnmn7x
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0/go.mod h1:I28hc9eaiqKCoOB+9Wh/P1IOScfm0xCgyWC6DAo0lmo=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
|
||||
@@ -162,8 +160,6 @@ github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454Wv
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2/v2 v2.11.0 h1:z5uRPHav+eTVpoffDSuUP2KPS4f1ONZaPDK73S8HA0w=
|
||||
@@ -528,7 +524,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
@@ -559,11 +554,8 @@ github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3j
|
||||
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.8.6 h1:d0VcaP1sx9GkFVkoW+KtggpGi2KZ965i14b0+bDQST4=
|
||||
github.com/yuin/goldmark v1.8.6/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
)
|
||||
|
||||
@@ -82,6 +83,13 @@ func htmlEscape(code string) template.HTML {
|
||||
return template.HTML(gohtml.EscapeString(code))
|
||||
}
|
||||
|
||||
func normalizeLexer(lexer chroma.Lexer) chroma.Lexer {
|
||||
if lexer != lexers.Fallback {
|
||||
lexer = chroma.Coalesce(lexer)
|
||||
}
|
||||
return lexer
|
||||
}
|
||||
|
||||
// RenderCodeSlowGuess tries to get a lexer by file name and language first,
|
||||
// if not found, it will try to guess the lexer by code content, which is slow (more than several hundreds of milliseconds).
|
||||
func RenderCodeSlowGuess(fileName, language, code string) (output template.HTML, lexer chroma.Lexer, lexerDisplayName string) {
|
||||
@@ -96,11 +104,15 @@ func RenderCodeSlowGuess(fileName, language, code string) (output template.HTML,
|
||||
}
|
||||
|
||||
lexer = detectChromaLexerWithAnalyze(fileName, language, util.UnsafeStringToBytes(code)) // it is also slow
|
||||
lexer = normalizeLexer(lexer)
|
||||
return RenderCodeByLexer(lexer, code), lexer, formatLexerName(lexer.Config().Name)
|
||||
}
|
||||
|
||||
// RenderCodeByLexer returns a HTML version of code string with chroma syntax highlighting classes
|
||||
// RenderCodeByLexer returns an HTML version of code string with chroma syntax highlighting classes
|
||||
func RenderCodeByLexer(lexer chroma.Lexer, code string) template.HTML {
|
||||
if lexer == lexers.Fallback {
|
||||
return htmlEscape(code) // chroma is 3x slower for plain text
|
||||
}
|
||||
formatter := chromahtml.New(chromahtml.WithClasses(true),
|
||||
chromahtml.WithLineNumbers(false),
|
||||
chromahtml.PreventSurroundingPre(true),
|
||||
@@ -128,6 +140,7 @@ func RenderFullFile(fileName, language string, code []byte) ([]template.HTML, st
|
||||
return renderPlainText(code), formatLexerName(LanguagePlaintext)
|
||||
}
|
||||
lexer := detectChromaLexerWithAnalyze(fileName, language, code)
|
||||
lexer = normalizeLexer(lexer)
|
||||
lexerName := formatLexerName(lexer.Config().Name)
|
||||
rendered := RenderCodeByLexer(lexer, util.UnsafeBytesToString(code))
|
||||
unsafeLines := UnsafeSplitHighlightedLines(rendered)
|
||||
|
||||
@@ -215,7 +215,7 @@ func normalizeFileNameLang(fileName, fileLang string) (string, string) {
|
||||
|
||||
func DetectChromaLexerByFileName(fileName, fileLang string) chroma.Lexer {
|
||||
lexer, _ := detectChromaLexerByFileName(fileName, fileLang)
|
||||
return lexer
|
||||
return normalizeLexer(lexer)
|
||||
}
|
||||
|
||||
func detectChromaLexerByFileName(fileName, fileLang string) (_ chroma.Lexer, byLang bool) {
|
||||
@@ -289,14 +289,14 @@ func detectChromaLexerWithAnalyze(fileName, lang string, code []byte) chroma.Lex
|
||||
|
||||
// if lang is provided, and it matches a lexer, use it directly
|
||||
if byLang {
|
||||
return chroma.Coalesce(lexer)
|
||||
return lexer
|
||||
}
|
||||
|
||||
// if a lexer is detected and there is no conflict for the file extension, use it directly
|
||||
fileExt := path.Ext(fileName)
|
||||
_, hasConflicts := chromaLexers().conflictingExtLangMap[fileExt]
|
||||
if !hasConflicts && lexer != lexers.Fallback {
|
||||
return chroma.Coalesce(lexer)
|
||||
return lexer
|
||||
}
|
||||
|
||||
// try to detect language by content, for best guessing for the language
|
||||
@@ -308,5 +308,5 @@ func detectChromaLexerWithAnalyze(fileName, lang string, code []byte) chroma.Lex
|
||||
log.Warn("No chroma lexer found for enry detected language: %s (file: %s), need to fix the language mapping between enry and chroma.", analyzedLanguage, fileName)
|
||||
}
|
||||
}
|
||||
return chroma.Coalesce(lexer)
|
||||
return lexer
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
|
||||
return template.HTML(reAttrClass().ReplaceAllString(string(content), `$1 data-attr-class="`+r.secureIDPrefix+`$2"$3`))
|
||||
}
|
||||
|
||||
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt template.HTML, a ...any) error {
|
||||
func (r *RenderInternal) FormatWithSafeAttrsf(w io.Writer, fmt template.HTML, a ...any) error {
|
||||
htmlStr := r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))
|
||||
_, err := io.WriteString(w, string(htmlStr))
|
||||
return err
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/highlight"
|
||||
"gitea.dev/modules/htmlutil"
|
||||
"gitea.dev/modules/markup"
|
||||
"gitea.dev/modules/markup/internal"
|
||||
"gitea.dev/modules/markup/markdown/math"
|
||||
@@ -145,23 +144,29 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(KindSummary, r.renderSummary)
|
||||
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
|
||||
reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
|
||||
reg.Register(ast.KindFencedCodeBlock, r.renderCodeBlock)
|
||||
reg.Register(KindAttention, r.renderAttention)
|
||||
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
|
||||
reg.Register(KindRawHTML, r.renderRawHTML)
|
||||
}
|
||||
|
||||
// renderCodeBlock wraps indented code blocks like the fenced renderer
|
||||
func (r *HTMLRenderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if entering {
|
||||
preAttrs, codeAttrs := highlight.CodeBlockAttributes("") // no language
|
||||
opening := r.renderInternal.ProtectSafeAttrs(htmlutil.HTMLFormat(`<div class="code-block-container code-overflow-scroll"><pre %s><code %s>`, preAttrs, codeAttrs))
|
||||
if _, err := w.WriteString(string(opening)); err != nil {
|
||||
var language string
|
||||
if fenced, ok := n.(*ast.FencedCodeBlock); ok {
|
||||
language = string(fenced.Language(source))
|
||||
}
|
||||
preAttrs, codeAttrs := highlight.CodeBlockAttributes(language)
|
||||
err := r.renderInternal.FormatWithSafeAttrsf(w, `<div class="code-block-container code-overflow-scroll"><pre %s><code %s>`, preAttrs, codeAttrs)
|
||||
if err != nil {
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
lines := n.Lines()
|
||||
for i := 0; i < lines.Len(); i++ {
|
||||
line := lines.At(i)
|
||||
r.Writer.RawWrite(w, line.Value(source))
|
||||
|
||||
code := n.Lines().Value(source)
|
||||
lexer := highlight.DetectChromaLexerByFileName("", language)
|
||||
renderedHTML := highlight.RenderCodeByLexer(lexer, string(code))
|
||||
if _, err = w.WriteString(string(renderedHTML)); err != nil {
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
} else {
|
||||
if _, err := w.WriteString("</code></pre></div>"); err != nil {
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/highlight"
|
||||
"gitea.dev/modules/htmlutil"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/markup"
|
||||
@@ -22,9 +21,7 @@ import (
|
||||
"gitea.dev/modules/setting"
|
||||
giteautil "gitea.dev/modules/util"
|
||||
|
||||
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/yuin/goldmark"
|
||||
highlighting "github.com/yuin/goldmark-highlighting/v2"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
@@ -68,32 +65,6 @@ func newParserContext(ctx *markup.RenderContext) parser.Context {
|
||||
return pc
|
||||
}
|
||||
|
||||
type GoldmarkRender struct {
|
||||
ctx *markup.RenderContext
|
||||
|
||||
goldmarkMarkdown goldmark.Markdown
|
||||
}
|
||||
|
||||
func (r *GoldmarkRender) Convert(source []byte, writer io.Writer, opts ...parser.ParseOption) error {
|
||||
return r.goldmarkMarkdown.Convert(source, writer, opts...)
|
||||
}
|
||||
|
||||
func (r *GoldmarkRender) highlightingRenderer(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) {
|
||||
if entering {
|
||||
languageBytes, _ := c.Language()
|
||||
preAttrs, codeAttrs := highlight.CodeBlockAttributes(string(languageBytes))
|
||||
err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<div class="code-block-container code-overflow-scroll"><pre %s><code %s>`, preAttrs, codeAttrs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err := w.WriteString("</code></pre></div>")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type goldmarkEmphasisParser struct {
|
||||
parser.InlineParser
|
||||
}
|
||||
@@ -130,11 +101,10 @@ func goldmarkDefaultParser() parser.Parser {
|
||||
}
|
||||
|
||||
// SpecializedMarkdown sets up the Gitea specific markdown extensions
|
||||
func SpecializedMarkdown(ctx *markup.RenderContext) *GoldmarkRender {
|
||||
func SpecializedMarkdown(ctx *markup.RenderContext) goldmark.Markdown {
|
||||
// TODO: it could use a pool to cache the renderers to reuse them with different contexts
|
||||
// at the moment it is fast enough (see the benchmarks)
|
||||
r := &GoldmarkRender{ctx: ctx}
|
||||
r.goldmarkMarkdown = goldmark.New(
|
||||
return goldmark.New(
|
||||
goldmark.WithParser(goldmarkDefaultParser()),
|
||||
goldmark.WithExtensions(
|
||||
extension.NewTable(extension.WithTableCellAlignMethod(extension.TableCellAlignAttribute)),
|
||||
@@ -142,13 +112,6 @@ func SpecializedMarkdown(ctx *markup.RenderContext) *GoldmarkRender {
|
||||
extension.TaskList,
|
||||
extension.DefinitionList,
|
||||
common.FootnoteExtension,
|
||||
highlighting.NewHighlighting(
|
||||
highlighting.WithFormatOptions(
|
||||
chromahtml.WithClasses(true),
|
||||
chromahtml.PreventSurroundingPre(true),
|
||||
),
|
||||
highlighting.WithWrapperRenderer(r.highlightingRenderer),
|
||||
),
|
||||
math.NewExtension(&ctx.RenderInternal, math.Options{
|
||||
Enabled: setting.Markdown.EnableMath,
|
||||
ParseInlineDollar: setting.Markdown.MathCodeBlockOptions.ParseInlineDollar,
|
||||
@@ -161,15 +124,11 @@ func SpecializedMarkdown(ctx *markup.RenderContext) *GoldmarkRender {
|
||||
parser.WithAttribute(),
|
||||
parser.WithASTTransformers(util.Prioritized(NewASTTransformer(&ctx.RenderInternal), 10000)),
|
||||
),
|
||||
goldmark.WithRendererOptions(html.WithUnsafe()),
|
||||
goldmark.WithRendererOptions(
|
||||
html.WithUnsafe(),
|
||||
renderer.WithNodeRenderers(util.Prioritized(NewHTMLRenderer(&ctx.RenderInternal), 10)),
|
||||
),
|
||||
)
|
||||
|
||||
// Override the original Tasklist renderer!
|
||||
r.goldmarkMarkdown.Renderer().AddOptions(
|
||||
renderer.WithNodeRenderers(util.Prioritized(NewHTMLRenderer(&ctx.RenderInternal), 10)),
|
||||
)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// render calls goldmark render to convert Markdown to HTML
|
||||
@@ -275,14 +234,18 @@ func FeedExcerpt(ctx context.Context, content string) string {
|
||||
pc := newParserContext(rctx)
|
||||
pc.Set(renderConfigKey, &RenderConfig{})
|
||||
start, texts := len(content), []text.Segment(nil)
|
||||
_ = ast.Walk(SpecializedMarkdown(rctx).goldmarkMarkdown.Parser().Parse(text.NewReader([]byte(content)), parser.WithContext(pc)), func(node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
|
||||
mdParser := SpecializedMarkdown(rctx).Parser()
|
||||
mdReader := text.NewReader([]byte(content))
|
||||
mdWalker := func(node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if entering && node.Type() == ast.TypeBlock && node.Lines().Len() > 0 {
|
||||
start = min(start, strings.LastIndexByte(content[:node.Lines().At(0).Start], '\n')+1)
|
||||
} else if textNode, ok := node.(*ast.Text); ok && entering && node.Parent().Type() == ast.TypeBlock {
|
||||
texts = append(texts, textNode.Segment)
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
}
|
||||
_ = ast.Walk(mdParser.Parse(mdReader, parser.WithContext(pc)), mdWalker)
|
||||
|
||||
excerpt := giteautil.EllipsisDisplayString(content[start:], 200)
|
||||
kept, ok := strings.CutSuffix(excerpt, "…")
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
// Block render output:
|
||||
// <pre class="code-block is-loading"><code class="language-math">...</code></pre>
|
||||
//
|
||||
// Keep in mind that there is another "code block" render in "func (r *GoldmarkRender) highlightingRenderer"
|
||||
// "highlightingRenderer" outputs the math block with extra "chroma" class:
|
||||
// Keep in mind that there is another "code block" render in "func (r *HTMLRenderer) renderCodeBlock"
|
||||
// "renderCodeBlock" outputs the math block with extra "chroma" class:
|
||||
// <pre class="code-block is-loading"><code class="chroma language-math">...</code></pre>
|
||||
//
|
||||
// Special classes:
|
||||
|
||||
@@ -39,7 +39,7 @@ func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Nod
|
||||
r.Writer.RawWrite(w, value)
|
||||
}
|
||||
case *ColorPreview:
|
||||
_ = r.renderInternal.FormatWithSafeAttrs(w, `<span class="color-preview" style="background-color: %s"></span>`, v.Color)
|
||||
_ = r.renderInternal.FormatWithSafeAttrsf(w, `<span class="color-preview" style="background-color: %s"></span>`, v.Color)
|
||||
}
|
||||
}
|
||||
return ast.WalkSkipChildren, nil
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"gitea.dev/modules/markup"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/niklasfasching/go-org/org"
|
||||
)
|
||||
|
||||
@@ -58,10 +57,8 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
|
||||
|
||||
preAttrs, codeAttrs := highlight.CodeBlockAttributes(lang)
|
||||
lexer := highlight.DetectChromaLexerByFileName("", lang) // don't use content to detect, it is too slow
|
||||
lexer = chroma.Coalesce(lexer)
|
||||
|
||||
sb := &strings.Builder{}
|
||||
_ = ctx.RenderInternal.FormatWithSafeAttrs(sb, `<pre %s><code %s>`, preAttrs, codeAttrs)
|
||||
_ = ctx.RenderInternal.FormatWithSafeAttrsf(sb, `<pre %s><code %s>`, preAttrs, codeAttrs)
|
||||
_, _ = sb.WriteString(string(highlight.RenderCodeByLexer(lexer, source)))
|
||||
_, _ = sb.WriteString("</code></pre>")
|
||||
return sb.String()
|
||||
|
||||
Reference in new issue
Block a user