feat(tui): collapse directories when possible in file tree (#28512)
This commit is contained in:
@@ -74,21 +74,41 @@ export function buildFileTree(files: readonly FileTreeItem[]): FileTree {
|
||||
|
||||
export function flattenFileTree(tree: FileTree, expanded?: ReadonlySet<number>): FileTreeRow[] {
|
||||
const rows: FileTreeRow[] = []
|
||||
const visit = (id: number) => {
|
||||
const visit = (id: number, depth: number) => {
|
||||
const node = tree.nodes[id]!
|
||||
if (node.kind === "file") {
|
||||
rows.push({
|
||||
id: node.id,
|
||||
depth,
|
||||
kind: node.kind,
|
||||
name: node.name,
|
||||
fileIndex: node.fileIndex,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const chain = collapsedFileTreeDirectoryChain(tree, node.id)
|
||||
const last = chain[chain.length - 1]!
|
||||
rows.push({
|
||||
id: node.id,
|
||||
depth: node.depth,
|
||||
depth,
|
||||
kind: node.kind,
|
||||
name: node.name,
|
||||
name: chain.map((item) => item.name).join("/"),
|
||||
fileIndex: node.fileIndex,
|
||||
})
|
||||
if (node.kind === "directory" && (!expanded || expanded.has(node.id))) node.children.forEach(visit)
|
||||
if (!expanded || expanded.has(node.id)) last.children.forEach((child) => visit(child, depth + 1))
|
||||
}
|
||||
tree.roots.forEach(visit)
|
||||
tree.roots.forEach((root) => visit(root, 0))
|
||||
return rows
|
||||
}
|
||||
|
||||
function collapsedFileTreeDirectoryChain(tree: FileTree, id: number): FileTreeNode[] {
|
||||
const node = tree.nodes[id]!
|
||||
const child = node.children.length === 1 ? tree.nodes[node.children[0]!] : undefined
|
||||
if (child?.kind !== "directory") return [node]
|
||||
return [node, ...collapsedFileTreeDirectoryChain(tree, child.id)]
|
||||
}
|
||||
|
||||
export function compareFileTreeNodes(tree: FileTree, left: number, right: number) {
|
||||
const leftNode = tree.nodes[left]!
|
||||
const rightNode = tree.nodes[right]!
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
/** @jsxImportSource @opentui/solid */
|
||||
import type { ColorInput, ScrollBoxRenderable } from "@opentui/core"
|
||||
import { Locale } from "@/util/locale"
|
||||
import { createEffect, createMemo, For, Match, Switch } from "solid-js"
|
||||
import { buildFileTree, flattenFileTree, type FileTreeItem } from "./diff-viewer-file-tree-utils"
|
||||
|
||||
const FILE_TREE_WIDTH = 32
|
||||
const FILE_TREE_HORIZONTAL_PADDING = 2
|
||||
|
||||
export type DiffViewerFileTreeTheme = {
|
||||
readonly background: ColorInput
|
||||
readonly backgroundPanel: ColorInput
|
||||
@@ -41,7 +45,7 @@ export function DiffViewerFileTree(props: DiffViewerFileTreeProps) {
|
||||
|
||||
return (
|
||||
<box
|
||||
width={32}
|
||||
width={FILE_TREE_WIDTH}
|
||||
flexShrink={0}
|
||||
backgroundColor={props.theme.backgroundPanel}
|
||||
paddingLeft={1}
|
||||
@@ -68,11 +72,15 @@ export function DiffViewerFileTree(props: DiffViewerFileTreeProps) {
|
||||
<For each={rows()}>
|
||||
{(row) => {
|
||||
const highlighted = () => props.focused && props.highlightedNode === row.id
|
||||
const prefix = () =>
|
||||
`${" ".repeat(row.depth)}${row.kind === "directory" ? (props.expandedNodes && !props.expandedNodes.has(row.id) ? "▸ " : "▾ ") : " "}`
|
||||
const name = () =>
|
||||
Locale.truncate(
|
||||
row.name,
|
||||
Math.max(1, FILE_TREE_WIDTH - FILE_TREE_HORIZONTAL_PADDING - prefix().length),
|
||||
)
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text fg={row.kind === "directory" ? props.theme.textMuted : props.theme.text} wrapMode="none">
|
||||
{`${" ".repeat(row.depth)}${row.kind === "directory" ? (props.expandedNodes && !props.expandedNodes.has(row.id) ? "▸ " : "▾ ") : " "}`}
|
||||
</text>
|
||||
<box flexDirection="row" width="100%">
|
||||
<text
|
||||
fg={
|
||||
highlighted()
|
||||
@@ -83,9 +91,25 @@ export function DiffViewerFileTree(props: DiffViewerFileTreeProps) {
|
||||
}
|
||||
bg={highlighted() ? props.theme.primary : undefined}
|
||||
wrapMode="none"
|
||||
flexShrink={0}
|
||||
>
|
||||
{row.name}
|
||||
{prefix()}
|
||||
</text>
|
||||
<box flexGrow={1} minWidth={0}>
|
||||
<text
|
||||
fg={
|
||||
highlighted()
|
||||
? props.theme.background
|
||||
: row.kind === "directory"
|
||||
? props.theme.textMuted
|
||||
: props.theme.text
|
||||
}
|
||||
bg={highlighted() ? props.theme.primary : undefined}
|
||||
wrapMode="none"
|
||||
>
|
||||
{name()}
|
||||
</text>
|
||||
</box>
|
||||
</box>
|
||||
)
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user