2024-04-03 01:48:27 +08:00
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"html/template"
"net/url"
"strconv"
"strings"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"golang.org/x/net/html"
)
type RenderCodePreviewOptions struct {
FullURL string
OwnerName string
RepoName string
CommitID string
FilePath string
LineStart , LineStop int
}
func renderCodeBlock ( ctx * RenderContext , node * html . Node ) ( urlPosStart , urlPosStop int , htm template . HTML , err error ) {
2024-11-18 13:25:42 +08:00
m := globalVars ( ) . codePreviewPattern . FindStringSubmatchIndex ( node . Data )
2024-04-03 01:48:27 +08:00
if m == nil {
return 0 , 0 , "" , nil
}
opts := RenderCodePreviewOptions {
FullURL : node . Data [ m [ 0 ] : m [ 1 ] ] ,
OwnerName : node . Data [ m [ 2 ] : m [ 3 ] ] ,
RepoName : node . Data [ m [ 4 ] : m [ 5 ] ] ,
CommitID : node . Data [ m [ 6 ] : m [ 7 ] ] ,
FilePath : node . Data [ m [ 8 ] : m [ 9 ] ] ,
}
2024-11-22 13:48:09 +08:00
if ! httplib . IsCurrentGiteaSiteURL ( ctx , opts . FullURL ) {
2024-04-03 01:48:27 +08:00
return 0 , 0 , "" , nil
}
u , err := url . Parse ( opts . FilePath )
if err != nil {
return 0 , 0 , "" , err
}
opts . FilePath = strings . TrimPrefix ( u . Path , "/" )
lineStartStr , lineStopStr , _ := strings . Cut ( node . Data [ m [ 10 ] : m [ 11 ] ] , "-" )
lineStart , _ := strconv . Atoi ( strings . TrimPrefix ( lineStartStr , "L" ) )
lineStop , _ := strconv . Atoi ( strings . TrimPrefix ( lineStopStr , "L" ) )
opts . LineStart , opts . LineStop = lineStart , lineStop
2024-11-24 16:18:57 +08:00
h , err := DefaultRenderHelperFuncs . RenderRepoFileCodePreview ( ctx , opts )
2024-04-03 01:48:27 +08:00
return m [ 0 ] , m [ 1 ] , h , err
}
func codePreviewPatternProcessor ( ctx * RenderContext , node * html . Node ) {
2024-05-04 09:48:16 +08:00
nodeStop := node . NextSibling
for node != nodeStop {
2024-04-03 01:48:27 +08:00
if node . Type != html . TextNode {
node = node . NextSibling
continue
}
2024-11-18 13:25:42 +08:00
urlPosStart , urlPosEnd , renderedCodeBlock , err := renderCodeBlock ( ctx , node )
if err != nil || renderedCodeBlock == "" {
2024-04-03 01:48:27 +08:00
if err != nil {
log . Error ( "Unable to render code preview: %v" , err )
}
node = node . NextSibling
continue
}
next := node . NextSibling
textBefore := node . Data [ : urlPosStart ]
textAfter := node . Data [ urlPosEnd : ]
// "textBefore" could be empty if there is only a URL in the text node, then an empty node (p, or li) will be left here.
// However, the empty node can't be simply removed, because:
// 1. the following processors will still try to access it (need to double-check undefined behaviors)
// 2. the new node is inserted as "<p>{TextBefore}<div NewNode/>{TextAfter}</p>" (the parent could also be "li")
// then it is resolved as: "<p>{TextBefore}</p><div NewNode/><p>{TextAfter}</p>",
// so unless it could correctly replace the parent "p/li" node, it is very difficult to eliminate the "TextBefore" empty node.
node . Data = textBefore
2024-11-18 13:25:42 +08:00
renderedCodeNode := & html . Node { Type : html . RawNode , Data : string ( ctx . RenderInternal . ProtectSafeAttrs ( renderedCodeBlock ) ) }
node . Parent . InsertBefore ( renderedCodeNode , next )
2024-04-03 01:48:27 +08:00
if textAfter != "" {
node . Parent . InsertBefore ( & html . Node { Type : html . TextNode , Data : textAfter } , next )
}
node = next
}
}