Slowly disconnect venv handling from project/terminals.rs
This commit is contained in:
parent
b756853407
commit
dc0e81b13b
4 changed files with 117 additions and 138 deletions
|
@ -123,13 +123,86 @@ impl Project {
|
||||||
cx.spawn(async move |project, cx| {
|
cx.spawn(async move |project, cx| {
|
||||||
let python_venv_directory = if let Some(path) = path {
|
let python_venv_directory = if let Some(path) = path {
|
||||||
project
|
project
|
||||||
.update(cx, |this, cx| this.python_venv_directory(path, venv, cx))?
|
.update(cx, |this, cx| {
|
||||||
|
this.python_venv_directory(path, venv.clone(), cx)
|
||||||
|
})?
|
||||||
.await
|
.await
|
||||||
|
.zip(Some(venv))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
project.create_terminal_with_venv(kind, python_venv_directory, cx)
|
// todo lw: move all this out of here?
|
||||||
|
let startup_script = move |shell: &_| {
|
||||||
|
let Some((python_venv_directory, venv)) = &python_venv_directory else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
// todo: If this returns `None` we may don't have a venv, but we may still have a python toolchiain
|
||||||
|
// we should modify the PATH here still
|
||||||
|
let venv_settings = venv.as_option()?;
|
||||||
|
let script_kind = if venv_settings.activate_script
|
||||||
|
== terminal_settings::ActivateScript::Default
|
||||||
|
{
|
||||||
|
match shell {
|
||||||
|
Shell::Program(program) => Self::activate_script_kind(Some(program)),
|
||||||
|
Shell::WithArguments {
|
||||||
|
program,
|
||||||
|
args: _,
|
||||||
|
title_override: _,
|
||||||
|
} => Self::activate_script_kind(Some(program)),
|
||||||
|
Shell::System => Self::activate_script_kind(None),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
venv_settings.activate_script
|
||||||
|
};
|
||||||
|
|
||||||
|
let activate_script_name = match script_kind {
|
||||||
|
terminal_settings::ActivateScript::Default
|
||||||
|
| terminal_settings::ActivateScript::Pyenv => "activate",
|
||||||
|
terminal_settings::ActivateScript::Csh => "activate.csh",
|
||||||
|
terminal_settings::ActivateScript::Fish => "activate.fish",
|
||||||
|
terminal_settings::ActivateScript::Nushell => "activate.nu",
|
||||||
|
terminal_settings::ActivateScript::PowerShell => "activate.ps1",
|
||||||
|
};
|
||||||
|
|
||||||
|
let activate_keyword = match venv_settings.activate_script {
|
||||||
|
terminal_settings::ActivateScript::Default => match std::env::consts::OS {
|
||||||
|
"windows" => ".",
|
||||||
|
_ => ".",
|
||||||
|
},
|
||||||
|
terminal_settings::ActivateScript::Nushell => "overlay use",
|
||||||
|
terminal_settings::ActivateScript::PowerShell => ".",
|
||||||
|
terminal_settings::ActivateScript::Pyenv => "pyenv",
|
||||||
|
_ => "source",
|
||||||
|
};
|
||||||
|
|
||||||
|
let line_ending = match std::env::consts::OS {
|
||||||
|
"windows" => "\r",
|
||||||
|
_ => "\n",
|
||||||
|
};
|
||||||
|
|
||||||
|
if venv_settings.venv_name.is_empty() {
|
||||||
|
let path = python_venv_directory
|
||||||
|
.join(match std::env::consts::OS {
|
||||||
|
"windows" => "Scripts",
|
||||||
|
_ => "bin",
|
||||||
|
})
|
||||||
|
.join(activate_script_name)
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
let quoted = shlex::try_quote(&path).ok()?;
|
||||||
|
Some(format!(
|
||||||
|
"{} {} ; clear{}",
|
||||||
|
activate_keyword, quoted, line_ending
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Some(format!(
|
||||||
|
"{activate_keyword} {activate_script_name} {name}; clear{line_ending}",
|
||||||
|
name = venv_settings.venv_name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
project.create_terminal_with_startup(kind, startup_script, cx)
|
||||||
})?
|
})?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -199,10 +272,30 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_terminal_with_venv(
|
pub fn clone_terminal(
|
||||||
|
&mut self,
|
||||||
|
terminal: &Entity<Terminal>,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Result<Entity<Terminal>> {
|
||||||
|
let terminal = terminal.read(cx);
|
||||||
|
let working_directory = terminal
|
||||||
|
.working_directory()
|
||||||
|
.or_else(|| Some(self.active_project_directory(cx)?.to_path_buf()));
|
||||||
|
let startup_script = terminal.startup_script.clone();
|
||||||
|
self.create_terminal_with_startup(
|
||||||
|
TerminalKind::Shell(working_directory),
|
||||||
|
|_| {
|
||||||
|
// The shell shouldn't change here
|
||||||
|
startup_script
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_terminal_with_startup(
|
||||||
&mut self,
|
&mut self,
|
||||||
kind: TerminalKind,
|
kind: TerminalKind,
|
||||||
python_venv_directory: Option<PathBuf>,
|
startup_script: impl FnOnce(&Shell) -> Option<String> + 'static,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Result<Entity<Terminal>> {
|
) -> Result<Entity<Terminal>> {
|
||||||
let this = &mut *self;
|
let this = &mut *self;
|
||||||
|
@ -353,17 +446,10 @@ impl Project {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let python_venv_activate_command = if let Some(python_venv_directory) =
|
let startup_script = startup_script(&shell);
|
||||||
&python_venv_directory
|
|
||||||
{
|
|
||||||
this.python_activate_command(python_venv_directory, &settings.detect_venv, &shell, cx)
|
|
||||||
} else {
|
|
||||||
Task::ready(None)
|
|
||||||
};
|
|
||||||
|
|
||||||
TerminalBuilder::new(
|
TerminalBuilder::new(
|
||||||
local_path.map(|path| path.to_path_buf()),
|
local_path.map(|path| path.to_path_buf()),
|
||||||
python_venv_directory,
|
startup_script,
|
||||||
spawn_task,
|
spawn_task,
|
||||||
shell,
|
shell,
|
||||||
env,
|
env,
|
||||||
|
@ -396,12 +482,6 @@ impl Project {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
this.activate_python_virtual_environment(
|
|
||||||
python_venv_activate_command,
|
|
||||||
&terminal_handle,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
|
|
||||||
terminal_handle
|
terminal_handle
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -514,104 +594,6 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn python_activate_command(
|
|
||||||
&self,
|
|
||||||
venv_base_directory: &Path,
|
|
||||||
venv_settings: &VenvSettings,
|
|
||||||
shell: &Shell,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> Task<Option<String>> {
|
|
||||||
let Some(venv_settings) = venv_settings.as_option() else {
|
|
||||||
return Task::ready(None);
|
|
||||||
};
|
|
||||||
let activate_keyword = match venv_settings.activate_script {
|
|
||||||
terminal_settings::ActivateScript::Default => match std::env::consts::OS {
|
|
||||||
"windows" => ".",
|
|
||||||
_ => ".",
|
|
||||||
},
|
|
||||||
terminal_settings::ActivateScript::Nushell => "overlay use",
|
|
||||||
terminal_settings::ActivateScript::PowerShell => ".",
|
|
||||||
terminal_settings::ActivateScript::Pyenv => "pyenv",
|
|
||||||
_ => "source",
|
|
||||||
};
|
|
||||||
let script_kind =
|
|
||||||
if venv_settings.activate_script == terminal_settings::ActivateScript::Default {
|
|
||||||
match shell {
|
|
||||||
Shell::Program(program) => Self::activate_script_kind(Some(program)),
|
|
||||||
Shell::WithArguments {
|
|
||||||
program,
|
|
||||||
args: _,
|
|
||||||
title_override: _,
|
|
||||||
} => Self::activate_script_kind(Some(program)),
|
|
||||||
Shell::System => Self::activate_script_kind(None),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
venv_settings.activate_script
|
|
||||||
};
|
|
||||||
|
|
||||||
let activate_script_name = match script_kind {
|
|
||||||
terminal_settings::ActivateScript::Default
|
|
||||||
| terminal_settings::ActivateScript::Pyenv => "activate",
|
|
||||||
terminal_settings::ActivateScript::Csh => "activate.csh",
|
|
||||||
terminal_settings::ActivateScript::Fish => "activate.fish",
|
|
||||||
terminal_settings::ActivateScript::Nushell => "activate.nu",
|
|
||||||
terminal_settings::ActivateScript::PowerShell => "activate.ps1",
|
|
||||||
};
|
|
||||||
|
|
||||||
let line_ending = match std::env::consts::OS {
|
|
||||||
"windows" => "\r",
|
|
||||||
_ => "\n",
|
|
||||||
};
|
|
||||||
|
|
||||||
if venv_settings.venv_name.is_empty() {
|
|
||||||
let path = venv_base_directory
|
|
||||||
.join(match std::env::consts::OS {
|
|
||||||
"windows" => "Scripts",
|
|
||||||
_ => "bin",
|
|
||||||
})
|
|
||||||
.join(activate_script_name)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let is_valid_path = self.resolve_abs_path(path.as_ref(), cx);
|
|
||||||
cx.background_spawn(async move {
|
|
||||||
let quoted = shlex::try_quote(&path).ok()?;
|
|
||||||
if is_valid_path.await.is_some_and(|meta| meta.is_file()) {
|
|
||||||
Some(format!(
|
|
||||||
"{} {} ; clear{}",
|
|
||||||
activate_keyword, quoted, line_ending
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Task::ready(Some(format!(
|
|
||||||
"{activate_keyword} {activate_script_name} {name}; clear{line_ending}",
|
|
||||||
name = venv_settings.venv_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn activate_python_virtual_environment(
|
|
||||||
&self,
|
|
||||||
command: Task<Option<String>>,
|
|
||||||
terminal_handle: &Entity<Terminal>,
|
|
||||||
cx: &mut App,
|
|
||||||
) {
|
|
||||||
terminal_handle.update(cx, |_, cx| {
|
|
||||||
cx.spawn(async move |this, cx| {
|
|
||||||
if let Some(command) = command.await {
|
|
||||||
this.update(cx, |this, _| {
|
|
||||||
this.input(command.into_bytes());
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_terminal_handles(&self) -> &Vec<WeakEntity<terminal::Terminal>> {
|
pub fn local_terminal_handles(&self) -> &Vec<WeakEntity<terminal::Terminal>> {
|
||||||
&self.terminals.local_handles
|
&self.terminals.local_handles
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,7 +344,7 @@ pub struct TerminalBuilder {
|
||||||
impl TerminalBuilder {
|
impl TerminalBuilder {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
working_directory: Option<PathBuf>,
|
working_directory: Option<PathBuf>,
|
||||||
python_venv_directory: Option<PathBuf>,
|
startup_script: Option<String>,
|
||||||
task: Option<TaskState>,
|
task: Option<TaskState>,
|
||||||
shell: Shell,
|
shell: Shell,
|
||||||
mut env: HashMap<String, String>,
|
mut env: HashMap<String, String>,
|
||||||
|
@ -492,8 +492,8 @@ impl TerminalBuilder {
|
||||||
//Kick things off
|
//Kick things off
|
||||||
let pty_tx = event_loop.channel();
|
let pty_tx = event_loop.channel();
|
||||||
let _io_thread = event_loop.spawn(); // DANGER
|
let _io_thread = event_loop.spawn(); // DANGER
|
||||||
|
let activate = startup_script.clone();
|
||||||
let terminal = Terminal {
|
let mut terminal = Terminal {
|
||||||
task,
|
task,
|
||||||
pty_tx: Notifier(pty_tx),
|
pty_tx: Notifier(pty_tx),
|
||||||
completion_tx,
|
completion_tx,
|
||||||
|
@ -514,13 +514,17 @@ impl TerminalBuilder {
|
||||||
hyperlink_regex_searches: RegexSearches::new(),
|
hyperlink_regex_searches: RegexSearches::new(),
|
||||||
vi_mode_enabled: false,
|
vi_mode_enabled: false,
|
||||||
is_ssh_terminal,
|
is_ssh_terminal,
|
||||||
python_venv_directory,
|
startup_script,
|
||||||
last_mouse_move_time: Instant::now(),
|
last_mouse_move_time: Instant::now(),
|
||||||
last_hyperlink_search_position: None,
|
last_hyperlink_search_position: None,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
shell_program,
|
shell_program,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(activate) = activate {
|
||||||
|
terminal.input(activate.into_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(TerminalBuilder {
|
Ok(TerminalBuilder {
|
||||||
terminal,
|
terminal,
|
||||||
events_rx,
|
events_rx,
|
||||||
|
@ -684,6 +688,8 @@ pub struct Terminal {
|
||||||
term: Arc<FairMutex<Term<ZedListener>>>,
|
term: Arc<FairMutex<Term<ZedListener>>>,
|
||||||
term_config: Config,
|
term_config: Config,
|
||||||
events: VecDeque<InternalEvent>,
|
events: VecDeque<InternalEvent>,
|
||||||
|
// TODO lw: type this better
|
||||||
|
pub startup_script: Option<String>,
|
||||||
/// This is only used for mouse mode cell change detection
|
/// This is only used for mouse mode cell change detection
|
||||||
last_mouse: Option<(AlacPoint, AlacDirection)>,
|
last_mouse: Option<(AlacPoint, AlacDirection)>,
|
||||||
pub matches: Vec<RangeInclusive<AlacPoint>>,
|
pub matches: Vec<RangeInclusive<AlacPoint>>,
|
||||||
|
@ -692,7 +698,6 @@ pub struct Terminal {
|
||||||
pub breadcrumb_text: String,
|
pub breadcrumb_text: String,
|
||||||
pub pty_info: PtyProcessInfo,
|
pub pty_info: PtyProcessInfo,
|
||||||
title_override: Option<SharedString>,
|
title_override: Option<SharedString>,
|
||||||
pub python_venv_directory: Option<PathBuf>,
|
|
||||||
scroll_px: Pixels,
|
scroll_px: Pixels,
|
||||||
next_link_id: usize,
|
next_link_id: usize,
|
||||||
selection_phase: SelectionPhase,
|
selection_phase: SelectionPhase,
|
||||||
|
|
|
@ -416,22 +416,18 @@ impl TerminalPanel {
|
||||||
let database_id = workspace.database_id();
|
let database_id = workspace.database_id();
|
||||||
let weak_workspace = self.workspace.clone();
|
let weak_workspace = self.workspace.clone();
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
let working_directory = self
|
let terminal = self
|
||||||
.active_pane
|
.active_pane
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.active_item()
|
.active_item()
|
||||||
.and_then(|item| item.downcast::<TerminalView>())
|
.and_then(|item| item.downcast::<TerminalView>())
|
||||||
.map(|terminal_view| {
|
.map(|terminal_view| terminal_view.read(cx).terminal().clone());
|
||||||
let terminal = terminal_view.read(cx).terminal().read(cx);
|
|
||||||
terminal
|
|
||||||
.working_directory()
|
|
||||||
.or_else(|| default_working_directory(workspace, cx))
|
|
||||||
})
|
|
||||||
.unwrap_or(None);
|
|
||||||
let kind = TerminalKind::Shell(working_directory);
|
|
||||||
let terminal = project
|
let terminal = project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| match terminal {
|
||||||
project.create_terminal_with_venv(kind, None, cx)
|
Some(terminal) => project.clone_terminal(&terminal, cx),
|
||||||
|
None => {
|
||||||
|
project.create_terminal_with_startup(TerminalKind::Shell(None), |_| None, cx)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
|
|
|
@ -1668,11 +1668,7 @@ impl Item for TerminalView {
|
||||||
let terminal = self
|
let terminal = self
|
||||||
.project
|
.project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| {
|
||||||
let terminal = self.terminal().read(cx);
|
project.clone_terminal(self.terminal(), cx)
|
||||||
let working_directory = terminal
|
|
||||||
.working_directory()
|
|
||||||
.or_else(|| Some(project.active_project_directory(cx)?.to_path_buf()));
|
|
||||||
project.create_terminal_with_venv(TerminalKind::Shell(working_directory), None, cx)
|
|
||||||
})
|
})
|
||||||
.ok()?
|
.ok()?
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue