Skip to content

fix(file-tree): add Refresh to directory right-click menu#10551

Open
spalagu wants to merge 1 commit into
warpdotdev:masterfrom
spalagu:fix/file-tree-refresh-menu
Open

fix(file-tree): add Refresh to directory right-click menu#10551
spalagu wants to merge 1 commit into
warpdotdev:masterfrom
spalagu:fix/file-tree-refresh-menu

Conversation

@spalagu
Copy link
Copy Markdown

@spalagu spalagu commented May 9, 2026

Summary

Adds a Refresh menu item under directory headers in the file tree right-click context menu. Re-reads the clicked directory's contents from disk (one level deep, matching lazy-load behavior) via the existing LocalRepoMetadataModel::load_directory path.

Why

Long-standing open issues report Project Explorer / file tree failing to reflect filesystem changes after the initial scan:

Source-level: in apply_file_tree_mutations (crates/repo_metadata/src/local_model.rs), three branches do if lazy_load && !is_parent_loaded_in_entry { continue }, silently dropping watcher mutations when a parent directory is not loaded. Combined with FSEvents itself being best-effort, the in-memory tree drifts from disk and there is currently no API or UI to reconcile.

What this PR does

  • New FileTreeAction::RefreshDirectory { id } variant
  • Right-click menu on any directory header now shows Refresh after Open in new tab
  • Dispatch handler calls a new refresh_directory helper
  • Helper resolves the target directory and calls the existing wrapper RepoMetadataModel::load_directory(repo_root, dir_path, ctx), which delegates to LocalRepoMetadataModel::load_directoryEntry::loadEntry::build_tree (force-reads from disk)

Surgical scope: only the targeted subtree is re-scanned. No watcher churn, no full root re-index. FileTreeItem::File does not get the option since files don't have contents to refresh.

What this PR does NOT do (intentionally scoped out)

Files

  • app/src/code/file_tree/view.rs — +42 lines, 0 deletions

Verification

  • cargo check -p warp --bin warp-oss clean
  • cargo clippy -p warp --bin warp-oss --no-deps no new warnings
  • Manually verified on a self-built warp-oss bundle: right-click any directory header → Refresh appears after Open in new tab; selecting it reconciles a drifted subtree

Closes #10205

@cla-bot cla-bot Bot added the cla-signed label May 9, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 9, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 9, 2026

@spalagu

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR adds a directory-only Refresh action to the file tree context menu and routes it through the existing local repo metadata directory-loading path.

Concerns

  • The refresh helper uses the displayed file-tree root as the repository metadata key. Displayed roots can be subdirectories of the indexed repository, so refresh fails with RepoNotFound and does not update the tree in common cwd-inside-repo cases.
  • This is a user-visible menu change, but the provided context includes only a manual verification note and no screenshot or screen recording. Manual testing evidence is required for changes that can be visually verified; please include screenshots or a recording showing the menu item and refresh behavior, or justify why that is not possible.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread app/src/code/file_tree/view.rs Outdated
return;
};
let dir_path = item.path().clone();
let root_path = id.root.clone();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] Refresh uses the displayed root as the metadata repository key, but displayed roots can be subdirectories of the indexed repo; use the backing entry root or this is a no-op with RepoNotFound for common cwd-inside-repo trees.

Suggested change
let root_path = id.root.clone();
let root_path = (**root_dir.entry.root_directory()).clone();

@spalagu spalagu force-pushed the fix/file-tree-refresh-menu branch from 42b6dd0 to 397a5a8 Compare May 9, 2026 10:40
@spalagu
Copy link
Copy Markdown
Author

spalagu commented May 9, 2026

Addressed concern #1 (RepoNotFound in cwd-inside-repo case): refresh_directory now resolves the indexed repository root via the existing RepoMetadataModel::find_repository_for_path before calling load_directory, so the displayed file-tree root being a subdirectory of an indexed repo no longer fails the call. Falls back to the displayed root for standalone lazy-loaded paths where the two coincide.

Force-pushed a squashed single commit (397a5a80) capturing the original menu wiring + the indexed-root resolution fix.

Concern #2 (visual evidence): I'll attach a screenshot/recording in a follow-up comment shortly.

/oz-review

@spalagu
Copy link
Copy Markdown
Author

spalagu commented May 11, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 11, 2026

@spalagu

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR adds a directory-only Refresh action to the file tree context menu and routes it through the existing local repo metadata load_directory path.

Concerns

  • refresh_directory calls the existing load_directory path for a loaded directory, but that path overlays rebuilt entries without clearing stale child mappings first. A refresh can therefore leave removed entries visible instead of reconciling the directory with disk.
  • Manual testing evidence is missing for this user-visible context-menu change. Please include screenshots or a screen recording that show the menu entry and refresh behavior end to end, or justify why visual evidence is not possible.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

.find_repository_for_path(&local_path, ctx)
.unwrap_or_else(|| id.root.clone());
self.repository_metadata_model.update(ctx, |model, ctx| {
if let Err(err) = model.load_directory(&repo_root, &dir_path, ctx) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] load_directory overlays the rebuilt subtree without clearing stale child mappings first. For refresh, this leaves removed entries visible when the directory becomes empty or when expanded child directories had old descendants; replace or remove the existing subtree before loading so the UI reconciles deletions.

Adds a Refresh menu item under directory headers in the file tree
right-click context menu. Re-reads the clicked directory's contents
from disk (one level deep, matching lazy-load behavior) via the
existing LocalRepoMetadataModel::load_directory path.

Mitigates warpdotdev#10205 (and the related warpdotdev#9190, warpdotdev#9890) — Project Explorer
not reflecting filesystem changes — by giving users a deliberate way
to reconcile the in-memory tree with disk after watcher events drop
or apply_file_tree_mutations silently skips a mutation under
lazy_load when a parent's loaded flag is false.

Surgical: only the targeted subtree is re-scanned, avoiding the cost
of re-indexing the entire root.

Resolves the indexed repository root via the existing wrapper
RepoMetadataModel::find_repository_for_path before dispatching to
load_directory, since the displayed file-tree root (id.root) can be a
subdirectory of the indexed repository in cwd-inside-repo cases —
passing the displayed root would return RepoNotFound and refresh
would silently fail. Falls back to the displayed root for standalone
lazy-loaded paths where the two coincide.

Also fixes FileTreeState::load_at_path to clear stale entries before
re-inserting. Previously it called insert_entry_at_path which only
`extend`s state_map and parent_to_child_map, leaving entries for
files that were deleted on disk between loads still visible in the
tree. The fix is one line — `self.remove(path)` before insert. Safe
for the existing first-time-expand callers (view.rs:1375 / 1413,
server_model.rs:1167) because their pre-load entry is just an
unloaded placeholder with no descendants in the maps, so the remove
is a no-op for them.

This does NOT fix the underlying watcher event drop in
apply_file_tree_mutations, nor does it implement the Command Palette
action requested in warpdotdev#10003 (which is tracked separately by spec
PR warpdotdev#10025). Both are intentionally scoped out — this PR is only the
right-click escape hatch.
@spalagu spalagu force-pushed the fix/file-tree-refresh-menu branch from 397a5a8 to 1042ccf Compare May 11, 2026 06:02
@spalagu
Copy link
Copy Markdown
Author

spalagu commented May 11, 2026

Addressed the stale-entry concern. Added one line to FileTreeState::load_at_path (crates/repo_metadata/src/file_tree_store/file_tree_state.rs):

entry.load(gitignores)?;
self.remove(path);   // ← NEW: clear stale entries before re-inserting
self.insert_entry_at_path(child_path, entry);

The bot is correct that insert_entry_at_path only extends the maps without removing prior entries — a refresh of a directory whose contents shrank on disk would leave deleted files visible. FileTreeState::remove (line 83) recursively removes the path's entries plus all descendants, which is exactly what we need before laying down the freshly-built subtree.

Verified safe for the three existing callers of load_at_path (view.rs:1375 / view.rs:1413 / server_model.rs:1167) — they all hit the path on first-time-expand when the prior entry is just an unloaded placeholder (loaded: false, no descendants in the maps), so the new remove is a no-op for them.

Force-pushed a single squashed commit (1042ccfd).

Visual evidence still pending — will follow up.

/oz-review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project Explorer does not refresh to show newly created files and folders

1 participant