Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions app/src/sftp_manager/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ use super::types::{
/// same verbs are reachable by keyboard (pros) and click (beginners). F5/F6
/// cross-pane copy/move light up fully once a second file panel exists.
const FUNCTION_BAR: &[(&str, &str, fn() -> SftpBrowserAction)] = &[
("F3", "View", || SftpBrowserAction::ActivateCursor),
("F4", "Edit", || SftpBrowserAction::ActivateCursor),
("F3", "View", || SftpBrowserAction::OpenCursorInEditor),
("F4", "Edit", || SftpBrowserAction::OpenCursorInEditor),
("F5", "Copy", || SftpBrowserAction::CopyToOtherPane),
("F6", "Move", || SftpBrowserAction::MoveToOtherPane),
("F7", "MkDir", || SftpBrowserAction::CreateFolder),
Expand Down Expand Up @@ -157,9 +157,12 @@ pub enum SftpBrowserAction {
CursorPageUp,
/// Move the cursor down one page.
CursorPageDown,
/// Activate the row under the cursor (Enter / F3): a directory is entered,
/// a file is opened (details).
/// Activate the row under the cursor (Enter): a directory is entered, a
/// file is downloaded.
ActivateCursor,
/// Open the row under the cursor in the code editor (F3 View / F4 Edit): a
/// directory is entered; a file opens in an editor pane.
OpenCursorInEditor,
/// Enter the directory under the cursor (Right arrow); no-op on a file.
EnterCursorDir,
/// Toggle the row under the cursor in the multi-selection (Space).
Expand Down Expand Up @@ -807,6 +810,30 @@ impl SftpBrowserView {
}
}

/// F3/F4: open the row under the cursor in the code editor. A directory is
/// entered; a file is opened in an editor pane (view + edit). The workspace
/// resolves local vs remote (`node_id`).
fn open_cursor_in_editor(&mut self, ctx: &mut ViewContext<Self>) {
let Some(index) = self.cursor_entry_index() else {
return;
};
let Some(entry) = self.entries.get(index) else {
return;
};
if matches!(
entry.file_type,
FileEntryType::Directory | FileEntryType::Symlink
) {
let path = entry.path.clone();
self.navigate_to(path, ctx);
return;
}
ctx.dispatch_typed_action(&crate::WorkspaceAction::OpenFileInEditor {
node_id: self.node_id.clone(),
path: entry.path.clone(),
});
}

/// Enter the directory under the cursor (Right arrow); a file is a no-op.
fn enter_cursor_dir(&mut self, ctx: &mut ViewContext<Self>) {
if let Some(index) = self.cursor_entry_index() {
Expand Down Expand Up @@ -2541,6 +2568,7 @@ impl TypedActionView for SftpBrowserView {
self.move_cursor(CursorMove::PageDown(page), ctx);
}
SftpBrowserAction::ActivateCursor => self.activate_cursor(ctx),
SftpBrowserAction::OpenCursorInEditor => self.open_cursor_in_editor(ctx),
SftpBrowserAction::EnterCursorDir => self.enter_cursor_dir(ctx),
SftpBrowserAction::ToggleSelectCursor => self.toggle_select_cursor(ctx),
SftpBrowserAction::RenameCursor => self.rename_cursor(ctx),
Expand Down Expand Up @@ -2810,8 +2838,8 @@ impl View for SftpBrowserView {
// Rename (F2, the common file-manager convention; MC's F6
// is repurposed here for the cross-pane move).
"f2" => Some(SftpBrowserAction::RenameCursor),
// Function-key bar (MC parity)
"f3" | "f4" => Some(SftpBrowserAction::ActivateCursor),
// Function-key bar (MC parity): F3 View / F4 Edit open the editor.
"f3" | "f4" => Some(SftpBrowserAction::OpenCursorInEditor),
"f5" => Some(SftpBrowserAction::CopyToOtherPane),
"f6" => Some(SftpBrowserAction::MoveToOtherPane),
"f7" => Some(SftpBrowserAction::CreateFolder),
Expand Down
22 changes: 22 additions & 0 deletions app/src/sftp_manager/browser_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2209,3 +2209,25 @@ fn test_copy_conflict_overwrite_all_applies_to_batch() {
assert_eq!(std::fs::read(root.join("right/b.txt")).unwrap(), b"B2");
});
}

// ============================================================
// F3/F4 open-in-editor (FM Pflicht 2 — directory branch)
// ============================================================

/// F3/F4 on a directory enters it (the file branch dispatches a workspace
/// action, which the file-manager harness can't observe here).
#[test]
fn test_open_in_editor_on_directory_navigates() {
warpui::App::test((), |mut app| async move {
initialize_app(&mut app);
let (_, view, _temp) = create_connected_view(&mut app, &[("subdir/inner.txt", b"x")]);

// /subdir is the only entry → cursor on it → F3/F4 enters it.
view.update(&mut app, |v, ctx| {
v.handle_action(&SftpBrowserAction::OpenCursorInEditor, ctx);
});
view.read(&app, |v, _| {
assert_eq!(v.current_path, PathBuf::from("/subdir"));
});
});
}
1 change: 1 addition & 0 deletions app/src/sftp_manager/context_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ pub fn render_context_menu(state: &ContextMenuState, appearance: &Appearance) ->
| SftpBrowserAction::CloseFileManager
| SftpBrowserAction::OverwriteConflict { .. }
| SftpBrowserAction::SkipConflict { .. }
| SftpBrowserAction::OpenCursorInEditor
| SftpBrowserAction::CancelTransfer(_)
| SftpBrowserAction::ToggleTransferPanel
| SftpBrowserAction::ConfirmCloseTransferPanel => "sftp_ctx:unknown",
Expand Down
9 changes: 9 additions & 0 deletions app/src/workspace/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,14 @@ pub enum WorkspaceAction {
/// Isolate the fork's file effects in a fresh sibling worktree.
into_worktree: bool,
},
/// Open a file from the file manager in a code editor pane. `node_id` empty
/// = a local file (opened directly); non-empty = a remote host — native
/// remote editing (buffer-sync via the SSH daemon) is a follow-up, so a
/// remote path is reported rather than silently downloaded.
OpenFileInEditor {
node_id: String,
path: PathBuf,
},
FixSettingsWithOz {
error_description: String,
},
Expand Down Expand Up @@ -882,6 +890,7 @@ impl WorkspaceAction {
| OpenSettingsFile
| AskAgent { .. }
| ForkAgentSession { .. }
| OpenFileInEditor { .. }
| FixSettingsWithOz { .. } => false,
#[cfg(debug_assertions)]
ShowHoaOnboardingFlow => false,
Expand Down
19 changes: 19 additions & 0 deletions app/src/workspace/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19733,6 +19733,25 @@ impl TypedActionView for Workspace {
ctx,
);
}
OpenFileInEditor { node_id, path } => {
if node_id.is_empty() {
// Local file: open it directly in a code pane (view + edit).
self.add_tab_for_code_file(path.clone(), None, ctx);
} else {
// Remote file: native editing (buffer-sync via the SSH daemon)
// needs the host's daemon connection + HostId; not yet wired
// from the SFTP file manager. Report honestly rather than
// download-and-edit a local copy that wouldn't save back.
let message = format!(
"Opening remote files in the editor is coming — {} is on {node_id}. \
Use the file manager's Download for now.",
path.display()
);
self.toast_stack.update(ctx, |view, ctx| {
view.add_ephemeral_toast(DismissibleToast::error(message), ctx);
});
}
}
FixSettingsWithOz { error_description } => {
// Repurposed (Oz-repurpose P1): the fix goes to the USER's own
// CLI coding agent, not the retired in-app agent mode.
Expand Down
Loading