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| {
|
||||
let python_venv_directory = if let Some(path) = path {
|
||||
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
|
||||
.zip(Some(venv))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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,
|
||||
kind: TerminalKind,
|
||||
python_venv_directory: Option<PathBuf>,
|
||||
startup_script: impl FnOnce(&Shell) -> Option<String> + 'static,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Result<Entity<Terminal>> {
|
||||
let this = &mut *self;
|
||||
|
@ -353,17 +446,10 @@ impl Project {
|
|||
}
|
||||
};
|
||||
|
||||
let python_venv_activate_command = if let Some(python_venv_directory) =
|
||||
&python_venv_directory
|
||||
{
|
||||
this.python_activate_command(python_venv_directory, &settings.detect_venv, &shell, cx)
|
||||
} else {
|
||||
Task::ready(None)
|
||||
};
|
||||
|
||||
let startup_script = startup_script(&shell);
|
||||
TerminalBuilder::new(
|
||||
local_path.map(|path| path.to_path_buf()),
|
||||
python_venv_directory,
|
||||
startup_script,
|
||||
spawn_task,
|
||||
shell,
|
||||
env,
|
||||
|
@ -396,12 +482,6 @@ impl Project {
|
|||
})
|
||||
.detach();
|
||||
|
||||
this.activate_python_virtual_environment(
|
||||
python_venv_activate_command,
|
||||
&terminal_handle,
|
||||
cx,
|
||||
);
|
||||
|
||||
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>> {
|
||||
&self.terminals.local_handles
|
||||
}
|
||||
|
|
|
@ -344,7 +344,7 @@ pub struct TerminalBuilder {
|
|||
impl TerminalBuilder {
|
||||
pub fn new(
|
||||
working_directory: Option<PathBuf>,
|
||||
python_venv_directory: Option<PathBuf>,
|
||||
startup_script: Option<String>,
|
||||
task: Option<TaskState>,
|
||||
shell: Shell,
|
||||
mut env: HashMap<String, String>,
|
||||
|
@ -492,8 +492,8 @@ impl TerminalBuilder {
|
|||
//Kick things off
|
||||
let pty_tx = event_loop.channel();
|
||||
let _io_thread = event_loop.spawn(); // DANGER
|
||||
|
||||
let terminal = Terminal {
|
||||
let activate = startup_script.clone();
|
||||
let mut terminal = Terminal {
|
||||
task,
|
||||
pty_tx: Notifier(pty_tx),
|
||||
completion_tx,
|
||||
|
@ -514,13 +514,17 @@ impl TerminalBuilder {
|
|||
hyperlink_regex_searches: RegexSearches::new(),
|
||||
vi_mode_enabled: false,
|
||||
is_ssh_terminal,
|
||||
python_venv_directory,
|
||||
startup_script,
|
||||
last_mouse_move_time: Instant::now(),
|
||||
last_hyperlink_search_position: None,
|
||||
#[cfg(windows)]
|
||||
shell_program,
|
||||
};
|
||||
|
||||
if let Some(activate) = activate {
|
||||
terminal.input(activate.into_bytes());
|
||||
}
|
||||
|
||||
Ok(TerminalBuilder {
|
||||
terminal,
|
||||
events_rx,
|
||||
|
@ -684,6 +688,8 @@ pub struct Terminal {
|
|||
term: Arc<FairMutex<Term<ZedListener>>>,
|
||||
term_config: Config,
|
||||
events: VecDeque<InternalEvent>,
|
||||
// TODO lw: type this better
|
||||
pub startup_script: Option<String>,
|
||||
/// This is only used for mouse mode cell change detection
|
||||
last_mouse: Option<(AlacPoint, AlacDirection)>,
|
||||
pub matches: Vec<RangeInclusive<AlacPoint>>,
|
||||
|
@ -692,7 +698,6 @@ pub struct Terminal {
|
|||
pub breadcrumb_text: String,
|
||||
pub pty_info: PtyProcessInfo,
|
||||
title_override: Option<SharedString>,
|
||||
pub python_venv_directory: Option<PathBuf>,
|
||||
scroll_px: Pixels,
|
||||
next_link_id: usize,
|
||||
selection_phase: SelectionPhase,
|
||||
|
|
|
@ -416,22 +416,18 @@ impl TerminalPanel {
|
|||
let database_id = workspace.database_id();
|
||||
let weak_workspace = self.workspace.clone();
|
||||
let project = workspace.project().clone();
|
||||
let working_directory = self
|
||||
let terminal = self
|
||||
.active_pane
|
||||
.read(cx)
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<TerminalView>())
|
||||
.map(|terminal_view| {
|
||||
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);
|
||||
.map(|terminal_view| terminal_view.read(cx).terminal().clone());
|
||||
let terminal = project
|
||||
.update(cx, |project, cx| {
|
||||
project.create_terminal_with_venv(kind, None, cx)
|
||||
.update(cx, |project, cx| match terminal {
|
||||
Some(terminal) => project.clone_terminal(&terminal, cx),
|
||||
None => {
|
||||
project.create_terminal_with_startup(TerminalKind::Shell(None), |_| None, cx)
|
||||
}
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
|
|
|
@ -1668,11 +1668,7 @@ impl Item for TerminalView {
|
|||
let terminal = self
|
||||
.project
|
||||
.update(cx, |project, cx| {
|
||||
let terminal = self.terminal().read(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)
|
||||
project.clone_terminal(self.terminal(), cx)
|
||||
})
|
||||
.ok()?
|
||||
.log_err()?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue