2024-04-02 20:48:27 +03:00
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"bufio"
"context"
"fmt"
"html/template"
"strings"
"code.gitea.io/gitea/models/perm/access"
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/repository/files"
)
func renderRepoFileCodePreview ( ctx context . Context , opts markup . RenderCodePreviewOptions ) ( template . HTML , error ) {
opts . LineStop = max ( opts . LineStop , opts . LineStart )
lineCount := opts . LineStop - opts . LineStart + 1
if lineCount <= 0 || lineCount > 140 /* GitHub at most show 140 lines */ {
lineCount = 10
opts . LineStop = opts . LineStart + lineCount
}
dbRepo , err := repo . GetRepositoryByOwnerAndName ( ctx , opts . OwnerName , opts . RepoName )
if err != nil {
return "" , err
}
webCtx , ok := ctx . Value ( gitea_context . WebContextKey ) . ( * gitea_context . Context )
if ! ok {
return "" , fmt . Errorf ( "context is not a web context" )
}
doer := webCtx . Doer
perms , err := access . GetUserRepoPermission ( ctx , dbRepo , doer )
if err != nil {
return "" , err
}
if ! perms . CanRead ( unit . TypeCode ) {
return "" , fmt . Errorf ( "no permission" )
}
gitRepo , err := gitrepo . OpenRepository ( ctx , dbRepo )
if err != nil {
return "" , err
}
defer gitRepo . Close ( )
commit , err := gitRepo . GetCommit ( opts . CommitID )
if err != nil {
return "" , err
}
language , _ := files . TryGetContentLanguage ( gitRepo , opts . CommitID , opts . FilePath )
blob , err := commit . GetBlobByPath ( opts . FilePath )
if err != nil {
return "" , err
}
if blob . Size ( ) > setting . UI . MaxDisplayFileSize {
return "" , fmt . Errorf ( "file is too large" )
}
dataRc , err := blob . DataAsync ( )
if err != nil {
return "" , err
}
defer dataRc . Close ( )
reader := bufio . NewReader ( dataRc )
for i := 1 ; i < opts . LineStart ; i ++ {
if _ , err = reader . ReadBytes ( '\n' ) ; err != nil {
return "" , err
}
}
lineNums := make ( [ ] int , 0 , lineCount )
lineCodes := make ( [ ] string , 0 , lineCount )
for i := opts . LineStart ; i <= opts . LineStop ; i ++ {
2024-04-22 14:48:42 +03:00
line , err := reader . ReadString ( '\n' )
if err != nil && line == "" {
2024-04-02 20:48:27 +03:00
break
}
2024-04-22 14:48:42 +03:00
lineNums = append ( lineNums , i )
lineCodes = append ( lineCodes , line )
2024-04-02 20:48:27 +03:00
}
realLineStop := max ( opts . LineStart , opts . LineStart + len ( lineNums ) - 1 )
highlightLines := code . HighlightSearchResultCode ( opts . FilePath , language , lineNums , strings . Join ( lineCodes , "" ) )
escapeStatus := & charset . EscapeStatus { }
lineEscapeStatus := make ( [ ] * charset . EscapeStatus , len ( highlightLines ) )
for i , hl := range highlightLines {
lineEscapeStatus [ i ] , hl . FormattedContent = charset . EscapeControlHTML ( hl . FormattedContent , webCtx . Base . Locale , charset . RuneNBSP )
escapeStatus = escapeStatus . Or ( lineEscapeStatus [ i ] )
}
return webCtx . RenderToHTML ( "base/markup_codepreview" , map [ string ] any {
"FullURL" : opts . FullURL ,
"FilePath" : opts . FilePath ,
"LineStart" : opts . LineStart ,
"LineStop" : realLineStop ,
"RepoLink" : dbRepo . Link ( ) ,
"CommitID" : opts . CommitID ,
"HighlightLines" : highlightLines ,
"EscapeStatus" : escapeStatus ,
"LineEscapeStatus" : lineEscapeStatus ,
} )
}