gitea源码

treelist.go 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "html/template"
  6. "net/http"
  7. "path"
  8. "strings"
  9. pull_model "code.gitea.io/gitea/models/pull"
  10. "code.gitea.io/gitea/modules/base"
  11. "code.gitea.io/gitea/modules/fileicon"
  12. "code.gitea.io/gitea/modules/git"
  13. "code.gitea.io/gitea/services/context"
  14. "code.gitea.io/gitea/services/gitdiff"
  15. files_service "code.gitea.io/gitea/services/repository/files"
  16. "github.com/go-enry/go-enry/v2"
  17. )
  18. // TreeList get all files' entries of a repository
  19. func TreeList(ctx *context.Context) {
  20. tree, err := ctx.Repo.Commit.SubTree("/")
  21. if err != nil {
  22. ctx.ServerError("Repo.Commit.SubTree", err)
  23. return
  24. }
  25. entries, err := tree.ListEntriesRecursiveFast()
  26. if err != nil {
  27. ctx.ServerError("ListEntriesRecursiveFast", err)
  28. return
  29. }
  30. entries.CustomSort(base.NaturalSortLess)
  31. files := make([]string, 0, len(entries))
  32. for _, entry := range entries {
  33. if !isExcludedEntry(entry) {
  34. files = append(files, entry.Name())
  35. }
  36. }
  37. ctx.JSON(http.StatusOK, files)
  38. }
  39. func isExcludedEntry(entry *git.TreeEntry) bool {
  40. if entry.IsDir() {
  41. return true
  42. }
  43. if entry.IsSubModule() {
  44. return true
  45. }
  46. if enry.IsVendor(entry.Name()) {
  47. return true
  48. }
  49. return false
  50. }
  51. // WebDiffFileItem is used by frontend, check the field names in frontend before changing
  52. type WebDiffFileItem struct {
  53. FullName string
  54. DisplayName string
  55. NameHash string
  56. DiffStatus string
  57. EntryMode string
  58. IsViewed bool
  59. Children []*WebDiffFileItem
  60. FileIcon template.HTML
  61. }
  62. // WebDiffFileTree is used by frontend, check the field names in frontend before changing
  63. type WebDiffFileTree struct {
  64. TreeRoot WebDiffFileItem
  65. }
  66. // transformDiffTreeForWeb transforms a gitdiff.DiffTree into a WebDiffFileTree for Web UI rendering
  67. // it also takes a map of file names to their viewed state, which is used to mark files as viewed
  68. func transformDiffTreeForWeb(renderedIconPool *fileicon.RenderedIconPool, diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) (dft WebDiffFileTree) {
  69. dirNodes := map[string]*WebDiffFileItem{"": &dft.TreeRoot}
  70. addItem := func(item *WebDiffFileItem) {
  71. var parentPath string
  72. pos := strings.LastIndexByte(item.FullName, '/')
  73. if pos == -1 {
  74. item.DisplayName = item.FullName
  75. } else {
  76. parentPath = item.FullName[:pos]
  77. item.DisplayName = item.FullName[pos+1:]
  78. }
  79. parentNode, parentExists := dirNodes[parentPath]
  80. if !parentExists {
  81. parentNode = &dft.TreeRoot
  82. fields := strings.Split(parentPath, "/")
  83. for idx, field := range fields {
  84. nodePath := strings.Join(fields[:idx+1], "/")
  85. node, ok := dirNodes[nodePath]
  86. if !ok {
  87. node = &WebDiffFileItem{EntryMode: "tree", DisplayName: field, FullName: nodePath}
  88. dirNodes[nodePath] = node
  89. parentNode.Children = append(parentNode.Children, node)
  90. }
  91. parentNode = node
  92. }
  93. }
  94. parentNode.Children = append(parentNode.Children, item)
  95. }
  96. for _, file := range diffTree.Files {
  97. item := &WebDiffFileItem{FullName: file.HeadPath, DiffStatus: file.Status}
  98. item.IsViewed = filesViewedState[item.FullName] == pull_model.Viewed
  99. item.NameHash = git.HashFilePathForWebUI(item.FullName)
  100. item.FileIcon = fileicon.RenderEntryIconHTML(renderedIconPool, &fileicon.EntryInfo{BaseName: path.Base(file.HeadPath), EntryMode: file.HeadMode})
  101. switch file.HeadMode {
  102. case git.EntryModeTree:
  103. item.EntryMode = "tree"
  104. case git.EntryModeCommit:
  105. item.EntryMode = "commit" // submodule
  106. default:
  107. // default to empty, and will be treated as "blob" file because there is no "symlink" support yet
  108. }
  109. addItem(item)
  110. }
  111. var mergeSingleDir func(node *WebDiffFileItem)
  112. mergeSingleDir = func(node *WebDiffFileItem) {
  113. if len(node.Children) == 1 {
  114. if child := node.Children[0]; child.EntryMode == "tree" {
  115. node.FullName = child.FullName
  116. node.DisplayName = node.DisplayName + "/" + child.DisplayName
  117. node.Children = child.Children
  118. mergeSingleDir(node)
  119. }
  120. }
  121. }
  122. for _, node := range dft.TreeRoot.Children {
  123. mergeSingleDir(node)
  124. }
  125. return dft
  126. }
  127. func TreeViewNodes(ctx *context.Context) {
  128. renderedIconPool := fileicon.NewRenderedIconPool()
  129. results, err := files_service.GetTreeViewNodes(ctx, ctx.Repo.RepoLink, renderedIconPool, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormString("sub_path"))
  130. if err != nil {
  131. ctx.ServerError("GetTreeViewNodes", err)
  132. return
  133. }
  134. ctx.JSON(http.StatusOK, map[string]any{"fileTreeNodes": results, "renderedIconPool": renderedIconPool.IconSVGs})
  135. }