Detailed changes
@@ -131,10 +131,10 @@ use ui::{
use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
use workspace::item::ItemHandle;
use workspace::notifications::NotificationId;
-use workspace::Toast;
use workspace::{
searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
};
+use workspace::{OpenInTerminal, OpenTerminal, Toast};
use crate::hover_links::find_url;
@@ -4943,6 +4943,25 @@ impl Editor {
}
}
+ pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
+ if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
+ let project_path = buffer.read(cx).project_path(cx)?;
+ let project = self.project.as_ref()?.read(cx);
+ let entry = project.entry_for_path(&project_path, cx)?;
+ let abs_path = project.absolute_path(&project_path, cx)?;
+ let parent = if entry.is_symlink {
+ abs_path.canonicalize().ok()?
+ } else {
+ abs_path
+ }
+ .parent()?
+ .to_path_buf();
+ Some(parent)
+ }) {
+ cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
+ }
+ }
+
fn gather_revert_changes(
&mut self,
selections: &[Selection<Anchor>],
@@ -363,6 +363,7 @@ impl EditorElement {
register_action(view, cx, Editor::unique_lines_case_sensitive);
register_action(view, cx, Editor::accept_partial_inline_completion);
register_action(view, cx, Editor::revert_selected_hunks);
+ register_action(view, cx, Editor::open_active_item_in_terminal)
}
fn register_key_listeners(&self, cx: &mut ElementContext, layout: &EditorLayout) {
@@ -3,6 +3,7 @@ use crate::{
GoToTypeDefinition, Rename, RevealInFinder, SelectMode, ToggleCodeActions,
};
use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
+use workspace::OpenInTerminal;
pub struct MouseContextMenu {
pub(crate) position: Point<Pixels>,
@@ -83,6 +84,7 @@ pub fn deploy_context_menu(
)
.separator()
.action("Reveal in Finder", Box::new(RevealInFinder))
+ .action("Open in Terminal", Box::new(OpenInTerminal))
})
};
let mouse_context_menu = MouseContextMenu::new(position, context_menu, cx);
@@ -37,7 +37,7 @@ use util::{maybe, NumericPrefixWithSuffix, ResultExt, TryFutureExt};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
notifications::DetachAndPromptErr,
- Workspace,
+ OpenInTerminal, Workspace,
};
const PROJECT_PANEL_KEY: &str = "ProjectPanel";
@@ -127,7 +127,6 @@ actions!(
CopyPath,
CopyRelativePath,
RevealInFinder,
- OpenInTerminal,
Cut,
Paste,
Rename,
@@ -441,9 +440,7 @@ impl ProjectPanel {
.action("New Folder", Box::new(NewDirectory))
.separator()
.action("Reveal in Finder", Box::new(RevealInFinder))
- .when(is_dir, |menu| {
- menu.action("Open in Terminalβ¦", Box::new(OpenInTerminal))
- })
+ .action("Open in Terminal", Box::new(OpenInTerminal))
.when(is_dir, |menu| {
menu.separator()
.action("Find in Folderβ¦", Box::new(NewSearchInDirectory))
@@ -1131,13 +1128,20 @@ impl ProjectPanel {
fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
if let Some((worktree, entry)) = self.selected_entry(cx) {
- let path = worktree.abs_path().join(&entry.path);
- cx.dispatch_action(
- workspace::OpenTerminal {
- working_directory: path,
+ let abs_path = worktree.abs_path().join(&entry.path);
+ let working_directory = if entry.is_dir() {
+ Some(abs_path)
+ } else {
+ if entry.is_symlink {
+ abs_path.canonicalize().ok()
+ } else {
+ Some(abs_path)
}
- .boxed_clone(),
- )
+ .and_then(|path| Some(path.parent()?.to_path_buf()))
+ };
+ if let Some(working_directory) = working_directory {
+ cx.dispatch_action(workspace::OpenTerminal { working_directory }.boxed_clone())
+ }
}
}
@@ -98,7 +98,6 @@ impl TerminalPanel {
.on_click(cx.listener(|pane, _, cx| {
pane.toggle_zoom(&workspace::ToggleZoom, cx);
}))
- // TODO kb
.tooltip(move |cx| {
Tooltip::for_action(
if zoomed { "Zoom Out" } else { "Zoom In" },
@@ -292,13 +291,13 @@ impl TerminalPanel {
action: &workspace::OpenTerminal,
cx: &mut ViewContext<Workspace>,
) {
- let Some(this) = workspace.focus_panel::<Self>(cx) else {
+ let Some(terminal_panel) = workspace.panel::<Self>(cx) else {
return;
};
-
- this.update(cx, |this, cx| {
- this.add_terminal(Some(action.working_directory.clone()), None, cx)
- })
+ terminal_panel.update(cx, |panel, cx| {
+ panel.add_terminal(Some(action.working_directory.clone()), None, cx)
+ });
+ workspace.focus_panel::<Self>(cx);
}
fn spawn_task(&mut self, spawn_in_terminal: &SpawnInTerminal, cx: &mut ViewContext<Self>) {
@@ -427,26 +426,17 @@ impl TerminalPanel {
}
}
- ///Create a new Terminal in the current working directory or the user's home directory
+ /// Create a new Terminal in the current working directory or the user's home directory
fn new_terminal(
workspace: &mut Workspace,
_: &workspace::NewTerminal,
cx: &mut ViewContext<Workspace>,
) {
- let has_no_terminals = workspace
- .panel::<Self>(cx)
- .map(|terminal_panel| terminal_panel.update(cx, |panel, cx| panel.has_no_terminals(cx)))
- .unwrap_or(true);
- let Some(this) = workspace.focus_panel::<Self>(cx) else {
+ let Some(terminal_panel) = workspace.panel::<Self>(cx) else {
return;
};
- if has_no_terminals {
- // `set_active` on focus, will already add a new terminal
- // into an empty terminal pane, no need to add another one
- return;
- }
-
- this.update(cx, |this, cx| this.add_terminal(None, None, cx))
+ terminal_panel.update(cx, |this, cx| this.add_terminal(None, None, cx));
+ workspace.focus_panel::<Self>(cx);
}
fn terminals_for_task(
@@ -5,7 +5,8 @@ use crate::{
},
toolbar::Toolbar,
workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},
- NewCenterTerminal, NewFile, NewSearch, OpenVisible, SplitDirection, ToggleZoom, Workspace,
+ NewCenterTerminal, NewFile, NewSearch, OpenInTerminal, OpenTerminal, OpenVisible,
+ SplitDirection, ToggleZoom, Workspace,
};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
@@ -1597,20 +1598,58 @@ impl Pane {
);
if let Some(entry) = single_entry_to_resolve {
+ let parent_abs_path = pane
+ .update(cx, |pane, cx| {
+ pane.workspace.update(cx, |workspace, cx| {
+ let project = workspace.project().read(cx);
+ project.worktree_for_entry(entry, cx).and_then(|worktree| {
+ let worktree = worktree.read(cx);
+ let entry = worktree.entry_for_id(entry)?;
+ let abs_path = worktree.absolutize(&entry.path).ok()?;
+ let parent = if entry.is_symlink {
+ abs_path.canonicalize().ok()?
+ } else {
+ abs_path
+ }
+ .parent()?
+ .to_path_buf();
+ Some(parent)
+ })
+ })
+ })
+ .ok()
+ .flatten();
+
let entry_id = entry.to_proto();
- menu = menu.separator().entry(
- "Reveal In Project Panel",
- Some(Box::new(RevealInProjectPanel {
- entry_id: Some(entry_id),
- })),
- cx.handler_for(&pane, move |pane, cx| {
- pane.project.update(cx, |_, cx| {
- cx.emit(project::Event::RevealInProjectPanel(
- ProjectEntryId::from_proto(entry_id),
- ))
- });
- }),
- );
+ menu = menu
+ .separator()
+ .entry(
+ "Reveal In Project Panel",
+ Some(Box::new(RevealInProjectPanel {
+ entry_id: Some(entry_id),
+ })),
+ cx.handler_for(&pane, move |pane, cx| {
+ pane.project.update(cx, |_, cx| {
+ cx.emit(project::Event::RevealInProjectPanel(
+ ProjectEntryId::from_proto(entry_id),
+ ))
+ });
+ }),
+ )
+ .when_some(parent_abs_path, |menu, abs_path| {
+ menu.entry(
+ "Open in Terminal",
+ Some(Box::new(OpenInTerminal)),
+ cx.handler_for(&pane, move |_, cx| {
+ cx.dispatch_action(
+ OpenTerminal {
+ working_directory: abs_path.clone(),
+ }
+ .boxed_clone(),
+ );
+ }),
+ )
+ });
}
}
@@ -112,6 +112,7 @@ actions!(
workspace,
[
Open,
+ OpenInTerminal,
NewFile,
NewWindow,
CloseWindow,