Allow ssh connection for setting up zed (#12063)
Co-Authored-By: Mikayla <mikayla@zed.dev> Release Notes: - Magic `ssh` login feature for remote development --------- Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Nate Butler <iamnbutler@gmail.com>
This commit is contained in:
parent
3382e79ef9
commit
e5b9e2044e
29 changed files with 1242 additions and 785 deletions
|
@ -6,16 +6,19 @@ use db::kvp::KEY_VALUE_STORE;
|
|||
use futures::future::join_all;
|
||||
use gpui::{
|
||||
actions, Action, AppContext, AsyncWindowContext, DismissEvent, Entity, EventEmitter,
|
||||
ExternalPaths, FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled,
|
||||
Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
ExternalPaths, FocusHandle, FocusableView, IntoElement, Model, ParentElement, Pixels, Render,
|
||||
Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use project::{Fs, ProjectEntryId};
|
||||
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use task::{RevealStrategy, SpawnInTerminal, TaskId};
|
||||
use terminal::terminal_settings::{Shell, TerminalDockPosition, TerminalSettings};
|
||||
use task::{RevealStrategy, SpawnInTerminal, TaskId, TerminalWorkDir};
|
||||
use terminal::{
|
||||
terminal_settings::{Shell, TerminalDockPosition, TerminalSettings},
|
||||
Terminal,
|
||||
};
|
||||
use ui::{
|
||||
h_flex, ButtonCommon, Clickable, ContextMenu, FluentBuilder, IconButton, IconSize, Selectable,
|
||||
Tooltip,
|
||||
|
@ -319,14 +322,16 @@ impl TerminalPanel {
|
|||
return;
|
||||
};
|
||||
|
||||
terminal_panel.update(cx, |panel, cx| {
|
||||
panel.add_terminal(
|
||||
Some(action.working_directory.clone()),
|
||||
None,
|
||||
RevealStrategy::Always,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let terminal_work_dir = workspace
|
||||
.project()
|
||||
.read(cx)
|
||||
.terminal_work_dir_for(Some(&action.working_directory), cx);
|
||||
|
||||
terminal_panel
|
||||
.update(cx, |panel, cx| {
|
||||
panel.add_terminal(terminal_work_dir, None, RevealStrategy::Always, cx)
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn spawn_task(&mut self, spawn_in_terminal: &SpawnInTerminal, cx: &mut ViewContext<Self>) {
|
||||
|
@ -355,18 +360,19 @@ impl TerminalPanel {
|
|||
let spawn_task = spawn_task;
|
||||
|
||||
let reveal = spawn_task.reveal;
|
||||
let working_directory = spawn_in_terminal.cwd.clone();
|
||||
let allow_concurrent_runs = spawn_in_terminal.allow_concurrent_runs;
|
||||
let use_new_terminal = spawn_in_terminal.use_new_terminal;
|
||||
|
||||
if allow_concurrent_runs && use_new_terminal {
|
||||
self.spawn_in_new_terminal(spawn_task, working_directory, cx);
|
||||
self.spawn_in_new_terminal(spawn_task, cx)
|
||||
.detach_and_log_err(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
let terminals_for_task = self.terminals_for_task(&spawn_in_terminal.full_label, cx);
|
||||
if terminals_for_task.is_empty() {
|
||||
self.spawn_in_new_terminal(spawn_task, working_directory, cx);
|
||||
self.spawn_in_new_terminal(spawn_task, cx)
|
||||
.detach_and_log_err(cx);
|
||||
return;
|
||||
}
|
||||
let (existing_item_index, existing_terminal) = terminals_for_task
|
||||
|
@ -378,13 +384,7 @@ impl TerminalPanel {
|
|||
!use_new_terminal,
|
||||
"Should have handled 'allow_concurrent_runs && use_new_terminal' case above"
|
||||
);
|
||||
self.replace_terminal(
|
||||
working_directory,
|
||||
spawn_task,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
cx,
|
||||
);
|
||||
self.replace_terminal(spawn_task, existing_item_index, existing_terminal, cx);
|
||||
} else {
|
||||
self.deferred_tasks.insert(
|
||||
spawn_in_terminal.id.clone(),
|
||||
|
@ -393,14 +393,11 @@ impl TerminalPanel {
|
|||
terminal_panel
|
||||
.update(&mut cx, |terminal_panel, cx| {
|
||||
if use_new_terminal {
|
||||
terminal_panel.spawn_in_new_terminal(
|
||||
spawn_task,
|
||||
working_directory,
|
||||
cx,
|
||||
);
|
||||
terminal_panel
|
||||
.spawn_in_new_terminal(spawn_task, cx)
|
||||
.detach_and_log_err(cx);
|
||||
} else {
|
||||
terminal_panel.replace_terminal(
|
||||
working_directory,
|
||||
spawn_task,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
|
@ -428,14 +425,13 @@ impl TerminalPanel {
|
|||
}
|
||||
}
|
||||
|
||||
fn spawn_in_new_terminal(
|
||||
pub fn spawn_in_new_terminal(
|
||||
&mut self,
|
||||
spawn_task: SpawnInTerminal,
|
||||
working_directory: Option<PathBuf>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
) -> Task<Result<Model<Terminal>>> {
|
||||
let reveal = spawn_task.reveal;
|
||||
self.add_terminal(working_directory, Some(spawn_task), reveal, cx);
|
||||
self.add_terminal(spawn_task.cwd.clone(), Some(spawn_task), reveal, cx)
|
||||
}
|
||||
|
||||
/// Create a new Terminal in the current working directory or the user's home directory
|
||||
|
@ -448,9 +444,11 @@ impl TerminalPanel {
|
|||
return;
|
||||
};
|
||||
|
||||
terminal_panel.update(cx, |this, cx| {
|
||||
this.add_terminal(None, None, RevealStrategy::Always, cx)
|
||||
});
|
||||
terminal_panel
|
||||
.update(cx, |this, cx| {
|
||||
this.add_terminal(None, None, RevealStrategy::Always, cx)
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn terminals_for_task(
|
||||
|
@ -482,17 +480,17 @@ impl TerminalPanel {
|
|||
|
||||
fn add_terminal(
|
||||
&mut self,
|
||||
working_directory: Option<PathBuf>,
|
||||
working_directory: Option<TerminalWorkDir>,
|
||||
spawn_task: Option<SpawnInTerminal>,
|
||||
reveal_strategy: RevealStrategy,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
) -> Task<Result<Model<Terminal>>> {
|
||||
let workspace = self.workspace.clone();
|
||||
self.pending_terminals_to_add += 1;
|
||||
|
||||
cx.spawn(|terminal_panel, mut cx| async move {
|
||||
let pane = terminal_panel.update(&mut cx, |this, _| this.pane.clone())?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let result = workspace.update(&mut cx, |workspace, cx| {
|
||||
let working_directory = if let Some(working_directory) = working_directory {
|
||||
Some(working_directory)
|
||||
} else {
|
||||
|
@ -502,35 +500,33 @@ impl TerminalPanel {
|
|||
};
|
||||
|
||||
let window = cx.window_handle();
|
||||
if let Some(terminal) = workspace.project().update(cx, |project, cx| {
|
||||
project
|
||||
.create_terminal(working_directory, spawn_task, window, cx)
|
||||
.log_err()
|
||||
}) {
|
||||
let terminal = Box::new(cx.new_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal,
|
||||
workspace.weak_handle(),
|
||||
workspace.database_id(),
|
||||
cx,
|
||||
)
|
||||
}));
|
||||
pane.update(cx, |pane, cx| {
|
||||
let focus = pane.has_focus(cx);
|
||||
pane.add_item(terminal, true, focus, None, cx);
|
||||
});
|
||||
}
|
||||
let terminal = workspace.project().update(cx, |project, cx| {
|
||||
project.create_terminal(working_directory, spawn_task, window, cx)
|
||||
})?;
|
||||
let terminal_view = Box::new(cx.new_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal.clone(),
|
||||
workspace.weak_handle(),
|
||||
workspace.database_id(),
|
||||
cx,
|
||||
)
|
||||
}));
|
||||
pane.update(cx, |pane, cx| {
|
||||
let focus = pane.has_focus(cx);
|
||||
pane.add_item(terminal_view, true, focus, None, cx);
|
||||
});
|
||||
|
||||
if reveal_strategy == RevealStrategy::Always {
|
||||
workspace.focus_panel::<Self>(cx);
|
||||
}
|
||||
Ok(terminal)
|
||||
})?;
|
||||
terminal_panel.update(&mut cx, |this, cx| {
|
||||
this.pending_terminals_to_add = this.pending_terminals_to_add.saturating_sub(1);
|
||||
this.serialize(cx)
|
||||
})?;
|
||||
anyhow::Ok(())
|
||||
result
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn serialize(&mut self, cx: &mut ViewContext<Self>) {
|
||||
|
@ -579,7 +575,6 @@ impl TerminalPanel {
|
|||
|
||||
fn replace_terminal(
|
||||
&self,
|
||||
working_directory: Option<PathBuf>,
|
||||
spawn_task: SpawnInTerminal,
|
||||
terminal_item_index: usize,
|
||||
terminal_to_replace: View<TerminalView>,
|
||||
|
@ -594,7 +589,7 @@ impl TerminalPanel {
|
|||
let window = cx.window_handle();
|
||||
let new_terminal = project.update(cx, |project, cx| {
|
||||
project
|
||||
.create_terminal(working_directory, Some(spawn_task), window, cx)
|
||||
.create_terminal(spawn_task.cwd.clone(), Some(spawn_task), window, cx)
|
||||
.log_err()
|
||||
})?;
|
||||
terminal_to_replace.update(cx, |terminal_to_replace, cx| {
|
||||
|
@ -738,7 +733,8 @@ impl Panel for TerminalPanel {
|
|||
|
||||
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
||||
if active && self.has_no_terminals(cx) {
|
||||
self.add_terminal(None, None, RevealStrategy::Never, cx);
|
||||
self.add_terminal(None, None, RevealStrategy::Never, cx)
|
||||
.detach_and_log_err(cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use language::Bias;
|
|||
use persistence::TERMINAL_DB;
|
||||
use project::{search::SearchQuery, Fs, LocalWorktree, Metadata, Project};
|
||||
use settings::SettingsStore;
|
||||
use task::TerminalWorkDir;
|
||||
use terminal::{
|
||||
alacritty_terminal::{
|
||||
index::Point,
|
||||
|
@ -878,21 +879,26 @@ impl Item for TerminalView {
|
|||
) -> Task<anyhow::Result<View<Self>>> {
|
||||
let window = cx.window_handle();
|
||||
cx.spawn(|pane, mut cx| async move {
|
||||
let cwd = TERMINAL_DB
|
||||
.get_working_directory(item_id, workspace_id)
|
||||
.log_err()
|
||||
.flatten()
|
||||
.or_else(|| {
|
||||
cx.update(|cx| {
|
||||
let cwd = cx
|
||||
.update(|cx| {
|
||||
let from_db = TERMINAL_DB
|
||||
.get_working_directory(item_id, workspace_id)
|
||||
.log_err()
|
||||
.flatten();
|
||||
if from_db
|
||||
.as_ref()
|
||||
.is_some_and(|from_db| !from_db.as_os_str().is_empty())
|
||||
{
|
||||
project.read(cx).terminal_work_dir_for(from_db.as_ref(), cx)
|
||||
} else {
|
||||
let strategy = TerminalSettings::get_global(cx).working_directory.clone();
|
||||
workspace.upgrade().and_then(|workspace| {
|
||||
get_working_directory(workspace.read(cx), cx, strategy)
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
})
|
||||
.filter(|cwd| !cwd.as_os_str().is_empty());
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
let terminal = project.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(cwd, None, window, cx)
|
||||
|
@ -1043,20 +1049,24 @@ pub fn get_working_directory(
|
|||
workspace: &Workspace,
|
||||
cx: &AppContext,
|
||||
strategy: WorkingDirectory,
|
||||
) -> Option<PathBuf> {
|
||||
let res = match strategy {
|
||||
WorkingDirectory::CurrentProjectDirectory => current_project_directory(workspace, cx)
|
||||
.or_else(|| first_project_directory(workspace, cx)),
|
||||
WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx),
|
||||
WorkingDirectory::AlwaysHome => None,
|
||||
WorkingDirectory::Always { directory } => {
|
||||
shellexpand::full(&directory) //TODO handle this better
|
||||
.ok()
|
||||
.map(|dir| Path::new(&dir.to_string()).to_path_buf())
|
||||
.filter(|dir| dir.is_dir())
|
||||
}
|
||||
};
|
||||
res.or_else(home_dir)
|
||||
) -> Option<TerminalWorkDir> {
|
||||
if workspace.project().read(cx).is_local() {
|
||||
let res = match strategy {
|
||||
WorkingDirectory::CurrentProjectDirectory => current_project_directory(workspace, cx)
|
||||
.or_else(|| first_project_directory(workspace, cx)),
|
||||
WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx),
|
||||
WorkingDirectory::AlwaysHome => None,
|
||||
WorkingDirectory::Always { directory } => {
|
||||
shellexpand::full(&directory) //TODO handle this better
|
||||
.ok()
|
||||
.map(|dir| Path::new(&dir.to_string()).to_path_buf())
|
||||
.filter(|dir| dir.is_dir())
|
||||
}
|
||||
};
|
||||
res.or_else(home_dir).map(|cwd| TerminalWorkDir::Local(cwd))
|
||||
} else {
|
||||
workspace.project().read(cx).terminal_work_dir_for(None, cx)
|
||||
}
|
||||
}
|
||||
|
||||
///Gets the first project's home directory, or the home directory
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue