1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-11 12:59:16 +00:00

Reconstruct the structure of go

This commit is contained in:
Tw93
2025-12-01 19:26:03 +08:00
parent 4bd4ffc7be
commit 36a84e5211
20 changed files with 1441 additions and 1273 deletions

View File

@@ -1,6 +1,7 @@
package main
import (
"context"
"encoding/gob"
"encoding/json"
"fmt"
@@ -291,7 +292,7 @@ func removeOverviewSnapshot(path string) {
// prefetchOverviewCache scans overview directories in background
// to populate cache for faster overview mode access
func prefetchOverviewCache() {
func prefetchOverviewCache(ctx context.Context) {
entries := createOverviewEntries()
// Check which entries need refresh
@@ -309,8 +310,15 @@ func prefetchOverviewCache() {
return
}
// Scan and cache in background
// Scan and cache in background with context cancellation support
for _, path := range needScan {
// Check if context is cancelled
select {
case <-ctx.Done():
return
default:
}
size, err := measureOverviewSize(path)
if err == nil && size > 0 {
_ = storeOverviewSize(path, size)

View File

@@ -59,17 +59,17 @@ var projectDependencyDirs = map[string]bool{
".pnpm-store": true, // pnpm store
// Python dependencies and outputs
"venv": true,
".venv": true,
"virtualenv": true,
"__pycache__": true,
".pytest_cache": true,
".mypy_cache": true,
".ruff_cache": true,
".tox": true,
".eggs": true,
"htmlcov": true, // Coverage reports
".ipynb_checkpoints": true, // Jupyter checkpoints
"venv": true,
".venv": true,
"virtualenv": true,
"__pycache__": true,
".pytest_cache": true,
".mypy_cache": true,
".ruff_cache": true,
".tox": true,
".eggs": true,
"htmlcov": true, // Coverage reports
".ipynb_checkpoints": true, // Jupyter checkpoints
// Ruby dependencies
"vendor": true,
@@ -95,10 +95,10 @@ var projectDependencyDirs = map[string]bool{
".nyc_output": true, // NYC coverage
// Frontend framework outputs
".angular": true, // Angular CLI cache
".svelte-kit": true, // SvelteKit build
".astro": true, // Astro cache
".docusaurus": true, // Docusaurus build
".angular": true, // Angular CLI cache
".svelte-kit": true, // SvelteKit build
".astro": true, // Astro cache
".docusaurus": true, // Docusaurus build
// iOS/macOS development
"DerivedData": true,

View File

@@ -6,8 +6,8 @@ const (
maxEntries = 30
maxLargeFiles = 30
barWidth = 24
minLargeFileSize = 100 << 20 // 100 MB
defaultViewport = 12 // Default viewport when terminal height is unknown
minLargeFileSize = 100 << 20 // 100 MB
defaultViewport = 12 // Default viewport when terminal height is unknown
overviewCacheTTL = 7 * 24 * time.Hour // 7 days
overviewCacheFile = "overview_sizes.json"
duTimeout = 60 * time.Second // Increased for large directories

View File

@@ -56,14 +56,19 @@ func deletePathWithProgress(root string, counter *int64) (int64, error) {
return nil
})
if err != nil {
return count, err
// Track walk error separately
if err != nil && firstErr == nil {
firstErr = err
}
if err := os.RemoveAll(root); err != nil {
return count, err
// Try to remove remaining directory structure
// Even if this fails, we still report files deleted
if removeErr := os.RemoveAll(root); removeErr != nil {
if firstErr == nil {
firstErr = removeErr
}
}
// Return the first error encountered during deletion if any
// Always return count (even if there were errors), along with first error
return count, firstErr
}

View File

@@ -141,7 +141,10 @@ func main() {
}
// Prefetch overview cache in background (non-blocking)
go prefetchOverviewCache()
// Use context with timeout to prevent hanging
prefetchCtx, prefetchCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer prefetchCancel()
go prefetchOverviewCache(prefetchCtx)
p := tea.NewProgram(newModel(abs, isOverview), tea.WithAltScreen())
if err := p.Start(); err != nil {
@@ -509,7 +512,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
// Handle delete confirmation
if m.deleteConfirm {
if msg.String() == "delete" || msg.String() == "backspace" {
switch msg.String() {
case "delete", "backspace":
// Confirm delete - start async deletion
if m.deleteTarget != nil {
m.deleteConfirm = false
@@ -525,17 +529,14 @@ func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
m.deleteConfirm = false
m.deleteTarget = nil
return m, nil
} else if msg.String() == "esc" || msg.String() == "q" {
case "esc", "q":
// Cancel delete with ESC or Q
m.status = "Cancelled"
m.deleteConfirm = false
m.deleteTarget = nil
return m, nil
} else {
// Any other key also cancels
m.status = "Cancelled"
m.deleteConfirm = false
m.deleteTarget = nil
default:
// Ignore other keys - keep showing confirmation
return m, nil
}
}

View File

@@ -87,10 +87,10 @@ func scanPathConcurrent(root string, filesScanned, dirsScanned, bytesScanned *in
atomic.AddInt64(&total, size)
entryChan <- dirEntry{
Name: child.Name() + " →", // Add arrow to indicate symlink
Name: child.Name() + " →", // Add arrow to indicate symlink
Path: fullPath,
Size: size,
IsDir: false, // Don't allow navigation into symlinks
IsDir: false, // Don't allow navigation into symlinks
LastAccess: getLastAccessTimeFromInfo(info),
}
continue
@@ -189,10 +189,14 @@ func scanPathConcurrent(root string, filesScanned, dirsScanned, bytesScanned *in
entries = entries[:maxEntries]
}
// Try to use Spotlight for faster large file discovery
// Try to use Spotlight (mdfind) for faster large file discovery
// This is a performance optimization that gracefully falls back to scan results
// if Spotlight is unavailable or fails. The fallback is intentionally silent
// because users only care about correct results, not the method used.
if spotlightFiles := findLargeFilesWithSpotlight(root, minLargeFileSize); len(spotlightFiles) > 0 {
largeFiles = spotlightFiles
} else {
// Use files collected during scanning (fallback path)
// Sort and trim large files collected from scanning
sort.Slice(largeFiles, func(i, j int) bool {
return largeFiles[i].Size > largeFiles[j].Size