aboutsummaryrefslogtreecommitdiffstats
path: root/routers/web/repo/treelist.go
blob: 340b2bc09174127c98384718ddc8cc91d28f45bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
	"html/template"
	"net/http"
	"path"
	"strings"

	pull_model "code.gitea.io/gitea/models/pull"
	"code.gitea.io/gitea/modules/base"
	"code.gitea.io/gitea/modules/fileicon"
	"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"
)

// TreeList get all files' entries of a repository
func TreeList(ctx *context.Context) {
	tree, err := ctx.Repo.Commit.SubTree("/")
	if err != nil {
		ctx.ServerError("Repo.Commit.SubTree", err)
		return
	}

	entries, err := tree.ListEntriesRecursiveFast()
	if err != nil {
		ctx.ServerError("ListEntriesRecursiveFast", err)
		return
	}
	entries.CustomSort(base.NaturalSortLess)

	files := make([]string, 0, len(entries))
	for _, entry := range entries {
		if !isExcludedEntry(entry) {
			files = append(files, entry.Name())
		}
	}
	ctx.JSON(http.StatusOK, files)
}

func isExcludedEntry(entry *git.TreeEntry) bool {
	if entry.IsDir() {
		return true
	}

	if entry.IsSubModule() {
		return true
	}

	if enry.IsVendor(entry.Name()) {
		return true
	}

	return false
}

// WebDiffFileItem is used by frontend, check the field names in frontend before changing
type WebDiffFileItem struct {
	FullName    string
	DisplayName string
	NameHash    string
	DiffStatus  string
	EntryMode   string
	IsViewed    bool
	Children    []*WebDiffFileItem
	FileIcon    template.HTML
}

// WebDiffFileTree is used by frontend, check the field names in frontend before changing
type WebDiffFileTree struct {
	TreeRoot WebDiffFileItem
}

// transformDiffTreeForWeb transforms a gitdiff.DiffTree into a WebDiffFileTree for Web UI rendering
// it also takes a map of file names to their viewed state, which is used to mark files as viewed
func transformDiffTreeForWeb(renderedIconPool *fileicon.RenderedIconPool, diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) (dft WebDiffFileTree) {
	dirNodes := map[string]*WebDiffFileItem{"": &dft.TreeRoot}
	addItem := func(item *WebDiffFileItem) {
		var parentPath string
		pos := strings.LastIndexByte(item.FullName, '/')
		if pos == -1 {
			item.DisplayName = item.FullName
		} else {
			parentPath = item.FullName[:pos]
			item.DisplayName = item.FullName[pos+1:]
		}
		parentNode, parentExists := dirNodes[parentPath]
		if !parentExists {
			parentNode = &dft.TreeRoot
			fields := strings.Split(parentPath, "/")
			for idx, field := range fields {
				nodePath := strings.Join(fields[:idx+1], "/")
				node, ok := dirNodes[nodePath]
				if !ok {
					node = &WebDiffFileItem{EntryMode: "tree", DisplayName: field, FullName: nodePath}
					dirNodes[nodePath] = node
					parentNode.Children = append(parentNode.Children, node)
				}
				parentNode = node
			}
		}
		parentNode.Children = append(parentNode.Children, item)
	}

	for _, file := range diffTree.Files {
		item := &WebDiffFileItem{FullName: file.HeadPath, DiffStatus: file.Status}
		item.IsViewed = filesViewedState[item.FullName] == pull_model.Viewed
		item.NameHash = git.HashFilePathForWebUI(item.FullName)
		item.FileIcon = fileicon.RenderEntryIconHTML(renderedIconPool, &fileicon.EntryInfo{BaseName: path.Base(file.HeadPath), EntryMode: file.HeadMode})

		switch file.HeadMode {
		case git.EntryModeTree:
			item.EntryMode = "tree"
		case git.EntryModeCommit:
			item.EntryMode = "commit" // submodule
		default:
			// default to empty, and will be treated as "blob" file because there is no "symlink" support yet
		}
		addItem(item)
	}

	var mergeSingleDir func(node *WebDiffFileItem)
	mergeSingleDir = func(node *WebDiffFileItem) {
		if len(node.Children) == 1 {
			if child := node.Children[0]; child.EntryMode == "tree" {
				node.FullName = child.FullName
				node.DisplayName = node.DisplayName + "/" + child.DisplayName
				node.Children = child.Children
				mergeSingleDir(node)
			}
		}
	}
	for _, node := range dft.TreeRoot.Children {
		mergeSingleDir(node)
	}
	return dft
}

func TreeViewNodes(ctx *context.Context) {
	renderedIconPool := fileicon.NewRenderedIconPool()
	results, err := files_service.GetTreeViewNodes(ctx, ctx.Repo.RepoLink, renderedIconPool, 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, "renderedIconPool": renderedIconPool.IconSVGs})
}