mirror of
https://gitee.com/johng/gf
synced 2026-07-07 06:15:15 +08:00
feat: 优化依赖树生成并默认使用树形视图
This commit is contained in:
@ -34,11 +34,15 @@ func (a *analyzer) generate(in Input) string {
|
||||
// generateTree generates ASCII tree output.
|
||||
func (a *analyzer) generateTree(in Input) string {
|
||||
var sb strings.Builder
|
||||
pkgs := a.getSortedPackages()
|
||||
|
||||
for _, pkgPath := range pkgs {
|
||||
// Find root packages (packages that are not imported by any other package)
|
||||
rootPkgs := a.findRootPackages()
|
||||
|
||||
// Use a single visited map across all root packages to avoid duplicates
|
||||
a.visited = make(map[string]bool)
|
||||
|
||||
for _, pkgPath := range rootPkgs {
|
||||
pkg := a.packages[pkgPath]
|
||||
a.visited = make(map[string]bool)
|
||||
shortName := a.shortName(pkg.ImportPath, in.Group)
|
||||
sb.WriteString(shortName + "\n")
|
||||
a.printTreeNode(&sb, pkg, "", in, 0)
|
||||
@ -46,6 +50,33 @@ func (a *analyzer) generateTree(in Input) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// findRootPackages finds packages that are not imported by any other internal package.
|
||||
func (a *analyzer) findRootPackages() []string {
|
||||
// Build a set of all imported packages
|
||||
imported := make(map[string]bool)
|
||||
for _, pkg := range a.packages {
|
||||
for _, dep := range pkg.Imports {
|
||||
imported[dep] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Find packages that are not imported by others
|
||||
roots := make([]string, 0)
|
||||
for pkgPath := range a.packages {
|
||||
if !imported[pkgPath] {
|
||||
roots = append(roots, pkgPath)
|
||||
}
|
||||
}
|
||||
|
||||
// If no roots found (circular dependencies), use all packages
|
||||
if len(roots) == 0 {
|
||||
roots = a.getSortedPackages()
|
||||
}
|
||||
|
||||
sort.Strings(roots)
|
||||
return roots
|
||||
}
|
||||
|
||||
func (a *analyzer) printTreeNode(sb *strings.Builder, pkg *goPackage, prefix string, in Input, depth int) {
|
||||
if in.Depth > 0 && depth >= in.Depth {
|
||||
return
|
||||
|
||||
@ -6,7 +6,7 @@ let selectedPackage = null;
|
||||
let allPackages = [];
|
||||
let isRemoteMode = false;
|
||||
let currentRemoteModule = '';
|
||||
let packageListMode = 'flat'; // 'flat' or 'tree'
|
||||
let packageListMode = 'tree'; // 'flat' or 'tree'
|
||||
let expandedNodes = new Set(); // Track expanded tree nodes
|
||||
|
||||
// Pan and Zoom state
|
||||
@ -94,9 +94,11 @@ async function init() {
|
||||
theme.init();
|
||||
initPanZoom();
|
||||
initRemoteModuleInput();
|
||||
await loadModuleName();
|
||||
await loadPackages();
|
||||
await refresh();
|
||||
const hasLocalModule = await loadModuleName();
|
||||
if (hasLocalModule) {
|
||||
await loadPackages();
|
||||
await refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize remote module input
|
||||
@ -376,21 +378,28 @@ function showZoomIndicator() {
|
||||
}
|
||||
|
||||
// Load module name from server
|
||||
// Returns true if local module exists, false otherwise
|
||||
async function loadModuleName() {
|
||||
try {
|
||||
const response = await fetch('/api/module');
|
||||
const data = await response.json();
|
||||
document.getElementById('moduleName').textContent = data.name || '';
|
||||
|
||||
// If no local module, set default value for remote module input
|
||||
// If no local module, set default value and auto-analyze
|
||||
if (!data.name) {
|
||||
const input = document.getElementById('remoteModuleInput');
|
||||
if (input && !input.value) {
|
||||
input.value = 'github.com/gogf/gf/v2';
|
||||
// Fetch versions first, then auto-analyze
|
||||
await fetchVersions();
|
||||
analyzeRemoteModule();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Failed to load module name:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,14 +575,14 @@ function toggleTreeNode(path) {
|
||||
|
||||
// Re-render with current filter
|
||||
const query = document.getElementById('searchInput').value.toLowerCase();
|
||||
const filtered = query ? allPackages.filter(pkg => pkg.toLowerCase().includes(query)) : allPackages;
|
||||
const filtered = query ? allPackages.filter(pkg => getPkgName(pkg).toLowerCase().includes(query)) : allPackages;
|
||||
renderPackageList(filtered);
|
||||
}
|
||||
|
||||
// Filter packages by search query
|
||||
function filterPackages() {
|
||||
const query = document.getElementById('searchInput').value.toLowerCase();
|
||||
const filtered = allPackages.filter(pkg => pkg.toLowerCase().includes(query));
|
||||
const filtered = allPackages.filter(pkg => getPkgName(pkg).toLowerCase().includes(query));
|
||||
renderPackageList(filtered);
|
||||
}
|
||||
|
||||
|
||||
@ -87,8 +87,8 @@
|
||||
<span class="count" id="packageCount">0</span>
|
||||
</div>
|
||||
<div class="sidebar-mode">
|
||||
<button class="mode-btn active" id="modeFlat" onclick="setPackageListMode('flat')" title="Flat list">☰</button>
|
||||
<button class="mode-btn" id="modeTree" onclick="setPackageListMode('tree')" title="Tree view">🌲</button>
|
||||
<button class="mode-btn" id="modeFlat" onclick="setPackageListMode('flat')" title="Flat list">☰</button>
|
||||
<button class="mode-btn active" id="modeTree" onclick="setPackageListMode('tree')" title="Tree view">🌲</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
|
||||
@ -112,7 +112,7 @@ body {
|
||||
}
|
||||
|
||||
.remote-input-group input[type="text"] {
|
||||
width: 260px;
|
||||
width: 320px;
|
||||
background: var(--input-bg);
|
||||
border: 1px solid var(--input-border);
|
||||
color: var(--text-primary);
|
||||
@ -128,7 +128,7 @@ body {
|
||||
}
|
||||
|
||||
.remote-input-group select {
|
||||
width: 120px;
|
||||
width: 160px;
|
||||
background: var(--input-bg);
|
||||
border: 1px solid var(--input-border);
|
||||
color: var(--text-primary);
|
||||
|
||||
Reference in New Issue
Block a user