forked from tangled.org/core
this repo has no description

appview: diff: organize changed-files into file-tree

introduces the filetree package.

eventually we will have a sticky side-panel style layout for any page
displaying diffs (probably makes most sense when we have split diffs),
and this file-tree will move there.

Changed files
+128 -30
appview
filetree
pages
state
patchutil
types
+62
appview/filetree/filetree.go
···
···
+
package filetree
+
+
import (
+
"path/filepath"
+
"sort"
+
"strings"
+
)
+
+
type FileTreeNode struct {
+
Name string
+
Path string
+
IsDirectory bool
+
Children map[string]*FileTreeNode
+
}
+
+
// NewNode creates a new node
+
func newNode(name, path string, isDir bool) *FileTreeNode {
+
return &FileTreeNode{
+
Name: name,
+
Path: path,
+
IsDirectory: isDir,
+
Children: make(map[string]*FileTreeNode),
+
}
+
}
+
+
func FileTree(files []string) *FileTreeNode {
+
rootNode := newNode("", "", true)
+
+
sort.Strings(files)
+
+
for _, file := range files {
+
if file == "" {
+
continue
+
}
+
+
parts := strings.Split(filepath.Clean(file), "/")
+
if len(parts) == 0 {
+
continue
+
}
+
+
currentNode := rootNode
+
currentPath := ""
+
+
for i, part := range parts {
+
if currentPath == "" {
+
currentPath = part
+
} else {
+
currentPath = filepath.Join(currentPath, part)
+
}
+
+
isDir := i < len(parts)-1
+
+
if _, exists := currentNode.Children[part]; !exists {
+
currentNode.Children[part] = newNode(part, currentPath, isDir)
+
}
+
+
currentNode = currentNode.Children[part]
+
}
+
}
+
+
return rootNode
+
}
+2
appview/pages/funcmap.go
···
"time"
"github.com/dustin/go-humanize"
"tangled.sh/tangled.sh/core/appview/pages/markup"
)
···
return template.HTML(data)
},
"cssContentHash": CssContentHash,
}
}
···
"time"
"github.com/dustin/go-humanize"
+
"tangled.sh/tangled.sh/core/appview/filetree"
"tangled.sh/tangled.sh/core/appview/pages/markup"
)
···
return template.HTML(data)
},
"cssContentHash": CssContentHash,
+
"fileTree": filetree.FileTree,
}
}
+6 -5
appview/pages/pages.go
···
}
type RepoCommitParams struct {
-
LoggedInUser *auth.User
-
RepoInfo RepoInfo
-
Active string
types.RepoCommitResponse
-
EmailToDidOrHandle map[string]string
}
func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error {
···
DidHandleMap map[string]string
RepoInfo RepoInfo
Pull *db.Pull
-
Diff types.NiceDiff
Round int
Submission *db.PullSubmission
}
···
}
type RepoCommitParams struct {
+
LoggedInUser *auth.User
+
RepoInfo RepoInfo
+
Active string
+
EmailToDidOrHandle map[string]string
+
types.RepoCommitResponse
}
func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error {
···
DidHandleMap map[string]string
RepoInfo RepoInfo
Pull *db.Pull
+
Diff *types.NiceDiff
Round int
Submission *db.PullSubmission
}
+4 -17
appview/pages/templates/repo/fragments/diff.html
···
{{ $diff := index . 1 }}
{{ $commit := $diff.Commit }}
{{ $stat := $diff.Stat }}
{{ $diff := $diff.Diff }}
{{ $this := $commit.This }}
···
<strong class="text-sm uppercase dark:text-gray-200">Changed files</strong>
{{ block "statPill" $stat }} {{ end }}
</div>
-
<div class="overflow-x-auto">
-
{{ range $diff }}
-
<ul class="dark:text-gray-200">
-
{{ if .IsDelete }}
-
<li><a href="#file-{{ .Name.Old }}" class="dark:hover:text-gray-300">{{ .Name.Old }}</a></li>
-
{{ else }}
-
<li><a href="#file-{{ .Name.New }}" class="dark:hover:text-gray-300">{{ .Name.New }}</a></li>
-
{{ end }}
-
</ul>
-
{{ end }}
-
</div>
</div>
</section>
···
<summary class="list-none cursor-pointer sticky top-0">
<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
-
<div class="flex gap-1 items-center" style="direction: ltr;">
{{ $markerstyle := "diff-type p-1 mr-1 font-mono text-sm rounded select-none" }}
{{ if .IsNew }}
<span class="bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400 {{ $markerstyle }}">ADDED</span>
···
{{ block "statPill" .Stats }} {{ end }}
</div>
-
<div class="flex gap-2 items-center overflow-x-auto" style="direction: rtl;">
{{ if .IsDelete }}
<a class="dark:text-white whitespace-nowrap overflow-x-auto" {{if $this }}href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.Old }}"{{end}}>
{{ .Name.Old }}
···
{{ else if .IsCopy }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
This file has been copied.
-
</p>
-
{{ else if .IsRename }}
-
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
-
This file has been renamed.
</p>
{{ else if .IsBinary }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
···
{{ $diff := index . 1 }}
{{ $commit := $diff.Commit }}
{{ $stat := $diff.Stat }}
+
{{ $fileTree := fileTree $diff.ChangedFiles }}
{{ $diff := $diff.Diff }}
{{ $this := $commit.This }}
···
<strong class="text-sm uppercase dark:text-gray-200">Changed files</strong>
{{ block "statPill" $stat }} {{ end }}
</div>
+
{{ block "fileTree" $fileTree }} {{ end }}
</div>
</section>
···
<summary class="list-none cursor-pointer sticky top-0">
<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
+
<div class="flex gap-1 items-center">
{{ $markerstyle := "diff-type p-1 mr-1 font-mono text-sm rounded select-none" }}
{{ if .IsNew }}
<span class="bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400 {{ $markerstyle }}">ADDED</span>
···
{{ block "statPill" .Stats }} {{ end }}
</div>
+
<div class="flex gap-2 items-center overflow-x-auto">
{{ if .IsDelete }}
<a class="dark:text-white whitespace-nowrap overflow-x-auto" {{if $this }}href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.Old }}"{{end}}>
{{ .Name.Old }}
···
{{ else if .IsCopy }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
This file has been copied.
</p>
{{ else if .IsBinary }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
+27
appview/pages/templates/repo/fragments/filetree.html
···
···
+
{{ define "fileTree" }}
+
{{ if and .Name .IsDirectory }}
+
<details open>
+
<summary class="cursor-pointer list-none pt-1">
+
<span class="inline-flex items-center gap-2 ">
+
{{ i "folder" "w-3 h-3 fill-current" }}
+
<span class="text-black dark:text-white">{{ .Name }}</span>
+
</span>
+
</summary>
+
<div class="ml-1 pl-4 border-l border-gray-200 dark:border-gray-700">
+
{{ range $child := .Children }}
+
{{ block "fileTree" $child }} {{ end }}
+
{{ end }}
+
</div>
+
</details>
+
{{ else if .Name }}
+
<div class="flex items-center gap-2 pt-1">
+
{{ i "file" "w-3 h-3" }}
+
<a href="#file-{{ .Path }}" class="text-black dark:text-white no-underline hover:underline">{{ .Name }}</a>
+
</div>
+
{{ else }}
+
{{ range $child := .Children }}
+
{{ block "fileTree" $child }} {{ end }}
+
{{ end }}
+
{{ end }}
+
{{ end }}
+
+2 -7
appview/pages/templates/repo/fragments/interdiff.html
···
{{ define "repo/fragments/interdiff" }}
{{ $repo := index . 0 }}
{{ $x := index . 1 }}
{{ $diff := $x.Files }}
<section class="mt-6 p-6 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm">
···
<div class="flex gap-2 items-center">
<strong class="text-sm uppercase dark:text-gray-200">files</strong>
</div>
-
<div class="overflow-x-auto">
-
<ul class="dark:text-gray-200">
-
{{ range $diff }}
-
<li><a href="#file-{{ .Name }}" class="dark:hover:text-gray-300">{{ .Name }}</a></li>
-
{{ end }}
-
</ul>
-
</div>
</div>
</section>
···
{{ define "repo/fragments/interdiff" }}
{{ $repo := index . 0 }}
{{ $x := index . 1 }}
+
{{ $fileTree := fileTree $x.AffectedFiles }}
{{ $diff := $x.Files }}
<section class="mt-6 p-6 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm">
···
<div class="flex gap-2 items-center">
<strong class="text-sm uppercase dark:text-gray-200">files</strong>
</div>
+
{{ block "fileTree" $fileTree }} {{ end }}
</div>
</section>
+3 -1
appview/state/pull.go
···
}
}
s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{
LoggedInUser: user,
DidHandleMap: didHandleMap,
···
Pull: pull,
Round: roundIdInt,
Submission: pull.Submissions[roundIdInt],
-
Diff: pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch),
})
}
···
}
}
+
diff := pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch)
+
s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{
LoggedInUser: user,
DidHandleMap: didHandleMap,
···
Pull: pull,
Round: roundIdInt,
Submission: pull.Submissions[roundIdInt],
+
Diff: &diff,
})
}
+8
patchutil/interdiff.go
···
Files []*InterdiffFile
}
func (i *InterdiffResult) String() string {
var b strings.Builder
for _, f := range i.Files {
···
Files []*InterdiffFile
}
+
func (i *InterdiffResult) AffectedFiles() []string {
+
files := make([]string, len(i.Files))
+
for _, f := range i.Files {
+
files = append(files, f.Name)
+
}
+
return files
+
}
+
func (i *InterdiffResult) String() string {
var b strings.Builder
for _, f := range i.Files {
+14
types/diff.go
···
Patch string `json:"patch"`
Diff []*gitdiff.File `json:"diff"`
}
···
Patch string `json:"patch"`
Diff []*gitdiff.File `json:"diff"`
}
+
+
func (d *NiceDiff) ChangedFiles() []string {
+
files := make([]string, len(d.Diff))
+
+
for i, f := range d.Diff {
+
if f.IsDelete {
+
files[i] = f.Name.Old
+
} else {
+
files[i] = f.Name.New
+
}
+
}
+
+
return files
+
}