mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Add file tree to file view page (#32721)
Resolve #29328 This pull request introduces a file tree on the left side when reviewing files of a repository. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -41,60 +41,45 @@ type blameRow struct {
|
||||
|
||||
// RefBlame render blame page
|
||||
func RefBlame(ctx *context.Context) {
|
||||
fileName := ctx.Repo.TreePath
|
||||
if len(fileName) == 0 {
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
ctx.Data["IsBlame"] = true
|
||||
|
||||
// Get current entry user currently looking at.
|
||||
if ctx.Repo.TreePath == "" {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
treeLink := branchLink
|
||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL()
|
||||
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
|
||||
var treeNames []string
|
||||
paths := make([]string, 0, 5)
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeNames = strings.Split(ctx.Repo.TreePath, "/")
|
||||
for i := range treeNames {
|
||||
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||
}
|
||||
|
||||
ctx.Data["HasParentPath"] = true
|
||||
if len(paths)-2 >= 0 {
|
||||
ctx.Data["ParentPath"] = "/" + paths[len(paths)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// Get current entry user currently looking at.
|
||||
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
|
||||
if err != nil {
|
||||
HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
|
||||
return
|
||||
}
|
||||
|
||||
blob := entry.Blob()
|
||||
treeNames := strings.Split(ctx.Repo.TreePath, "/")
|
||||
var paths []string
|
||||
for i := range treeNames {
|
||||
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||
}
|
||||
|
||||
ctx.Data["Paths"] = paths
|
||||
ctx.Data["TreeLink"] = treeLink
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["BranchLink"] = branchLink
|
||||
|
||||
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
|
||||
ctx.Data["IsBlame"] = true
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
||||
blob := entry.Blob()
|
||||
fileSize := blob.Size()
|
||||
ctx.Data["FileSize"] = fileSize
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
|
||||
tplName := tplRepoViewContent
|
||||
if !ctx.FormBool("only_content") {
|
||||
prepareHomeTreeSideBarSwitch(ctx)
|
||||
tplName = tplRepoView
|
||||
}
|
||||
|
||||
if fileSize >= setting.UI.MaxDisplayFileSize {
|
||||
ctx.Data["IsFileTooLarge"] = true
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
ctx.HTML(http.StatusOK, tplName)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -105,8 +90,7 @@ func RefBlame(ctx *context.Context) {
|
||||
}
|
||||
|
||||
bypassBlameIgnore, _ := strconv.ParseBool(ctx.FormString("bypass-blame-ignore"))
|
||||
|
||||
result, err := performBlame(ctx, ctx.Repo.Repository, ctx.Repo.Commit, fileName, bypassBlameIgnore)
|
||||
result, err := performBlame(ctx, ctx.Repo.Repository, ctx.Repo.Commit, ctx.Repo.TreePath, bypassBlameIgnore)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
@@ -122,7 +106,7 @@ func RefBlame(ctx *context.Context) {
|
||||
|
||||
renderBlame(ctx, result.Parts, commitNames)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
ctx.HTML(http.StatusOK, tplName)
|
||||
}
|
||||
|
||||
type blameResult struct {
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
|
||||
"github.com/go-enry/go-enry/v2"
|
||||
)
|
||||
@@ -84,3 +85,12 @@ func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[str
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
func TreeViewNodes(ctx *context.Context) {
|
||||
results, err := files_service.GetTreeViewNodes(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormString("sub_path"))
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTreeViewNodes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]any{"fileTreeNodes": results})
|
||||
}
|
||||
|
@@ -47,12 +47,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tplRepoEMPTY templates.TplName = "repo/empty"
|
||||
tplRepoHome templates.TplName = "repo/home"
|
||||
tplRepoViewList templates.TplName = "repo/view_list"
|
||||
tplWatchers templates.TplName = "repo/watchers"
|
||||
tplForks templates.TplName = "repo/forks"
|
||||
tplMigrating templates.TplName = "repo/migrate/migrating"
|
||||
tplRepoEMPTY templates.TplName = "repo/empty"
|
||||
tplRepoHome templates.TplName = "repo/home"
|
||||
tplRepoView templates.TplName = "repo/view"
|
||||
tplRepoViewContent templates.TplName = "repo/view_content"
|
||||
tplRepoViewList templates.TplName = "repo/view_list"
|
||||
tplWatchers templates.TplName = "repo/watchers"
|
||||
tplForks templates.TplName = "repo/forks"
|
||||
tplMigrating templates.TplName = "repo/migrate/migrating"
|
||||
)
|
||||
|
||||
type fileInfo struct {
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
@@ -328,6 +330,19 @@ func handleRepoHomeFeed(ctx *context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func prepareHomeTreeSideBarSwitch(ctx *context.Context) {
|
||||
showFileTree := true
|
||||
if ctx.Doer != nil {
|
||||
v, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyCodeViewShowFileTree, "true")
|
||||
if err != nil {
|
||||
log.Error("GetUserSetting: %v", err)
|
||||
} else {
|
||||
showFileTree, _ = strconv.ParseBool(v)
|
||||
}
|
||||
}
|
||||
ctx.Data["UserSettingCodeViewShowFileTree"] = showFileTree
|
||||
}
|
||||
|
||||
// Home render repository home page
|
||||
func Home(ctx *context.Context) {
|
||||
if handleRepoHomeFeed(ctx) {
|
||||
@@ -341,6 +356,8 @@ func Home(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
prepareHomeTreeSideBarSwitch(ctx)
|
||||
|
||||
title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
|
||||
if len(ctx.Repo.Repository.Description) > 0 {
|
||||
title += ": " + ctx.Repo.Repository.Description
|
||||
@@ -410,7 +427,13 @@ func Home(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
if ctx.FormBool("only_content") {
|
||||
ctx.HTML(http.StatusOK, tplRepoViewContent)
|
||||
} else if len(treeNames) != 0 {
|
||||
ctx.HTML(http.StatusOK, tplRepoView)
|
||||
} else {
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
}
|
||||
}
|
||||
|
||||
func RedirectRepoTreeToSrc(ctx *context.Context) {
|
||||
|
26
routers/web/user/setting/settings.go
Normal file
26
routers/web/user/setting/settings.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
func UpdatePreferences(ctx *context.Context) {
|
||||
type preferencesForm struct {
|
||||
CodeViewShowFileTree bool `json:"codeViewShowFileTree"`
|
||||
}
|
||||
form := &preferencesForm{}
|
||||
if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
|
||||
ctx.HTTPError(http.StatusBadRequest, "json decode failed")
|
||||
return
|
||||
}
|
||||
_ = user_model.SetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyCodeViewShowFileTree, strconv.FormatBool(form.CodeViewShowFileTree))
|
||||
ctx.JSONOK()
|
||||
}
|
@@ -580,6 +580,7 @@ func registerRoutes(m *web.Router) {
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user_setting.Profile)
|
||||
m.Post("", web.Bind(forms.UpdateProfileForm{}), user_setting.ProfilePost)
|
||||
m.Post("/update_preferences", user_setting.UpdatePreferences)
|
||||
m.Get("/change_password", auth.MustChangePassword)
|
||||
m.Post("/change_password", web.Bind(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost)
|
||||
m.Post("/avatar", web.Bind(forms.AvatarForm{}), user_setting.AvatarPost)
|
||||
@@ -1175,6 +1176,11 @@ func registerRoutes(m *web.Router) {
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeList)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeList)
|
||||
})
|
||||
m.Group("/tree-view", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeViewNodes)
|
||||
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeViewNodes)
|
||||
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeViewNodes)
|
||||
})
|
||||
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
|
||||
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
|
||||
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
|
||||
|
Reference in New Issue
Block a user