Add activation script support for terminals/toolchains
This commit is contained in:
parent
7b3d73d6fd
commit
8e11e6a03e
11 changed files with 98 additions and 32 deletions
|
@ -10,14 +10,14 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use collections::HashMap;
|
use collections::{FxHashMap, HashMap};
|
||||||
use gpui::{AsyncApp, SharedString};
|
use gpui::{AsyncApp, SharedString};
|
||||||
use settings::WorktreeId;
|
use settings::WorktreeId;
|
||||||
|
|
||||||
use crate::{LanguageName, ManifestName};
|
use crate::{LanguageName, ManifestName};
|
||||||
|
|
||||||
/// Represents a single toolchain.
|
/// Represents a single toolchain.
|
||||||
#[derive(Clone, Debug, Eq)]
|
#[derive(Clone, Eq, Debug)]
|
||||||
pub struct Toolchain {
|
pub struct Toolchain {
|
||||||
/// User-facing label
|
/// User-facing label
|
||||||
pub name: SharedString,
|
pub name: SharedString,
|
||||||
|
@ -25,24 +25,41 @@ pub struct Toolchain {
|
||||||
pub language_name: LanguageName,
|
pub language_name: LanguageName,
|
||||||
/// Full toolchain data (including language-specific details)
|
/// Full toolchain data (including language-specific details)
|
||||||
pub as_json: serde_json::Value,
|
pub as_json: serde_json::Value,
|
||||||
|
/// shell -> script
|
||||||
|
pub startup_script: FxHashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for Toolchain {
|
impl std::hash::Hash for Toolchain {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.name.hash(state);
|
let Self {
|
||||||
self.path.hash(state);
|
name,
|
||||||
self.language_name.hash(state);
|
path,
|
||||||
|
language_name,
|
||||||
|
as_json: _,
|
||||||
|
startup_script: _,
|
||||||
|
} = self;
|
||||||
|
name.hash(state);
|
||||||
|
path.hash(state);
|
||||||
|
language_name.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Toolchain {
|
impl PartialEq for Toolchain {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let Self {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
language_name,
|
||||||
|
as_json: _,
|
||||||
|
startup_script,
|
||||||
|
} = self;
|
||||||
// Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
|
// Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
|
||||||
// Thus, there could be multiple entries that look the same in the UI.
|
// Thus, there could be multiple entries that look the same in the UI.
|
||||||
(&self.name, &self.path, &self.language_name).eq(&(
|
(name, path, language_name, startup_script).eq(&(
|
||||||
&other.name,
|
&other.name,
|
||||||
&other.path,
|
&other.path,
|
||||||
&other.language_name,
|
&other.language_name,
|
||||||
|
&other.startup_script,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +99,7 @@ pub trait LocalLanguageToolchainStore: Send + Sync + 'static {
|
||||||
) -> Option<Toolchain>;
|
) -> Option<Toolchain>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send )]
|
#[async_trait(?Send)]
|
||||||
impl<T: LocalLanguageToolchainStore> LanguageToolchainStore for T {
|
impl<T: LocalLanguageToolchainStore> LanguageToolchainStore for T {
|
||||||
async fn active_toolchain(
|
async fn active_toolchain(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
|
|
|
@ -878,6 +878,8 @@ impl ToolchainLister for PythonToolchainProvider {
|
||||||
path: toolchain.executable.as_ref()?.to_str()?.to_owned().into(),
|
path: toolchain.executable.as_ref()?.to_str()?.to_owned().into(),
|
||||||
language_name: LanguageName::new("Python"),
|
language_name: LanguageName::new("Python"),
|
||||||
as_json: serde_json::to_value(toolchain).ok()?,
|
as_json: serde_json::to_value(toolchain).ok()?,
|
||||||
|
startup_script: std::iter::once(("fish".to_owned(), "test".to_owned()))
|
||||||
|
.collect(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -293,6 +293,7 @@ impl DapStore {
|
||||||
binary.cwd.as_deref(),
|
binary.cwd.as_deref(),
|
||||||
binary.envs,
|
binary.envs,
|
||||||
path_style,
|
path_style,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
|
|
|
@ -9204,6 +9204,7 @@ fn python_lang(fs: Arc<FakeFs>) -> Arc<Language> {
|
||||||
path: venv_path.to_string_lossy().into_owned().into(),
|
path: venv_path.to_string_lossy().into_owned().into(),
|
||||||
language_name: LanguageName(SharedString::new_static("Python")),
|
language_name: LanguageName(SharedString::new_static("Python")),
|
||||||
as_json: serde_json::Value::Null,
|
as_json: serde_json::Value::Null,
|
||||||
|
startup_script: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use task::{Shell, ShellBuilder, SpawnInTerminal};
|
use task::{Shell, ShellBuilder, SpawnInTerminal, system_shell};
|
||||||
use terminal::{
|
use terminal::{
|
||||||
TaskState, TaskStatus, Terminal, TerminalBuilder, terminal_settings::TerminalSettings,
|
TaskState, TaskStatus, Terminal, TerminalBuilder, terminal_settings::TerminalSettings,
|
||||||
};
|
};
|
||||||
|
@ -173,6 +173,7 @@ impl Project {
|
||||||
path.as_deref(),
|
path.as_deref(),
|
||||||
env,
|
env,
|
||||||
path_style,
|
path_style,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
env = HashMap::default();
|
env = HashMap::default();
|
||||||
if let Some(envs) = envs {
|
if let Some(envs) = envs {
|
||||||
|
@ -213,6 +214,7 @@ impl Project {
|
||||||
cx.entity_id().as_u64(),
|
cx.entity_id().as_u64(),
|
||||||
Some(completion_tx),
|
Some(completion_tx),
|
||||||
cx,
|
cx,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.map(|builder| {
|
.map(|builder| {
|
||||||
let terminal_handle = cx.new(|cx| builder.subscribe(cx));
|
let terminal_handle = cx.new(|cx| builder.subscribe(cx));
|
||||||
|
@ -276,21 +278,33 @@ impl Project {
|
||||||
let toolchain =
|
let toolchain =
|
||||||
project_path_context.map(|p| self.active_toolchain(p, LanguageName::new("Python"), cx));
|
project_path_context.map(|p| self.active_toolchain(p, LanguageName::new("Python"), cx));
|
||||||
cx.spawn(async move |project, cx| {
|
cx.spawn(async move |project, cx| {
|
||||||
let toolchain = maybe!(async {
|
let shell = match &ssh_details {
|
||||||
let toolchain = toolchain?.await?;
|
Some(ssh) => ssh.shell.clone(),
|
||||||
|
None => match &settings.shell {
|
||||||
|
Shell::Program(program) => program.clone(),
|
||||||
|
Shell::WithArguments {
|
||||||
|
program,
|
||||||
|
args: _,
|
||||||
|
title_override: _,
|
||||||
|
} => program.clone(),
|
||||||
|
Shell::System => system_shell(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Some(())
|
let scripts = maybe!(async {
|
||||||
|
let toolchain = toolchain?.await?;
|
||||||
|
Some(toolchain.startup_script)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
let activation_script = scripts.as_ref().and_then(|it| it.get(&shell));
|
||||||
let (spawn_task, shell) = {
|
let shell = {
|
||||||
match ssh_details {
|
match ssh_details {
|
||||||
Some(SshDetails {
|
Some(SshDetails {
|
||||||
host,
|
host,
|
||||||
ssh_command,
|
ssh_command,
|
||||||
envs,
|
envs,
|
||||||
path_style,
|
path_style,
|
||||||
shell,
|
shell: _,
|
||||||
}) => {
|
}) => {
|
||||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||||
|
|
||||||
|
@ -308,28 +322,37 @@ impl Project {
|
||||||
path.as_deref(),
|
path.as_deref(),
|
||||||
env,
|
env,
|
||||||
path_style,
|
path_style,
|
||||||
|
activation_script.map(String::as_str),
|
||||||
);
|
);
|
||||||
env = HashMap::default();
|
env = HashMap::default();
|
||||||
if let Some(envs) = envs {
|
if let Some(envs) = envs {
|
||||||
env.extend(envs);
|
env.extend(envs);
|
||||||
}
|
}
|
||||||
(
|
Shell::WithArguments {
|
||||||
Option::<TaskState>::None,
|
program,
|
||||||
Shell::WithArguments {
|
args,
|
||||||
program,
|
title_override: Some(format!("{} — Terminal", host).into()),
|
||||||
args,
|
}
|
||||||
title_override: Some(format!("{} — Terminal", host).into()),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
None => (None, settings.shell),
|
None if activation_script.is_some() => Shell::WithArguments {
|
||||||
|
program: shell.clone(),
|
||||||
|
args: vec![
|
||||||
|
"-c".to_owned(),
|
||||||
|
format!(
|
||||||
|
"{}; exec {} -l",
|
||||||
|
activation_script.unwrap().to_string(),
|
||||||
|
shell
|
||||||
|
),
|
||||||
|
],
|
||||||
|
title_override: None,
|
||||||
|
},
|
||||||
|
None => settings.shell,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
project.update(cx, move |this, cx| {
|
project.update(cx, move |this, cx| {
|
||||||
TerminalBuilder::new(
|
TerminalBuilder::new(
|
||||||
local_path.map(|path| path.to_path_buf()),
|
local_path.map(|path| path.to_path_buf()),
|
||||||
spawn_task,
|
None,
|
||||||
shell,
|
shell,
|
||||||
env,
|
env,
|
||||||
settings.cursor_shape.unwrap_or_default(),
|
settings.cursor_shape.unwrap_or_default(),
|
||||||
|
@ -339,6 +362,7 @@ impl Project {
|
||||||
cx.entity_id().as_u64(),
|
cx.entity_id().as_u64(),
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.map(|builder| {
|
.map(|builder| {
|
||||||
let terminal_handle = cx.new(|cx| builder.subscribe(cx));
|
let terminal_handle = cx.new(|cx| builder.subscribe(cx));
|
||||||
|
@ -447,6 +471,7 @@ impl Project {
|
||||||
path.as_deref(),
|
path.as_deref(),
|
||||||
env,
|
env,
|
||||||
path_style,
|
path_style,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let mut command = std::process::Command::new(command);
|
let mut command = std::process::Command::new(command);
|
||||||
command.args(args);
|
command.args(args);
|
||||||
|
@ -479,6 +504,7 @@ pub fn wrap_for_ssh(
|
||||||
path: Option<&Path>,
|
path: Option<&Path>,
|
||||||
env: HashMap<String, String>,
|
env: HashMap<String, String>,
|
||||||
path_style: PathStyle,
|
path_style: PathStyle,
|
||||||
|
activation_script: Option<&str>,
|
||||||
) -> (String, Vec<String>) {
|
) -> (String, Vec<String>) {
|
||||||
let to_run = if let Some((command, args)) = command {
|
let to_run = if let Some((command, args)) = command {
|
||||||
let command: Option<Cow<str>> = shlex::try_quote(command).ok();
|
let command: Option<Cow<str>> = shlex::try_quote(command).ok();
|
||||||
|
@ -495,6 +521,9 @@ pub fn wrap_for_ssh(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let activation_script = activation_script
|
||||||
|
.map(|s| format!(" {s};"))
|
||||||
|
.unwrap_or_default();
|
||||||
let commands = if let Some(path) = path {
|
let commands = if let Some(path) = path {
|
||||||
let path = RemotePathBuf::new(path.to_path_buf(), path_style).to_string();
|
let path = RemotePathBuf::new(path.to_path_buf(), path_style).to_string();
|
||||||
// shlex will wrap the command in single quotes (''), disabling ~ expansion,
|
// shlex will wrap the command in single quotes (''), disabling ~ expansion,
|
||||||
|
@ -506,12 +535,12 @@ pub fn wrap_for_ssh(
|
||||||
.trim_start_matches("~")
|
.trim_start_matches("~")
|
||||||
.trim_start_matches("/");
|
.trim_start_matches("/");
|
||||||
|
|
||||||
format!("cd \"$HOME/{trimmed_path}\"; {env_changes} {to_run}")
|
format!("cd \"$HOME/{trimmed_path}\";{activation_script} {env_changes} {to_run}")
|
||||||
} else {
|
} else {
|
||||||
format!("cd \"{path}\"; {env_changes} {to_run}")
|
format!("cd \"{path}\";{activation_script} {env_changes} {to_run}")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("cd; {env_changes} {to_run}")
|
format!("cd;{activation_script} {env_changes} {to_run}")
|
||||||
};
|
};
|
||||||
let shell_invocation = format!("{shell} -c {}", shlex::try_quote(&commands).unwrap());
|
let shell_invocation = format!("{shell} -c {}", shlex::try_quote(&commands).unwrap());
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,7 @@ impl ToolchainStore {
|
||||||
// Do we need to convert path to native string?
|
// Do we need to convert path to native string?
|
||||||
path: PathBuf::from(toolchain.path).to_proto().into(),
|
path: PathBuf::from(toolchain.path).to_proto().into(),
|
||||||
as_json: serde_json::Value::from_str(&toolchain.raw_json)?,
|
as_json: serde_json::Value::from_str(&toolchain.raw_json)?,
|
||||||
|
startup_script: toolchain.activation_script.into_iter().collect(),
|
||||||
language_name,
|
language_name,
|
||||||
};
|
};
|
||||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||||
|
@ -178,6 +179,7 @@ impl ToolchainStore {
|
||||||
name: toolchain.name.into(),
|
name: toolchain.name.into(),
|
||||||
path: path.to_proto(),
|
path: path.to_proto(),
|
||||||
raw_json: toolchain.as_json.to_string(),
|
raw_json: toolchain.as_json.to_string(),
|
||||||
|
activation_script: toolchain.startup_script.into_iter().collect(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -221,6 +223,7 @@ impl ToolchainStore {
|
||||||
name: toolchain.name.to_string(),
|
name: toolchain.name.to_string(),
|
||||||
path: path.to_proto(),
|
path: path.to_proto(),
|
||||||
raw_json: toolchain.as_json.to_string(),
|
raw_json: toolchain.as_json.to_string(),
|
||||||
|
activation_script: toolchain.startup_script.into_iter().collect(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -449,6 +452,7 @@ impl RemoteToolchainStore {
|
||||||
name: toolchain.name.into(),
|
name: toolchain.name.into(),
|
||||||
path: path.to_proto(),
|
path: path.to_proto(),
|
||||||
raw_json: toolchain.as_json.to_string(),
|
raw_json: toolchain.as_json.to_string(),
|
||||||
|
activation_script: toolchain.startup_script.into_iter().collect(),
|
||||||
}),
|
}),
|
||||||
path: Some(project_path.path.to_string_lossy().into_owned()),
|
path: Some(project_path.path.to_string_lossy().into_owned()),
|
||||||
})
|
})
|
||||||
|
@ -501,6 +505,7 @@ impl RemoteToolchainStore {
|
||||||
.to_string()
|
.to_string()
|
||||||
.into(),
|
.into(),
|
||||||
as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
|
as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
|
||||||
|
startup_script: toolchain.activation_script.into_iter().collect(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -557,6 +562,7 @@ impl RemoteToolchainStore {
|
||||||
.to_string()
|
.to_string()
|
||||||
.into(),
|
.into(),
|
||||||
as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
|
as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
|
||||||
|
startup_script: toolchain.activation_script.into_iter().collect(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,6 +12,7 @@ message Toolchain {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
string path = 2;
|
string path = 2;
|
||||||
string raw_json = 3;
|
string raw_json = 3;
|
||||||
|
map<string, string> activation_script = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ToolchainGroup {
|
message ToolchainGroup {
|
||||||
|
|
|
@ -178,7 +178,7 @@ impl ShellKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn system_shell() -> String {
|
pub fn system_shell() -> String {
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
// `alacritty_terminal` uses this as default on Windows. See:
|
// `alacritty_terminal` uses this as default on Windows. See:
|
||||||
// https://github.com/alacritty/alacritty/blob/0d4ab7bca43213d96ddfe40048fc0f922543c6f8/alacritty_terminal/src/tty/windows/mod.rs#L130
|
// https://github.com/alacritty/alacritty/blob/0d4ab7bca43213d96ddfe40048fc0f922543c6f8/alacritty_terminal/src/tty/windows/mod.rs#L130
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub use debug_format::{
|
||||||
AttachRequest, BuildTaskDefinition, DebugRequest, DebugScenario, DebugTaskFile, LaunchRequest,
|
AttachRequest, BuildTaskDefinition, DebugRequest, DebugScenario, DebugTaskFile, LaunchRequest,
|
||||||
Request, TcpArgumentsTemplate, ZedDebugConfig,
|
Request, TcpArgumentsTemplate, ZedDebugConfig,
|
||||||
};
|
};
|
||||||
pub use shell_builder::{ShellBuilder, ShellKind};
|
pub use shell_builder::{ShellBuilder, ShellKind, system_shell};
|
||||||
pub use task_template::{
|
pub use task_template::{
|
||||||
DebugArgsRequest, HideStrategy, RevealStrategy, TaskTemplate, TaskTemplates,
|
DebugArgsRequest, HideStrategy, RevealStrategy, TaskTemplate, TaskTemplates,
|
||||||
substitute_variables_in_map, substitute_variables_in_str,
|
substitute_variables_in_map, substitute_variables_in_str,
|
||||||
|
|
|
@ -354,6 +354,7 @@ impl TerminalBuilder {
|
||||||
window_id: u64,
|
window_id: u64,
|
||||||
completion_tx: Option<Sender<Option<ExitStatus>>>,
|
completion_tx: Option<Sender<Option<ExitStatus>>>,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
|
startup_script: Option<String>,
|
||||||
) -> Result<TerminalBuilder> {
|
) -> Result<TerminalBuilder> {
|
||||||
// If the parent environment doesn't have a locale set
|
// If the parent environment doesn't have a locale set
|
||||||
// (As is the case when launched from a .app on MacOS),
|
// (As is the case when launched from a .app on MacOS),
|
||||||
|
@ -517,6 +518,7 @@ impl TerminalBuilder {
|
||||||
last_hyperlink_search_position: None,
|
last_hyperlink_search_position: None,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
shell_program,
|
shell_program,
|
||||||
|
startup_script,
|
||||||
template: CopyTemplate {
|
template: CopyTemplate {
|
||||||
shell,
|
shell,
|
||||||
env,
|
env,
|
||||||
|
@ -710,6 +712,7 @@ pub struct Terminal {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
shell_program: Option<String>,
|
shell_program: Option<String>,
|
||||||
template: CopyTemplate,
|
template: CopyTemplate,
|
||||||
|
startup_script: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CopyTemplate {
|
struct CopyTemplate {
|
||||||
|
@ -1983,6 +1986,7 @@ impl Terminal {
|
||||||
self.template.window_id,
|
self.template.window_id,
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
|
self.startup_script.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2214,6 +2218,7 @@ mod tests {
|
||||||
0,
|
0,
|
||||||
Some(completion_tx),
|
Some(completion_tx),
|
||||||
cx,
|
cx,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.subscribe(cx)
|
.subscribe(cx)
|
||||||
|
|
|
@ -1403,7 +1403,9 @@ impl WorkspaceDb {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
language_name,
|
language_name,
|
||||||
as_json: serde_json::Value::from_str(&raw_json).ok()?
|
as_json: serde_json::Value::from_str(&raw_json).ok()?,
|
||||||
|
// todo refresh?
|
||||||
|
startup_script: Default::default(),
|
||||||
})))
|
})))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -1427,7 +1429,9 @@ impl WorkspaceDb {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
language_name: LanguageName::new(&language_name),
|
language_name: LanguageName::new(&language_name),
|
||||||
as_json: serde_json::Value::from_str(&raw_json).ok()?
|
as_json: serde_json::Value::from_str(&raw_json).ok()?,
|
||||||
|
// todo refresh?
|
||||||
|
startup_script: Default::default(),
|
||||||
}, WorktreeId::from_proto(worktree_id), Arc::from(relative_worktree_path.as_ref())))).collect())
|
}, WorktreeId::from_proto(worktree_id), Arc::from(relative_worktree_path.as_ref())))).collect())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue