@@ -29,9 +29,9 @@ use util::{ResultExt, TryFutureExt};
use workspace::{
ActivateNextPane, ActivatePane, ActivatePaneDown, ActivatePaneLeft, ActivatePaneRight,
ActivatePaneUp, ActivatePreviousPane, DraggedSelection, DraggedTab, ItemId, MoveItemToPane,
- MoveItemToPaneInDirection, MovePaneDown, MovePaneLeft, MovePaneRight, MovePaneUp, NewTerminal,
- Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft, SplitMode, SplitRight, SplitUp,
- SwapPaneDown, SwapPaneLeft, SwapPaneRight, SwapPaneUp, ToggleZoom, Workspace,
+ MoveItemToPaneInDirection, MovePaneDown, MovePaneLeft, MovePaneRight, MovePaneUp, Pane,
+ PaneGroup, SplitDirection, SplitDown, SplitLeft, SplitMode, SplitRight, SplitUp, SwapPaneDown,
+ SwapPaneLeft, SwapPaneRight, SwapPaneUp, ToggleZoom, Workspace,
dock::{DockPosition, Panel, PanelEvent, PanelHandle},
item::SerializableItem,
move_active_item, move_item, pane,
@@ -161,7 +161,7 @@ impl TerminalPanel {
menu.context(focus_handle.clone())
.action(
"New Terminal",
- workspace::NewTerminal.boxed_clone(),
+ workspace::NewTerminal::default().boxed_clone(),
)
// We want the focus to go back to terminal panel once task modal is dismissed,
// hence we focus that first. Otherwise, we'd end up without a focused element, as
@@ -524,12 +524,16 @@ impl TerminalPanel {
terminal_panel
.update(cx, |panel, cx| {
- panel.add_terminal_shell(
- Some(action.working_directory.clone()),
- RevealStrategy::Always,
- window,
- cx,
- )
+ if action.local {
+ panel.add_local_terminal_shell(RevealStrategy::Always, window, cx)
+ } else {
+ panel.add_terminal_shell(
+ Some(action.working_directory.clone()),
+ RevealStrategy::Always,
+ window,
+ cx,
+ )
+ }
})
.detach_and_log_err(cx);
}
@@ -649,7 +653,7 @@ impl TerminalPanel {
/// Create a new Terminal in the current working directory or the user's home directory
fn new_terminal(
workspace: &mut Workspace,
- _: &workspace::NewTerminal,
+ action: &workspace::NewTerminal,
window: &mut Window,
cx: &mut Context<Workspace>,
) {
@@ -659,12 +663,16 @@ impl TerminalPanel {
terminal_panel
.update(cx, |this, cx| {
- this.add_terminal_shell(
- default_working_directory(workspace, cx),
- RevealStrategy::Always,
- window,
- cx,
- )
+ if action.local {
+ this.add_local_terminal_shell(RevealStrategy::Always, window, cx)
+ } else {
+ this.add_terminal_shell(
+ default_working_directory(workspace, cx),
+ RevealStrategy::Always,
+ window,
+ cx,
+ )
+ }
})
.detach_and_log_err(cx);
}
@@ -824,6 +832,26 @@ impl TerminalPanel {
reveal_strategy: RevealStrategy,
window: &mut Window,
cx: &mut Context<Self>,
+ ) -> Task<Result<WeakEntity<Terminal>>> {
+ self.add_terminal_shell_internal(false, cwd, reveal_strategy, window, cx)
+ }
+
+ fn add_local_terminal_shell(
+ &mut self,
+ reveal_strategy: RevealStrategy,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<WeakEntity<Terminal>>> {
+ self.add_terminal_shell_internal(true, None, reveal_strategy, window, cx)
+ }
+
+ fn add_terminal_shell_internal(
+ &mut self,
+ force_local: bool,
+ cwd: Option<PathBuf>,
+ reveal_strategy: RevealStrategy,
+ window: &mut Window,
+ cx: &mut Context<Self>,
) -> Task<Result<WeakEntity<Terminal>>> {
let workspace = self.workspace.clone();
@@ -836,9 +864,15 @@ impl TerminalPanel {
terminal_panel.active_pane.clone()
})?;
let project = workspace.read_with(cx, |workspace, _| workspace.project().clone())?;
- let terminal = project
- .update(cx, |project, cx| project.create_terminal_shell(cwd, cx))
- .await;
+ let terminal = if force_local {
+ project
+ .update(cx, |project, cx| project.create_local_terminal(cx))
+ .await
+ } else {
+ project
+ .update(cx, |project, cx| project.create_terminal_shell(cwd, cx))
+ .await
+ };
match terminal {
Ok(terminal) => {
@@ -1120,7 +1154,7 @@ pub fn new_terminal_pane(
project.clone(),
Default::default(),
None,
- NewTerminal.boxed_clone(),
+ workspace::NewTerminal::default().boxed_clone(),
false,
window,
cx,
@@ -1980,6 +2014,37 @@ mod tests {
.unwrap();
}
+ #[gpui::test]
+ async fn test_local_terminal_in_local_project(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, [], cx).await;
+ let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
+
+ let (window_handle, terminal_panel) = workspace
+ .update(cx, |workspace, window, cx| {
+ let window_handle = window.window_handle();
+ let terminal_panel = cx.new(|cx| TerminalPanel::new(workspace, window, cx));
+ (window_handle, terminal_panel)
+ })
+ .unwrap();
+
+ let result = window_handle
+ .update(cx, |_, window, cx| {
+ terminal_panel.update(cx, |terminal_panel, cx| {
+ terminal_panel.add_local_terminal_shell(RevealStrategy::Always, window, cx)
+ })
+ })
+ .unwrap()
+ .await;
+
+ assert!(
+ result.is_ok(),
+ "local terminal should successfully create in local project"
+ );
+ }
+
fn set_max_tabs(cx: &mut TestAppContext, value: Option<usize>) {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings(cx, |settings| {
@@ -85,7 +85,7 @@ actions!(
terminal,
[
/// Reruns the last executed task in the terminal.
- RerunTask
+ RerunTask,
]
);
@@ -194,13 +194,18 @@ impl TerminalView {
///Create a new Terminal in the current working directory or the user's home directory
pub fn deploy(
workspace: &mut Workspace,
- _: &NewCenterTerminal,
+ action: &NewCenterTerminal,
window: &mut Window,
cx: &mut Context<Workspace>,
) {
+ let local = action.local;
let working_directory = default_working_directory(workspace, cx);
- TerminalPanel::add_center_terminal(workspace, window, cx, |project, cx| {
- project.create_terminal_shell(working_directory, cx)
+ TerminalPanel::add_center_terminal(workspace, window, cx, move |project, cx| {
+ if local {
+ project.create_local_terminal(cx)
+ } else {
+ project.create_terminal_shell(working_directory, cx)
+ }
})
.detach_and_log_err(cx);
}
@@ -389,7 +394,7 @@ impl TerminalView {
.is_some_and(|terminal_panel| terminal_panel.read(cx).assistant_enabled());
let context_menu = ContextMenu::build(window, cx, |menu, _, _| {
menu.context(self.focus_handle.clone())
- .action("New Terminal", Box::new(NewTerminal))
+ .action("New Terminal", Box::new(NewTerminal::default()))
.separator()
.action("Copy", Box::new(Copy))
.action("Paste", Box::new(Paste))
@@ -231,8 +231,6 @@ actions!(
FollowNextCollaborator,
/// Moves the focused panel to the next position.
MoveFocusedPanelToNextPosition,
- /// Opens a new terminal in the center.
- NewCenterTerminal,
/// Creates a new file.
NewFile,
/// Creates a new file in a vertical split.
@@ -241,8 +239,6 @@ actions!(
NewFileSplitHorizontal,
/// Opens a new search.
NewSearch,
- /// Opens a new terminal.
- NewTerminal,
/// Opens a new window.
NewWindow,
/// Opens a file or directory.
@@ -406,6 +402,26 @@ pub struct ToggleFileFinder {
pub separate_history: bool,
}
+/// Opens a new terminal in the center.
+#[derive(Default, PartialEq, Eq, Clone, Deserialize, JsonSchema, Action)]
+#[action(namespace = workspace)]
+#[serde(deny_unknown_fields)]
+pub struct NewCenterTerminal {
+ /// If true, creates a local terminal even in remote projects.
+ #[serde(default)]
+ pub local: bool,
+}
+
+/// Opens a new terminal.
+#[derive(Default, PartialEq, Eq, Clone, Deserialize, JsonSchema, Action)]
+#[action(namespace = workspace)]
+#[serde(deny_unknown_fields)]
+pub struct NewTerminal {
+ /// If true, creates a local terminal even in remote projects.
+ #[serde(default)]
+ pub local: bool,
+}
+
/// Increases size of a currently focused dock by a given amount of pixels.
#[derive(Clone, PartialEq, Deserialize, JsonSchema, Action)]
#[action(namespace = workspace)]
@@ -535,6 +551,9 @@ impl PartialEq for Toast {
#[serde(deny_unknown_fields)]
pub struct OpenTerminal {
pub working_directory: PathBuf,
+ /// If true, creates a local terminal even in remote projects.
+ #[serde(default)]
+ pub local: bool,
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]