From 8f1f2226f3dfa4b08f1ec98cf9186125cb975cc5 Mon Sep 17 00:00:00 2001 From: swanadiva Date: Wed, 24 Jun 2026 11:46:17 +0700 Subject: [PATCH] perf: SFTP directory listing cache - Added dirCache map to sftpPane for caching directory listings - Cache hit: instant navigation (no disk/network I/O) - Cache miss: read from disk/SFTP, store in cache per path - R key clears current directory cache and forces fresh read - Significant speedup when navigating back to previously visited folders --- CHANGELOG.md | 7 ++++++ pkg/tui/sftp_browser_tab.go | 45 ++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c17ee9..12025d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,13 @@ - Stacked: `maxH = t.height - 6`; Side-by-side: `maxH = t.height - 5` - Breakpoints: compact (<60), medium (60–100), wide (≥100) +### SFTP Performance Fix +- Added directory listing cache (`dirCache map[string][]os.FileInfo`) to each pane +- Cache hit: instant navigation (no disk/network call) +- Cache miss: read from disk/SFTP, store in cache +- `R` key now clears cache for current directory and forces re-read +- Cache is per-path: navigating to a previously visited folder is instant + ## v1.0.0 (2025-01-30) ### Added diff --git a/pkg/tui/sftp_browser_tab.go b/pkg/tui/sftp_browser_tab.go index 2d1753d..e0d96c1 100644 --- a/pkg/tui/sftp_browser_tab.go +++ b/pkg/tui/sftp_browser_tab.go @@ -38,6 +38,9 @@ type sftpPane struct { filterMode bool filter string + // Directory listing cache: path → entries + dirCache map[string][]os.FileInfo + // Remote only sftpClient *sftp.Client @@ -83,9 +86,11 @@ func NewSFTPBrowserTab(host *models.Host, dataDir string) *SFTPBrowserTab { left: sftpPane{ cwd: home, localRoot: home, + dirCache: make(map[string][]os.FileInfo), }, right: sftpPane{ - cwd: "/", + cwd: "/", + dirCache: make(map[string][]os.FileInfo), }, filterInput: fi, } @@ -154,10 +159,22 @@ func (t *SFTPBrowserTab) connect() { func (t *SFTPBrowserTab) refreshLocal() { t.mu.Lock() p := &t.left - p.entries = nil + cwd := p.cwd t.mu.Unlock() - entries, err := os.ReadDir(p.cwd) + // Check cache first + t.mu.Lock() + if cached, ok := p.dirCache[cwd]; ok { + p.entries = cached + if p.selIdx >= len(cached) { + p.selIdx = 0 + } + t.mu.Unlock() + return + } + t.mu.Unlock() + + entries, err := os.ReadDir(cwd) if err != nil { return } @@ -178,6 +195,7 @@ func (t *SFTPBrowserTab) refreshLocal() { }) t.mu.Lock() + p.dirCache[cwd] = infos p.entries = infos if p.selIdx >= len(infos) { p.selIdx = 0 @@ -196,6 +214,18 @@ func (t *SFTPBrowserTab) refreshRemote() { return } + // Check cache first + t.mu.Lock() + if cached, ok := p.dirCache[cwd]; ok { + p.entries = cached + if p.selIdx >= len(cached) { + p.selIdx = 0 + } + t.mu.Unlock() + return + } + t.mu.Unlock() + entries, err := sftpClient.ReadDir(cwd) if err != nil { return @@ -209,6 +239,7 @@ func (t *SFTPBrowserTab) refreshRemote() { }) t.mu.Lock() + p.dirCache[cwd] = entries p.entries = entries if p.selIdx >= len(entries) { p.selIdx = 0 @@ -329,6 +360,14 @@ func (t *SFTPBrowserTab) Update(msg tea.Msg) (Tab, tea.Cmd) { p.filter = "" case "r": + // Force refresh: clear cache then re-read + t.mu.Lock() + if t.active == paneRemote { + delete(t.right.dirCache, t.right.cwd) + } else { + delete(t.left.dirCache, t.left.cwd) + } + t.mu.Unlock() if t.active == paneRemote { go t.refreshRemote() } else {