Overhaul extension registration (#21083)
This PR overhauls extension registration in order to make it more modular. The `extension` crate now contains an `ExtensionHostProxy` that can be used to register various proxies that the extension host can use to interact with the rest of the system. There are now a number of different proxy traits representing the various pieces of functionality that can be provided by an extension. The respective crates that provide this functionality can implement their corresponding proxy trait in order to register a proxy that the extension host will use to register the bits of functionality provided by the extension. Release Notes: - N/A
This commit is contained in:
parent
c9f2c2792c
commit
1cfcdfa7ac
49
Cargo.lock
generated
49
Cargo.lock
generated
@ -2601,6 +2601,7 @@ dependencies = [
|
||||
"editor",
|
||||
"env_logger 0.11.5",
|
||||
"envy",
|
||||
"extension",
|
||||
"file_finder",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
@ -2842,6 +2843,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"command_palette_hooks",
|
||||
"extension",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"log",
|
||||
@ -4127,6 +4129,7 @@ dependencies = [
|
||||
"language",
|
||||
"log",
|
||||
"lsp",
|
||||
"parking_lot",
|
||||
"semantic_version",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4178,6 +4181,7 @@ dependencies = [
|
||||
"gpui",
|
||||
"http_client",
|
||||
"language",
|
||||
"language_extension",
|
||||
"log",
|
||||
"lsp",
|
||||
"node_runtime",
|
||||
@ -4196,6 +4200,7 @@ dependencies = [
|
||||
"task",
|
||||
"tempfile",
|
||||
"theme",
|
||||
"theme_extension",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"util",
|
||||
@ -4209,21 +4214,15 @@ name = "extensions_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assistant_slash_command",
|
||||
"client",
|
||||
"collections",
|
||||
"context_servers",
|
||||
"db",
|
||||
"editor",
|
||||
"extension",
|
||||
"extension_host",
|
||||
"fs",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"indexed_docs",
|
||||
"language",
|
||||
"log",
|
||||
"lsp",
|
||||
"num-format",
|
||||
"picker",
|
||||
"project",
|
||||
@ -4232,7 +4231,6 @@ dependencies = [
|
||||
"serde",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"snippet_provider",
|
||||
"theme",
|
||||
"ui",
|
||||
"util",
|
||||
@ -6533,6 +6531,23 @@ dependencies = [
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language_extension"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"collections",
|
||||
"extension",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"language",
|
||||
"lsp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language_model"
|
||||
version = "0.1.0"
|
||||
@ -9853,6 +9868,7 @@ dependencies = [
|
||||
"client",
|
||||
"clock",
|
||||
"env_logger 0.11.5",
|
||||
"extension",
|
||||
"extension_host",
|
||||
"fork",
|
||||
"fs",
|
||||
@ -9862,6 +9878,7 @@ dependencies = [
|
||||
"gpui",
|
||||
"http_client",
|
||||
"language",
|
||||
"language_extension",
|
||||
"languages",
|
||||
"libc",
|
||||
"log",
|
||||
@ -11304,6 +11321,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"extension",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
@ -12357,6 +12375,17 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "theme_extension"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"extension",
|
||||
"fs",
|
||||
"gpui",
|
||||
"theme",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "theme_importer"
|
||||
version = "0.1.0"
|
||||
@ -15466,7 +15495,6 @@ dependencies = [
|
||||
"ashpd",
|
||||
"assets",
|
||||
"assistant",
|
||||
"assistant_slash_command",
|
||||
"async-watch",
|
||||
"audio",
|
||||
"auto_update",
|
||||
@ -15483,12 +15511,12 @@ dependencies = [
|
||||
"collections",
|
||||
"command_palette",
|
||||
"command_palette_hooks",
|
||||
"context_servers",
|
||||
"copilot",
|
||||
"db",
|
||||
"diagnostics",
|
||||
"editor",
|
||||
"env_logger 0.11.5",
|
||||
"extension",
|
||||
"extension_host",
|
||||
"extensions_ui",
|
||||
"feature_flags",
|
||||
@ -15503,11 +15531,11 @@ dependencies = [
|
||||
"gpui",
|
||||
"http_client",
|
||||
"image_viewer",
|
||||
"indexed_docs",
|
||||
"inline_completion_button",
|
||||
"install_cli",
|
||||
"journal",
|
||||
"language",
|
||||
"language_extension",
|
||||
"language_model",
|
||||
"language_models",
|
||||
"language_selector",
|
||||
@ -15556,6 +15584,7 @@ dependencies = [
|
||||
"telemetry_events",
|
||||
"terminal_view",
|
||||
"theme",
|
||||
"theme_extension",
|
||||
"theme_selector",
|
||||
"time",
|
||||
"toolchain_selector",
|
||||
|
@ -55,6 +55,7 @@ members = [
|
||||
"crates/install_cli",
|
||||
"crates/journal",
|
||||
"crates/language",
|
||||
"crates/language_extension",
|
||||
"crates/language_model",
|
||||
"crates/language_models",
|
||||
"crates/language_selector",
|
||||
@ -116,6 +117,7 @@ members = [
|
||||
"crates/terminal_view",
|
||||
"crates/text",
|
||||
"crates/theme",
|
||||
"crates/theme_extension",
|
||||
"crates/theme_importer",
|
||||
"crates/theme_selector",
|
||||
"crates/time_format",
|
||||
@ -230,6 +232,7 @@ inline_completion_button = { path = "crates/inline_completion_button" }
|
||||
install_cli = { path = "crates/install_cli" }
|
||||
journal = { path = "crates/journal" }
|
||||
language = { path = "crates/language" }
|
||||
language_extension = { path = "crates/language_extension" }
|
||||
language_model = { path = "crates/language_model" }
|
||||
language_models = { path = "crates/language_models" }
|
||||
language_selector = { path = "crates/language_selector" }
|
||||
@ -292,6 +295,7 @@ terminal = { path = "crates/terminal" }
|
||||
terminal_view = { path = "crates/terminal_view" }
|
||||
text = { path = "crates/text" }
|
||||
theme = { path = "crates/theme" }
|
||||
theme_extension = { path = "crates/theme_extension" }
|
||||
theme_importer = { path = "crates/theme_importer" }
|
||||
theme_selector = { path = "crates/theme_selector" }
|
||||
time_format = { path = "crates/time_format" }
|
||||
|
@ -33,7 +33,6 @@ use feature_flags::FeatureFlagAppExt;
|
||||
use fs::Fs;
|
||||
use gpui::impl_actions;
|
||||
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
|
||||
use indexed_docs::IndexedDocsRegistry;
|
||||
pub(crate) use inline_assistant::*;
|
||||
use language_model::{
|
||||
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
|
||||
@ -275,7 +274,7 @@ pub fn init(
|
||||
client.telemetry().clone(),
|
||||
cx,
|
||||
);
|
||||
IndexedDocsRegistry::init_global(cx);
|
||||
indexed_docs::init(cx);
|
||||
|
||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||
filter.hide_namespace(Assistant::NAMESPACE);
|
||||
|
@ -18,6 +18,7 @@ use workspace::{ui::IconName, Workspace};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
SlashCommandRegistry::default_global(cx);
|
||||
extension_slash_command::init(cx);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
@ -3,17 +3,39 @@ use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use extension::{Extension, WorktreeDelegate};
|
||||
use gpui::{Task, WeakView, WindowContext};
|
||||
use extension::{Extension, ExtensionHostProxy, ExtensionSlashCommandProxy, WorktreeDelegate};
|
||||
use gpui::{AppContext, Task, WeakView, WindowContext};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use ui::prelude::*;
|
||||
use workspace::Workspace;
|
||||
|
||||
use crate::{
|
||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||
SlashCommandResult,
|
||||
SlashCommandRegistry, SlashCommandResult,
|
||||
};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let proxy = ExtensionHostProxy::default_global(cx);
|
||||
proxy.register_slash_command_proxy(SlashCommandRegistryProxy {
|
||||
slash_command_registry: SlashCommandRegistry::global(cx),
|
||||
});
|
||||
}
|
||||
|
||||
struct SlashCommandRegistryProxy {
|
||||
slash_command_registry: Arc<SlashCommandRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionSlashCommandProxy for SlashCommandRegistryProxy {
|
||||
fn register_slash_command(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
command: extension::SlashCommand,
|
||||
) {
|
||||
self.slash_command_registry
|
||||
.register_command(ExtensionSlashCommand::new(extension, command), false)
|
||||
}
|
||||
}
|
||||
|
||||
/// An adapter that allows an [`LspAdapterDelegate`] to be used as a [`WorktreeDelegate`].
|
||||
struct WorktreeDelegateAdapter(Arc<dyn LspAdapterDelegate>);
|
||||
|
||||
|
@ -90,6 +90,7 @@ collections = { workspace = true, features = ["test-support"] }
|
||||
ctor.workspace = true
|
||||
editor = { workspace = true, features = ["test-support"] }
|
||||
env_logger.workspace = true
|
||||
extension.workspace = true
|
||||
file_finder.workspace = true
|
||||
fs = { workspace = true, features = ["test-support"] }
|
||||
git = { workspace = true, features = ["test-support"] }
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::tests::TestServer;
|
||||
use call::ActiveCall;
|
||||
use collections::HashSet;
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{FakeFs, Fs as _};
|
||||
use futures::StreamExt as _;
|
||||
use gpui::{BackgroundExecutor, Context as _, SemanticVersion, TestAppContext, UpdateGlobal as _};
|
||||
@ -81,6 +82,7 @@ async fn test_sharing_an_ssh_remote_project(
|
||||
http_client: remote_http_client,
|
||||
node_runtime: node,
|
||||
languages,
|
||||
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
@ -243,6 +245,7 @@ async fn test_ssh_collaboration_git_branches(
|
||||
http_client: remote_http_client,
|
||||
node_runtime: node,
|
||||
languages,
|
||||
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
@ -400,6 +403,7 @@ async fn test_ssh_collaboration_formatting_with_prettier(
|
||||
http_client: remote_http_client,
|
||||
node_runtime: NodeRuntime::unavailable(),
|
||||
languages,
|
||||
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
|
@ -15,6 +15,7 @@ path = "src/context_servers.rs"
|
||||
anyhow.workspace = true
|
||||
collections.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
extension.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
log.workspace = true
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod client;
|
||||
mod extension_context_server;
|
||||
pub mod manager;
|
||||
pub mod protocol;
|
||||
mod registry;
|
||||
@ -19,6 +20,7 @@ pub const CONTEXT_SERVERS_NAMESPACE: &'static str = "context_servers";
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
ContextServerSettings::register(cx);
|
||||
ContextServerFactoryRegistry::default_global(cx);
|
||||
extension_context_server::init(cx);
|
||||
|
||||
CommandPaletteFilter::update_global(cx, |filter, _cx| {
|
||||
filter.hide_namespace(CONTEXT_SERVERS_NAMESPACE);
|
||||
|
78
crates/context_servers/src/extension_context_server.rs
Normal file
78
crates/context_servers/src/extension_context_server.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use extension::{Extension, ExtensionContextServerProxy, ExtensionHostProxy, ProjectDelegate};
|
||||
use gpui::{AppContext, Model};
|
||||
|
||||
use crate::manager::ServerCommand;
|
||||
use crate::ContextServerFactoryRegistry;
|
||||
|
||||
struct ExtensionProject {
|
||||
worktree_ids: Vec<u64>,
|
||||
}
|
||||
|
||||
impl ProjectDelegate for ExtensionProject {
|
||||
fn worktree_ids(&self) -> Vec<u64> {
|
||||
self.worktree_ids.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let proxy = ExtensionHostProxy::default_global(cx);
|
||||
proxy.register_context_server_proxy(ContextServerFactoryRegistryProxy {
|
||||
context_server_factory_registry: ContextServerFactoryRegistry::global(cx),
|
||||
});
|
||||
}
|
||||
|
||||
struct ContextServerFactoryRegistryProxy {
|
||||
context_server_factory_registry: Model<ContextServerFactoryRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionContextServerProxy for ContextServerFactoryRegistryProxy {
|
||||
fn register_context_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
id: Arc<str>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
self.context_server_factory_registry
|
||||
.update(cx, |registry, _| {
|
||||
registry.register_server_factory(
|
||||
id.clone(),
|
||||
Arc::new({
|
||||
move |project, cx| {
|
||||
log::info!(
|
||||
"loading command for context server {id} from extension {}",
|
||||
extension.manifest().id
|
||||
);
|
||||
|
||||
let id = id.clone();
|
||||
let extension = extension.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let extension_project =
|
||||
project.update(&mut cx, |project, cx| {
|
||||
Arc::new(ExtensionProject {
|
||||
worktree_ids: project
|
||||
.visible_worktrees(cx)
|
||||
.map(|worktree| worktree.read(cx).id().to_proto())
|
||||
.collect(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let command = extension
|
||||
.context_server_command(id.clone(), extension_project)
|
||||
.await?;
|
||||
|
||||
log::info!("loaded command for context server {id}: {command:?}");
|
||||
|
||||
Ok(ServerCommand {
|
||||
path: command.command,
|
||||
args: command.args,
|
||||
env: Some(command.env.into_iter().collect()),
|
||||
})
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ http_client.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
parking_lot.workspace = true
|
||||
semantic_version.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod extension_builder;
|
||||
mod extension_host_proxy;
|
||||
mod extension_manifest;
|
||||
mod types;
|
||||
|
||||
@ -9,13 +10,19 @@ use ::lsp::LanguageServerName;
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
use fs::normalize_path;
|
||||
use gpui::Task;
|
||||
use gpui::{AppContext, Task};
|
||||
use language::LanguageName;
|
||||
use semantic_version::SemanticVersion;
|
||||
|
||||
pub use crate::extension_host_proxy::*;
|
||||
pub use crate::extension_manifest::*;
|
||||
pub use crate::types::*;
|
||||
|
||||
/// Initializes the `extension` crate.
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
ExtensionHostProxy::default_global(cx);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait WorktreeDelegate: Send + Sync + 'static {
|
||||
fn id(&self) -> u64;
|
||||
|
324
crates/extension/src/extension_host_proxy.rs
Normal file
324
crates/extension/src/extension_host_proxy.rs
Normal file
@ -0,0 +1,324 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, Global, ReadGlobal, SharedString, Task};
|
||||
use language::{LanguageMatcher, LanguageName, LanguageServerBinaryStatus, LoadedLanguage};
|
||||
use lsp::LanguageServerName;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::{Extension, SlashCommand};
|
||||
|
||||
#[derive(Default)]
|
||||
struct GlobalExtensionHostProxy(Arc<ExtensionHostProxy>);
|
||||
|
||||
impl Global for GlobalExtensionHostProxy {}
|
||||
|
||||
/// A proxy for interacting with the extension host.
|
||||
///
|
||||
/// This object implements each of the individual proxy types so that their
|
||||
/// methods can be called directly on it.
|
||||
#[derive(Default)]
|
||||
pub struct ExtensionHostProxy {
|
||||
theme_proxy: RwLock<Option<Arc<dyn ExtensionThemeProxy>>>,
|
||||
grammar_proxy: RwLock<Option<Arc<dyn ExtensionGrammarProxy>>>,
|
||||
language_proxy: RwLock<Option<Arc<dyn ExtensionLanguageProxy>>>,
|
||||
language_server_proxy: RwLock<Option<Arc<dyn ExtensionLanguageServerProxy>>>,
|
||||
snippet_proxy: RwLock<Option<Arc<dyn ExtensionSnippetProxy>>>,
|
||||
slash_command_proxy: RwLock<Option<Arc<dyn ExtensionSlashCommandProxy>>>,
|
||||
context_server_proxy: RwLock<Option<Arc<dyn ExtensionContextServerProxy>>>,
|
||||
indexed_docs_provider_proxy: RwLock<Option<Arc<dyn ExtensionIndexedDocsProviderProxy>>>,
|
||||
}
|
||||
|
||||
impl ExtensionHostProxy {
|
||||
/// Returns the global [`ExtensionHostProxy`].
|
||||
pub fn global(cx: &AppContext) -> Arc<Self> {
|
||||
GlobalExtensionHostProxy::global(cx).0.clone()
|
||||
}
|
||||
|
||||
/// Returns the global [`ExtensionHostProxy`].
|
||||
///
|
||||
/// Inserts a default [`ExtensionHostProxy`] if one does not yet exist.
|
||||
pub fn default_global(cx: &mut AppContext) -> Arc<Self> {
|
||||
cx.default_global::<GlobalExtensionHostProxy>().0.clone()
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
theme_proxy: RwLock::default(),
|
||||
grammar_proxy: RwLock::default(),
|
||||
language_proxy: RwLock::default(),
|
||||
language_server_proxy: RwLock::default(),
|
||||
snippet_proxy: RwLock::default(),
|
||||
slash_command_proxy: RwLock::default(),
|
||||
context_server_proxy: RwLock::default(),
|
||||
indexed_docs_provider_proxy: RwLock::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_theme_proxy(&self, proxy: impl ExtensionThemeProxy) {
|
||||
self.theme_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_grammar_proxy(&self, proxy: impl ExtensionGrammarProxy) {
|
||||
self.grammar_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_language_proxy(&self, proxy: impl ExtensionLanguageProxy) {
|
||||
self.language_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_language_server_proxy(&self, proxy: impl ExtensionLanguageServerProxy) {
|
||||
self.language_server_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_snippet_proxy(&self, proxy: impl ExtensionSnippetProxy) {
|
||||
self.snippet_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_slash_command_proxy(&self, proxy: impl ExtensionSlashCommandProxy) {
|
||||
self.slash_command_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_context_server_proxy(&self, proxy: impl ExtensionContextServerProxy) {
|
||||
self.context_server_proxy.write().replace(Arc::new(proxy));
|
||||
}
|
||||
|
||||
pub fn register_indexed_docs_provider_proxy(
|
||||
&self,
|
||||
proxy: impl ExtensionIndexedDocsProviderProxy,
|
||||
) {
|
||||
self.indexed_docs_provider_proxy
|
||||
.write()
|
||||
.replace(Arc::new(proxy));
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionThemeProxy: Send + Sync + 'static {
|
||||
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
|
||||
|
||||
fn remove_user_themes(&self, themes: Vec<SharedString>);
|
||||
|
||||
fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>>;
|
||||
|
||||
fn reload_current_theme(&self, cx: &mut AppContext);
|
||||
}
|
||||
|
||||
impl ExtensionThemeProxy for ExtensionHostProxy {
|
||||
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
|
||||
let Some(proxy) = self.theme_proxy.read().clone() else {
|
||||
return Task::ready(Ok(Vec::new()));
|
||||
};
|
||||
|
||||
proxy.list_theme_names(theme_path, fs)
|
||||
}
|
||||
|
||||
fn remove_user_themes(&self, themes: Vec<SharedString>) {
|
||||
let Some(proxy) = self.theme_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.remove_user_themes(themes)
|
||||
}
|
||||
|
||||
fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
|
||||
let Some(proxy) = self.theme_proxy.read().clone() else {
|
||||
return Task::ready(Ok(()));
|
||||
};
|
||||
|
||||
proxy.load_user_theme(theme_path, fs)
|
||||
}
|
||||
|
||||
fn reload_current_theme(&self, cx: &mut AppContext) {
|
||||
let Some(proxy) = self.theme_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.reload_current_theme(cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionGrammarProxy: Send + Sync + 'static {
|
||||
fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>);
|
||||
}
|
||||
|
||||
impl ExtensionGrammarProxy for ExtensionHostProxy {
|
||||
fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
|
||||
let Some(proxy) = self.grammar_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_grammars(grammars)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionLanguageProxy: Send + Sync + 'static {
|
||||
fn register_language(
|
||||
&self,
|
||||
language: LanguageName,
|
||||
grammar: Option<Arc<str>>,
|
||||
matcher: LanguageMatcher,
|
||||
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
|
||||
);
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
languages_to_remove: &[LanguageName],
|
||||
grammars_to_remove: &[Arc<str>],
|
||||
);
|
||||
}
|
||||
|
||||
impl ExtensionLanguageProxy for ExtensionHostProxy {
|
||||
fn register_language(
|
||||
&self,
|
||||
language: LanguageName,
|
||||
grammar: Option<Arc<str>>,
|
||||
matcher: LanguageMatcher,
|
||||
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
|
||||
) {
|
||||
let Some(proxy) = self.language_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_language(language, grammar, matcher, load)
|
||||
}
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
languages_to_remove: &[LanguageName],
|
||||
grammars_to_remove: &[Arc<str>],
|
||||
) {
|
||||
let Some(proxy) = self.language_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.remove_languages(languages_to_remove, grammars_to_remove)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionLanguageServerProxy: Send + Sync + 'static {
|
||||
fn register_language_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language: LanguageName,
|
||||
);
|
||||
|
||||
fn remove_language_server(
|
||||
&self,
|
||||
language: &LanguageName,
|
||||
language_server_id: &LanguageServerName,
|
||||
);
|
||||
|
||||
fn update_language_server_status(
|
||||
&self,
|
||||
language_server_id: LanguageServerName,
|
||||
status: LanguageServerBinaryStatus,
|
||||
);
|
||||
}
|
||||
|
||||
impl ExtensionLanguageServerProxy for ExtensionHostProxy {
|
||||
fn register_language_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language: LanguageName,
|
||||
) {
|
||||
let Some(proxy) = self.language_server_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_language_server(extension, language_server_id, language)
|
||||
}
|
||||
|
||||
fn remove_language_server(
|
||||
&self,
|
||||
language: &LanguageName,
|
||||
language_server_id: &LanguageServerName,
|
||||
) {
|
||||
let Some(proxy) = self.language_server_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.remove_language_server(language, language_server_id)
|
||||
}
|
||||
|
||||
fn update_language_server_status(
|
||||
&self,
|
||||
language_server_id: LanguageServerName,
|
||||
status: LanguageServerBinaryStatus,
|
||||
) {
|
||||
let Some(proxy) = self.language_server_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.update_language_server_status(language_server_id, status)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionSnippetProxy: Send + Sync + 'static {
|
||||
fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
impl ExtensionSnippetProxy for ExtensionHostProxy {
|
||||
fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
|
||||
let Some(proxy) = self.snippet_proxy.read().clone() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
proxy.register_snippet(path, snippet_contents)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionSlashCommandProxy: Send + Sync + 'static {
|
||||
fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand);
|
||||
}
|
||||
|
||||
impl ExtensionSlashCommandProxy for ExtensionHostProxy {
|
||||
fn register_slash_command(&self, extension: Arc<dyn Extension>, command: SlashCommand) {
|
||||
let Some(proxy) = self.slash_command_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_slash_command(extension, command)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionContextServerProxy: Send + Sync + 'static {
|
||||
fn register_context_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
server_id: Arc<str>,
|
||||
cx: &mut AppContext,
|
||||
);
|
||||
}
|
||||
|
||||
impl ExtensionContextServerProxy for ExtensionHostProxy {
|
||||
fn register_context_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
server_id: Arc<str>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
let Some(proxy) = self.context_server_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_context_server(extension, server_id, cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExtensionIndexedDocsProviderProxy: Send + Sync + 'static {
|
||||
fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>);
|
||||
}
|
||||
|
||||
impl ExtensionIndexedDocsProviderProxy for ExtensionHostProxy {
|
||||
fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
|
||||
let Some(proxy) = self.indexed_docs_provider_proxy.read().clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
proxy.register_indexed_docs_provider(extension, provider_id)
|
||||
}
|
||||
}
|
@ -57,7 +57,9 @@ env_logger.workspace = true
|
||||
fs = { workspace = true, features = ["test-support"] }
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
language = { workspace = true, features = ["test-support"] }
|
||||
language_extension.workspace = true
|
||||
parking_lot.workspace = true
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
reqwest_client.workspace = true
|
||||
theme = { workspace = true, features = ["test-support"] }
|
||||
theme_extension.workspace = true
|
||||
|
@ -1,4 +1,3 @@
|
||||
pub mod extension_lsp_adapter;
|
||||
pub mod extension_settings;
|
||||
pub mod headless_host;
|
||||
pub mod wasm_host;
|
||||
@ -12,8 +11,12 @@ use async_tar::Archive;
|
||||
use client::{proto, telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
|
||||
use collections::{btree_map, BTreeMap, HashMap, HashSet};
|
||||
use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
|
||||
use extension::Extension;
|
||||
pub use extension::ExtensionManifest;
|
||||
use extension::{
|
||||
ExtensionContextServerProxy, ExtensionGrammarProxy, ExtensionHostProxy,
|
||||
ExtensionIndexedDocsProviderProxy, ExtensionLanguageProxy, ExtensionLanguageServerProxy,
|
||||
ExtensionSlashCommandProxy, ExtensionSnippetProxy, ExtensionThemeProxy,
|
||||
};
|
||||
use fs::{Fs, RemoveOptions};
|
||||
use futures::{
|
||||
channel::{
|
||||
@ -24,15 +27,14 @@ use futures::{
|
||||
select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
|
||||
};
|
||||
use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext,
|
||||
SharedString, Task, WeakModel,
|
||||
actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Task,
|
||||
WeakModel,
|
||||
};
|
||||
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use language::{
|
||||
LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
|
||||
QUERY_FILENAME_PREFIXES,
|
||||
};
|
||||
use lsp::LanguageServerName;
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::ContextProviderWithTasks;
|
||||
use release_channel::ReleaseChannel;
|
||||
@ -95,82 +97,8 @@ pub fn is_version_compatible(
|
||||
true
|
||||
}
|
||||
|
||||
pub trait ExtensionRegistrationHooks: Send + Sync + 'static {
|
||||
fn remove_user_themes(&self, _themes: Vec<SharedString>) {}
|
||||
|
||||
fn load_user_theme(&self, _theme_path: PathBuf, _fs: Arc<dyn Fs>) -> Task<Result<()>> {
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
|
||||
fn list_theme_names(
|
||||
&self,
|
||||
_theme_path: PathBuf,
|
||||
_fs: Arc<dyn Fs>,
|
||||
) -> Task<Result<Vec<String>>> {
|
||||
Task::ready(Ok(Vec::new()))
|
||||
}
|
||||
|
||||
fn reload_current_theme(&self, _cx: &mut AppContext) {}
|
||||
|
||||
fn register_language(
|
||||
&self,
|
||||
_language: LanguageName,
|
||||
_grammar: Option<Arc<str>>,
|
||||
_matcher: language::LanguageMatcher,
|
||||
_load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn register_lsp_adapter(
|
||||
&self,
|
||||
_extension: Arc<dyn Extension>,
|
||||
_language_server_id: LanguageServerName,
|
||||
_language: LanguageName,
|
||||
) {
|
||||
}
|
||||
|
||||
fn remove_lsp_adapter(&self, _language: &LanguageName, _server_name: &LanguageServerName) {}
|
||||
|
||||
fn register_wasm_grammars(&self, _grammars: Vec<(Arc<str>, PathBuf)>) {}
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
_languages_to_remove: &[LanguageName],
|
||||
_grammars_to_remove: &[Arc<str>],
|
||||
) {
|
||||
}
|
||||
|
||||
fn register_slash_command(
|
||||
&self,
|
||||
_extension: Arc<dyn Extension>,
|
||||
_command: extension::SlashCommand,
|
||||
) {
|
||||
}
|
||||
|
||||
fn register_context_server(
|
||||
&self,
|
||||
_extension: Arc<dyn Extension>,
|
||||
_id: Arc<str>,
|
||||
_cx: &mut AppContext,
|
||||
) {
|
||||
}
|
||||
|
||||
fn register_docs_provider(&self, _extension: Arc<dyn Extension>, _provider_id: Arc<str>) {}
|
||||
|
||||
fn register_snippets(&self, _path: &PathBuf, _snippet_contents: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_lsp_status(
|
||||
&self,
|
||||
_server_name: lsp::LanguageServerName,
|
||||
_status: language::LanguageServerBinaryStatus,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtensionStore {
|
||||
pub registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
|
||||
pub proxy: Arc<ExtensionHostProxy>,
|
||||
pub builder: Arc<ExtensionBuilder>,
|
||||
pub extension_index: ExtensionIndex,
|
||||
pub fs: Arc<dyn Fs>,
|
||||
@ -240,7 +168,7 @@ pub struct ExtensionIndexLanguageEntry {
|
||||
actions!(zed, [ReloadExtensions]);
|
||||
|
||||
pub fn init(
|
||||
registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
|
||||
extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
fs: Arc<dyn Fs>,
|
||||
client: Arc<Client>,
|
||||
node_runtime: NodeRuntime,
|
||||
@ -252,7 +180,7 @@ pub fn init(
|
||||
ExtensionStore::new(
|
||||
paths::extensions_dir().clone(),
|
||||
None,
|
||||
registration_hooks,
|
||||
extension_host_proxy,
|
||||
fs,
|
||||
client.http_client().clone(),
|
||||
client.http_client().clone(),
|
||||
@ -284,7 +212,7 @@ impl ExtensionStore {
|
||||
pub fn new(
|
||||
extensions_dir: PathBuf,
|
||||
build_dir: Option<PathBuf>,
|
||||
extension_api: Arc<dyn ExtensionRegistrationHooks>,
|
||||
extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
fs: Arc<dyn Fs>,
|
||||
http_client: Arc<HttpClientWithUrl>,
|
||||
builder_client: Arc<dyn HttpClient>,
|
||||
@ -300,7 +228,7 @@ impl ExtensionStore {
|
||||
let (reload_tx, mut reload_rx) = unbounded();
|
||||
let (connection_registered_tx, mut connection_registered_rx) = unbounded();
|
||||
let mut this = Self {
|
||||
registration_hooks: extension_api.clone(),
|
||||
proxy: extension_host_proxy.clone(),
|
||||
extension_index: Default::default(),
|
||||
installed_dir,
|
||||
index_path,
|
||||
@ -312,7 +240,7 @@ impl ExtensionStore {
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
node_runtime,
|
||||
extension_api,
|
||||
extension_host_proxy,
|
||||
work_dir,
|
||||
cx,
|
||||
),
|
||||
@ -1113,16 +1041,16 @@ impl ExtensionStore {
|
||||
grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
|
||||
for (language_server_name, config) in extension.manifest.language_servers.iter() {
|
||||
for language in config.languages() {
|
||||
self.registration_hooks
|
||||
.remove_lsp_adapter(&language, language_server_name);
|
||||
self.proxy
|
||||
.remove_language_server(&language, language_server_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.wasm_extensions
|
||||
.retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
|
||||
self.registration_hooks.remove_user_themes(themes_to_remove);
|
||||
self.registration_hooks
|
||||
self.proxy.remove_user_themes(themes_to_remove);
|
||||
self.proxy
|
||||
.remove_languages(&languages_to_remove, &grammars_to_remove);
|
||||
|
||||
let languages_to_add = new_index
|
||||
@ -1157,8 +1085,7 @@ impl ExtensionStore {
|
||||
}));
|
||||
}
|
||||
|
||||
self.registration_hooks
|
||||
.register_wasm_grammars(grammars_to_add);
|
||||
self.proxy.register_grammars(grammars_to_add);
|
||||
|
||||
for (language_name, language) in languages_to_add {
|
||||
let mut language_path = self.installed_dir.clone();
|
||||
@ -1166,7 +1093,7 @@ impl ExtensionStore {
|
||||
Path::new(language.extension.as_ref()),
|
||||
language.path.as_path(),
|
||||
]);
|
||||
self.registration_hooks.register_language(
|
||||
self.proxy.register_language(
|
||||
language_name.clone(),
|
||||
language.grammar.clone(),
|
||||
language.matcher.clone(),
|
||||
@ -1196,7 +1123,7 @@ impl ExtensionStore {
|
||||
let fs = self.fs.clone();
|
||||
let wasm_host = self.wasm_host.clone();
|
||||
let root_dir = self.installed_dir.clone();
|
||||
let api = self.registration_hooks.clone();
|
||||
let proxy = self.proxy.clone();
|
||||
let extension_entries = extensions_to_load
|
||||
.iter()
|
||||
.filter_map(|name| new_index.extensions.get(name).cloned())
|
||||
@ -1212,13 +1139,17 @@ impl ExtensionStore {
|
||||
let fs = fs.clone();
|
||||
async move {
|
||||
for theme_path in themes_to_add.into_iter() {
|
||||
api.load_user_theme(theme_path, fs.clone()).await.log_err();
|
||||
proxy
|
||||
.load_user_theme(theme_path, fs.clone())
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
for snippets_path in &snippets_to_add {
|
||||
if let Some(snippets_contents) = fs.load(snippets_path).await.log_err()
|
||||
{
|
||||
api.register_snippets(snippets_path, &snippets_contents)
|
||||
proxy
|
||||
.register_snippet(snippets_path, &snippets_contents)
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
@ -1259,7 +1190,7 @@ impl ExtensionStore {
|
||||
|
||||
for (language_server_id, language_server_config) in &manifest.language_servers {
|
||||
for language in language_server_config.languages() {
|
||||
this.registration_hooks.register_lsp_adapter(
|
||||
this.proxy.register_language_server(
|
||||
extension.clone(),
|
||||
language_server_id.clone(),
|
||||
language.clone(),
|
||||
@ -1268,7 +1199,7 @@ impl ExtensionStore {
|
||||
}
|
||||
|
||||
for (slash_command_name, slash_command) in &manifest.slash_commands {
|
||||
this.registration_hooks.register_slash_command(
|
||||
this.proxy.register_slash_command(
|
||||
extension.clone(),
|
||||
extension::SlashCommand {
|
||||
name: slash_command_name.to_string(),
|
||||
@ -1283,21 +1214,18 @@ impl ExtensionStore {
|
||||
}
|
||||
|
||||
for (id, _context_server_entry) in &manifest.context_servers {
|
||||
this.registration_hooks.register_context_server(
|
||||
extension.clone(),
|
||||
id.clone(),
|
||||
cx,
|
||||
);
|
||||
this.proxy
|
||||
.register_context_server(extension.clone(), id.clone(), cx);
|
||||
}
|
||||
|
||||
for (provider_id, _provider) in &manifest.indexed_docs_providers {
|
||||
this.registration_hooks
|
||||
.register_docs_provider(extension.clone(), provider_id.clone());
|
||||
this.proxy
|
||||
.register_indexed_docs_provider(extension.clone(), provider_id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
this.wasm_extensions.extend(wasm_extensions);
|
||||
this.registration_hooks.reload_current_theme(cx);
|
||||
this.proxy.reload_current_theme(cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
@ -1308,7 +1236,7 @@ impl ExtensionStore {
|
||||
let work_dir = self.wasm_host.work_dir.clone();
|
||||
let extensions_dir = self.installed_dir.clone();
|
||||
let index_path = self.index_path.clone();
|
||||
let extension_api = self.registration_hooks.clone();
|
||||
let proxy = self.proxy.clone();
|
||||
cx.background_executor().spawn(async move {
|
||||
let start_time = Instant::now();
|
||||
let mut index = ExtensionIndex::default();
|
||||
@ -1334,7 +1262,7 @@ impl ExtensionStore {
|
||||
fs.clone(),
|
||||
extension_dir,
|
||||
&mut index,
|
||||
extension_api.clone(),
|
||||
proxy.clone(),
|
||||
)
|
||||
.await
|
||||
.log_err();
|
||||
@ -1357,7 +1285,7 @@ impl ExtensionStore {
|
||||
fs: Arc<dyn Fs>,
|
||||
extension_dir: PathBuf,
|
||||
index: &mut ExtensionIndex,
|
||||
extension_api: Arc<dyn ExtensionRegistrationHooks>,
|
||||
proxy: Arc<ExtensionHostProxy>,
|
||||
) -> Result<()> {
|
||||
let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
|
||||
let extension_id = extension_manifest.id.clone();
|
||||
@ -1409,7 +1337,7 @@ impl ExtensionStore {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(theme_families) = extension_api
|
||||
let Some(theme_families) = proxy
|
||||
.list_theme_names(theme_path.clone(), fs.clone())
|
||||
.await
|
||||
.log_err()
|
||||
|
@ -1,20 +1,16 @@
|
||||
use crate::extension_lsp_adapter::ExtensionLspAdapter;
|
||||
use crate::{
|
||||
Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry,
|
||||
ExtensionIndexThemeEntry, ExtensionManifest, ExtensionSettings, ExtensionStore,
|
||||
GrammarManifestEntry, SchemaVersion, RELOAD_DEBOUNCE_DURATION,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_compression::futures::bufread::GzipEncoder;
|
||||
use collections::BTreeMap;
|
||||
use extension::Extension;
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{FakeFs, Fs, RealFs};
|
||||
use futures::{io::BufReader, AsyncReadExt, StreamExt};
|
||||
use gpui::{BackgroundExecutor, Context, SemanticVersion, SharedString, Task, TestAppContext};
|
||||
use gpui::{Context, SemanticVersion, TestAppContext};
|
||||
use http_client::{FakeHttpClient, Response};
|
||||
use language::{
|
||||
LanguageMatcher, LanguageName, LanguageRegistry, LanguageServerBinaryStatus, LoadedLanguage,
|
||||
};
|
||||
use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus};
|
||||
use lsp::LanguageServerName;
|
||||
use node_runtime::NodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
@ -31,91 +27,6 @@ use std::{
|
||||
use theme::ThemeRegistry;
|
||||
use util::test::temp_tree;
|
||||
|
||||
use crate::ExtensionRegistrationHooks;
|
||||
|
||||
struct TestExtensionRegistrationHooks {
|
||||
executor: BackgroundExecutor,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
theme_registry: Arc<ThemeRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionRegistrationHooks for TestExtensionRegistrationHooks {
|
||||
fn list_theme_names(&self, path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
|
||||
self.executor.spawn(async move {
|
||||
let themes = theme::read_user_theme(&path, fs).await?;
|
||||
Ok(themes.themes.into_iter().map(|theme| theme.name).collect())
|
||||
})
|
||||
}
|
||||
|
||||
fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn fs::Fs>) -> Task<Result<()>> {
|
||||
let theme_registry = self.theme_registry.clone();
|
||||
self.executor
|
||||
.spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
|
||||
}
|
||||
|
||||
fn remove_user_themes(&self, themes: Vec<SharedString>) {
|
||||
self.theme_registry.remove_user_themes(&themes);
|
||||
}
|
||||
|
||||
fn register_language(
|
||||
&self,
|
||||
language: language::LanguageName,
|
||||
grammar: Option<Arc<str>>,
|
||||
matcher: language::LanguageMatcher,
|
||||
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
|
||||
) {
|
||||
self.language_registry
|
||||
.register_language(language, grammar, matcher, load)
|
||||
}
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
languages_to_remove: &[language::LanguageName],
|
||||
grammars_to_remove: &[Arc<str>],
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_languages(&languages_to_remove, &grammars_to_remove);
|
||||
}
|
||||
|
||||
fn register_wasm_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
|
||||
self.language_registry.register_wasm_grammars(grammars)
|
||||
}
|
||||
|
||||
fn register_lsp_adapter(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language: LanguageName,
|
||||
) {
|
||||
self.language_registry.register_lsp_adapter(
|
||||
language.clone(),
|
||||
Arc::new(ExtensionLspAdapter::new(
|
||||
extension,
|
||||
language_server_id,
|
||||
language,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
fn update_lsp_status(
|
||||
&self,
|
||||
server_name: lsp::LanguageServerName,
|
||||
status: LanguageServerBinaryStatus,
|
||||
) {
|
||||
self.language_registry
|
||||
.update_lsp_status(server_name, status);
|
||||
}
|
||||
|
||||
fn remove_lsp_adapter(
|
||||
&self,
|
||||
language_name: &language::LanguageName,
|
||||
server_name: &lsp::LanguageServerName,
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_lsp_adapter(language_name, server_name);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
@ -347,20 +258,18 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
|
||||
let proxy = Arc::new(ExtensionHostProxy::new());
|
||||
let theme_registry = Arc::new(ThemeRegistry::new(Box::new(())));
|
||||
let registration_hooks = Arc::new(TestExtensionRegistrationHooks {
|
||||
executor: cx.executor(),
|
||||
language_registry: language_registry.clone(),
|
||||
theme_registry: theme_registry.clone(),
|
||||
});
|
||||
theme_extension::init(proxy.clone(), theme_registry.clone(), cx.executor());
|
||||
let language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
|
||||
language_extension::init(proxy.clone(), language_registry.clone());
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
|
||||
let store = cx.new_model(|cx| {
|
||||
ExtensionStore::new(
|
||||
PathBuf::from("/the-extension-dir"),
|
||||
None,
|
||||
registration_hooks.clone(),
|
||||
proxy.clone(),
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
http_client.clone(),
|
||||
@ -485,7 +394,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
||||
ExtensionStore::new(
|
||||
PathBuf::from("/the-extension-dir"),
|
||||
None,
|
||||
registration_hooks,
|
||||
proxy,
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
http_client.clone(),
|
||||
@ -568,13 +477,11 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
|
||||
|
||||
let project = Project::test(fs.clone(), [project_dir.as_path()], cx).await;
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _cx| project.languages().clone());
|
||||
let proxy = Arc::new(ExtensionHostProxy::new());
|
||||
let theme_registry = Arc::new(ThemeRegistry::new(Box::new(())));
|
||||
let registration_hooks = Arc::new(TestExtensionRegistrationHooks {
|
||||
executor: cx.executor(),
|
||||
language_registry: language_registry.clone(),
|
||||
theme_registry: theme_registry.clone(),
|
||||
});
|
||||
theme_extension::init(proxy.clone(), theme_registry.clone(), cx.executor());
|
||||
let language_registry = project.read_with(cx, |project, _cx| project.languages().clone());
|
||||
language_extension::init(proxy.clone(), language_registry.clone());
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
|
||||
let mut status_updates = language_registry.language_server_binary_statuses();
|
||||
@ -668,7 +575,7 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
|
||||
ExtensionStore::new(
|
||||
extensions_dir.clone(),
|
||||
Some(cache_dir),
|
||||
registration_hooks,
|
||||
proxy,
|
||||
fs.clone(),
|
||||
extension_client.clone(),
|
||||
builder_client,
|
||||
|
@ -3,29 +3,18 @@ use std::{path::PathBuf, sync::Arc};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use client::{proto, TypedEnvelope};
|
||||
use collections::{HashMap, HashSet};
|
||||
use extension::{Extension, ExtensionManifest};
|
||||
use extension::{
|
||||
Extension, ExtensionHostProxy, ExtensionLanguageProxy, ExtensionLanguageServerProxy,
|
||||
ExtensionManifest,
|
||||
};
|
||||
use fs::{Fs, RemoveOptions, RenameOptions};
|
||||
use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext, Task, WeakModel};
|
||||
use http_client::HttpClient;
|
||||
use language::{LanguageConfig, LanguageName, LanguageQueries, LanguageRegistry, LoadedLanguage};
|
||||
use language::{LanguageConfig, LanguageName, LanguageQueries, LoadedLanguage};
|
||||
use lsp::LanguageServerName;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
||||
use crate::{
|
||||
extension_lsp_adapter::ExtensionLspAdapter,
|
||||
wasm_host::{WasmExtension, WasmHost},
|
||||
ExtensionRegistrationHooks,
|
||||
};
|
||||
|
||||
pub struct HeadlessExtensionStore {
|
||||
pub registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
|
||||
pub fs: Arc<dyn Fs>,
|
||||
pub extension_dir: PathBuf,
|
||||
pub wasm_host: Arc<WasmHost>,
|
||||
pub loaded_extensions: HashMap<Arc<str>, Arc<str>>,
|
||||
pub loaded_languages: HashMap<Arc<str>, Vec<LanguageName>>,
|
||||
pub loaded_language_servers: HashMap<Arc<str>, Vec<(LanguageServerName, LanguageName)>>,
|
||||
}
|
||||
use crate::wasm_host::{WasmExtension, WasmHost};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExtensionVersion {
|
||||
@ -34,28 +23,37 @@ pub struct ExtensionVersion {
|
||||
pub dev: bool,
|
||||
}
|
||||
|
||||
pub struct HeadlessExtensionStore {
|
||||
pub fs: Arc<dyn Fs>,
|
||||
pub extension_dir: PathBuf,
|
||||
pub proxy: Arc<ExtensionHostProxy>,
|
||||
pub wasm_host: Arc<WasmHost>,
|
||||
pub loaded_extensions: HashMap<Arc<str>, Arc<str>>,
|
||||
pub loaded_languages: HashMap<Arc<str>, Vec<LanguageName>>,
|
||||
pub loaded_language_servers: HashMap<Arc<str>, Vec<(LanguageServerName, LanguageName)>>,
|
||||
}
|
||||
|
||||
impl HeadlessExtensionStore {
|
||||
pub fn new(
|
||||
fs: Arc<dyn Fs>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
extension_dir: PathBuf,
|
||||
extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
node_runtime: NodeRuntime,
|
||||
cx: &mut AppContext,
|
||||
) -> Model<Self> {
|
||||
let registration_hooks = Arc::new(HeadlessRegistrationHooks::new(languages.clone()));
|
||||
cx.new_model(|cx| Self {
|
||||
registration_hooks: registration_hooks.clone(),
|
||||
fs: fs.clone(),
|
||||
wasm_host: WasmHost::new(
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
node_runtime,
|
||||
registration_hooks,
|
||||
extension_host_proxy.clone(),
|
||||
extension_dir.join("work"),
|
||||
cx,
|
||||
),
|
||||
extension_dir,
|
||||
proxy: extension_host_proxy,
|
||||
loaded_extensions: Default::default(),
|
||||
loaded_languages: Default::default(),
|
||||
loaded_language_servers: Default::default(),
|
||||
@ -154,7 +152,7 @@ impl HeadlessExtensionStore {
|
||||
|
||||
config.grammar = None;
|
||||
|
||||
this.registration_hooks.register_language(
|
||||
this.proxy.register_language(
|
||||
config.name.clone(),
|
||||
None,
|
||||
config.matcher.clone(),
|
||||
@ -184,7 +182,7 @@ impl HeadlessExtensionStore {
|
||||
.entry(manifest.id.clone())
|
||||
.or_default()
|
||||
.push((language_server_id.clone(), language.clone()));
|
||||
this.registration_hooks.register_lsp_adapter(
|
||||
this.proxy.register_language_server(
|
||||
wasm_extension.clone(),
|
||||
language_server_id.clone(),
|
||||
language.clone(),
|
||||
@ -202,19 +200,20 @@ impl HeadlessExtensionStore {
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
self.loaded_extensions.remove(extension_id);
|
||||
|
||||
let languages_to_remove = self
|
||||
.loaded_languages
|
||||
.remove(extension_id)
|
||||
.unwrap_or_default();
|
||||
self.registration_hooks
|
||||
.remove_languages(&languages_to_remove, &[]);
|
||||
self.proxy.remove_languages(&languages_to_remove, &[]);
|
||||
|
||||
for (language_server_name, language) in self
|
||||
.loaded_language_servers
|
||||
.remove(extension_id)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
self.registration_hooks
|
||||
.remove_lsp_adapter(&language, &language_server_name);
|
||||
self.proxy
|
||||
.remove_language_server(&language, &language_server_name);
|
||||
}
|
||||
|
||||
let path = self.extension_dir.join(&extension_id.to_string());
|
||||
@ -318,71 +317,3 @@ impl HeadlessExtensionStore {
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
}
|
||||
|
||||
struct HeadlessRegistrationHooks {
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
}
|
||||
|
||||
impl HeadlessRegistrationHooks {
|
||||
fn new(language_registry: Arc<LanguageRegistry>) -> Self {
|
||||
Self { language_registry }
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtensionRegistrationHooks for HeadlessRegistrationHooks {
|
||||
fn register_language(
|
||||
&self,
|
||||
language: LanguageName,
|
||||
_grammar: Option<Arc<str>>,
|
||||
matcher: language::LanguageMatcher,
|
||||
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
|
||||
) {
|
||||
log::info!("registering language: {:?}", language);
|
||||
self.language_registry
|
||||
.register_language(language, None, matcher, load)
|
||||
}
|
||||
|
||||
fn register_lsp_adapter(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language: LanguageName,
|
||||
) {
|
||||
log::info!("registering lsp adapter {:?}", language);
|
||||
self.language_registry.register_lsp_adapter(
|
||||
language.clone(),
|
||||
Arc::new(ExtensionLspAdapter::new(
|
||||
extension,
|
||||
language_server_id,
|
||||
language,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
fn register_wasm_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
|
||||
self.language_registry.register_wasm_grammars(grammars)
|
||||
}
|
||||
|
||||
fn remove_lsp_adapter(&self, language: &LanguageName, server_name: &LanguageServerName) {
|
||||
self.language_registry
|
||||
.remove_lsp_adapter(language, server_name)
|
||||
}
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
languages_to_remove: &[LanguageName],
|
||||
_grammars_to_remove: &[Arc<str>],
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_languages(languages_to_remove, &[])
|
||||
}
|
||||
|
||||
fn update_lsp_status(
|
||||
&self,
|
||||
server_name: LanguageServerName,
|
||||
status: language::LanguageServerBinaryStatus,
|
||||
) {
|
||||
self.language_registry
|
||||
.update_lsp_status(server_name, status)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
pub mod wit;
|
||||
|
||||
use crate::{ExtensionManifest, ExtensionRegistrationHooks};
|
||||
use crate::ExtensionManifest;
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
use extension::{
|
||||
CodeLabel, Command, Completion, KeyValueStoreDelegate, ProjectDelegate, SlashCommand,
|
||||
SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate,
|
||||
CodeLabel, Command, Completion, ExtensionHostProxy, KeyValueStoreDelegate, ProjectDelegate,
|
||||
SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate,
|
||||
};
|
||||
use fs::{normalize_path, Fs};
|
||||
use futures::future::LocalBoxFuture;
|
||||
@ -40,7 +40,7 @@ pub struct WasmHost {
|
||||
release_channel: ReleaseChannel,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
pub registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
|
||||
pub(crate) proxy: Arc<ExtensionHostProxy>,
|
||||
fs: Arc<dyn Fs>,
|
||||
pub work_dir: PathBuf,
|
||||
_main_thread_message_task: Task<()>,
|
||||
@ -330,7 +330,7 @@ impl WasmHost {
|
||||
fs: Arc<dyn Fs>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
|
||||
proxy: Arc<ExtensionHostProxy>,
|
||||
work_dir: PathBuf,
|
||||
cx: &mut AppContext,
|
||||
) -> Arc<Self> {
|
||||
@ -346,7 +346,7 @@ impl WasmHost {
|
||||
work_dir,
|
||||
http_client,
|
||||
node_runtime,
|
||||
registration_hooks,
|
||||
proxy,
|
||||
release_channel: ReleaseChannel::global(cx),
|
||||
_main_thread_message_task: task,
|
||||
main_thread_message_tx: tx,
|
||||
|
@ -3,7 +3,7 @@ use crate::wasm_host::wit::since_v0_0_4;
|
||||
use crate::wasm_host::WasmState;
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use extension::WorktreeDelegate;
|
||||
use extension::{ExtensionLanguageServerProxy, WorktreeDelegate};
|
||||
use language::LanguageServerBinaryStatus;
|
||||
use semantic_version::SemanticVersion;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
@ -149,8 +149,9 @@ impl ExtensionImports for WasmState {
|
||||
};
|
||||
|
||||
self.host
|
||||
.registration_hooks
|
||||
.update_lsp_status(lsp::LanguageServerName(server_name.into()), status);
|
||||
.proxy
|
||||
.update_language_server_status(lsp::LanguageServerName(server_name.into()), status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use extension::{KeyValueStoreDelegate, WorktreeDelegate};
|
||||
use extension::{ExtensionLanguageServerProxy, KeyValueStoreDelegate, WorktreeDelegate};
|
||||
use futures::{io::BufReader, FutureExt as _};
|
||||
use futures::{lock::Mutex, AsyncReadExt};
|
||||
use language::LanguageName;
|
||||
@ -495,8 +495,9 @@ impl ExtensionImports for WasmState {
|
||||
};
|
||||
|
||||
self.host
|
||||
.registration_hooks
|
||||
.update_lsp_status(::lsp::LanguageServerName(server_name.into()), status);
|
||||
.proxy
|
||||
.update_language_server_status(::lsp::LanguageServerName(server_name.into()), status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@ use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use context_servers::manager::ContextServerSettings;
|
||||
use extension::{KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate};
|
||||
use extension::{
|
||||
ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate,
|
||||
};
|
||||
use futures::{io::BufReader, FutureExt as _};
|
||||
use futures::{lock::Mutex, AsyncReadExt};
|
||||
use language::{language_settings::AllLanguageSettings, LanguageName, LanguageServerBinaryStatus};
|
||||
@ -682,8 +684,9 @@ impl ExtensionImports for WasmState {
|
||||
};
|
||||
|
||||
self.host
|
||||
.registration_hooks
|
||||
.update_lsp_status(::lsp::LanguageServerName(server_name.into()), status);
|
||||
.proxy
|
||||
.update_language_server_status(::lsp::LanguageServerName(server_name.into()), status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -13,21 +13,15 @@ path = "src/extensions_ui.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
assistant_slash_command.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
context_servers.workspace = true
|
||||
db.workspace = true
|
||||
editor.workspace = true
|
||||
extension.workspace = true
|
||||
extension_host.workspace = true
|
||||
fs.workspace = true
|
||||
fuzzy.workspace = true
|
||||
gpui.workspace = true
|
||||
indexed_docs.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
num-format.workspace = true
|
||||
picker.workspace = true
|
||||
project.workspace = true
|
||||
@ -36,7 +30,6 @@ semantic_version.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
snippet_provider.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
|
@ -1,209 +0,0 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::{ExtensionSlashCommand, SlashCommandRegistry};
|
||||
use context_servers::manager::ServerCommand;
|
||||
use context_servers::ContextServerFactoryRegistry;
|
||||
use extension::{Extension, ProjectDelegate};
|
||||
use extension_host::extension_lsp_adapter::ExtensionLspAdapter;
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, BackgroundExecutor, Model, Task};
|
||||
use indexed_docs::{ExtensionIndexedDocsProvider, IndexedDocsRegistry, ProviderId};
|
||||
use language::{LanguageName, LanguageRegistry, LanguageServerBinaryStatus, LoadedLanguage};
|
||||
use lsp::LanguageServerName;
|
||||
use snippet_provider::SnippetRegistry;
|
||||
use theme::{ThemeRegistry, ThemeSettings};
|
||||
use ui::SharedString;
|
||||
|
||||
struct ExtensionProject {
|
||||
worktree_ids: Vec<u64>,
|
||||
}
|
||||
|
||||
impl ProjectDelegate for ExtensionProject {
|
||||
fn worktree_ids(&self) -> Vec<u64> {
|
||||
self.worktree_ids.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConcreteExtensionRegistrationHooks {
|
||||
slash_command_registry: Arc<SlashCommandRegistry>,
|
||||
theme_registry: Arc<ThemeRegistry>,
|
||||
indexed_docs_registry: Arc<IndexedDocsRegistry>,
|
||||
snippet_registry: Arc<SnippetRegistry>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
context_server_factory_registry: Model<ContextServerFactoryRegistry>,
|
||||
executor: BackgroundExecutor,
|
||||
}
|
||||
|
||||
impl ConcreteExtensionRegistrationHooks {
|
||||
pub fn new(
|
||||
theme_registry: Arc<ThemeRegistry>,
|
||||
slash_command_registry: Arc<SlashCommandRegistry>,
|
||||
indexed_docs_registry: Arc<IndexedDocsRegistry>,
|
||||
snippet_registry: Arc<SnippetRegistry>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
context_server_factory_registry: Model<ContextServerFactoryRegistry>,
|
||||
cx: &AppContext,
|
||||
) -> Arc<dyn extension_host::ExtensionRegistrationHooks> {
|
||||
Arc::new(Self {
|
||||
theme_registry,
|
||||
slash_command_registry,
|
||||
indexed_docs_registry,
|
||||
snippet_registry,
|
||||
language_registry,
|
||||
context_server_factory_registry,
|
||||
executor: cx.background_executor().clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl extension_host::ExtensionRegistrationHooks for ConcreteExtensionRegistrationHooks {
|
||||
fn remove_user_themes(&self, themes: Vec<SharedString>) {
|
||||
self.theme_registry.remove_user_themes(&themes);
|
||||
}
|
||||
|
||||
fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn fs::Fs>) -> Task<Result<()>> {
|
||||
let theme_registry = self.theme_registry.clone();
|
||||
self.executor
|
||||
.spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
|
||||
}
|
||||
|
||||
fn register_slash_command(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
command: extension::SlashCommand,
|
||||
) {
|
||||
self.slash_command_registry
|
||||
.register_command(ExtensionSlashCommand::new(extension, command), false)
|
||||
}
|
||||
|
||||
fn register_context_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
id: Arc<str>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
self.context_server_factory_registry
|
||||
.update(cx, |registry, _| {
|
||||
registry.register_server_factory(
|
||||
id.clone(),
|
||||
Arc::new({
|
||||
move |project, cx| {
|
||||
log::info!(
|
||||
"loading command for context server {id} from extension {}",
|
||||
extension.manifest().id
|
||||
);
|
||||
|
||||
let id = id.clone();
|
||||
let extension = extension.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let extension_project =
|
||||
project.update(&mut cx, |project, cx| {
|
||||
Arc::new(ExtensionProject {
|
||||
worktree_ids: project
|
||||
.visible_worktrees(cx)
|
||||
.map(|worktree| worktree.read(cx).id().to_proto())
|
||||
.collect(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let command = extension
|
||||
.context_server_command(id.clone(), extension_project)
|
||||
.await?;
|
||||
|
||||
log::info!("loaded command for context server {id}: {command:?}");
|
||||
|
||||
Ok(ServerCommand {
|
||||
path: command.command,
|
||||
args: command.args,
|
||||
env: Some(command.env.into_iter().collect()),
|
||||
})
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn register_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
|
||||
self.indexed_docs_registry
|
||||
.register_provider(Box::new(ExtensionIndexedDocsProvider::new(
|
||||
extension,
|
||||
ProviderId(provider_id),
|
||||
)));
|
||||
}
|
||||
|
||||
fn register_snippets(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
|
||||
self.snippet_registry
|
||||
.register_snippets(path, snippet_contents)
|
||||
}
|
||||
|
||||
fn update_lsp_status(
|
||||
&self,
|
||||
server_name: lsp::LanguageServerName,
|
||||
status: LanguageServerBinaryStatus,
|
||||
) {
|
||||
self.language_registry
|
||||
.update_lsp_status(server_name, status);
|
||||
}
|
||||
|
||||
fn register_lsp_adapter(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language: LanguageName,
|
||||
) {
|
||||
self.language_registry.register_lsp_adapter(
|
||||
language.clone(),
|
||||
Arc::new(ExtensionLspAdapter::new(
|
||||
extension,
|
||||
language_server_id,
|
||||
language,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
fn remove_lsp_adapter(
|
||||
&self,
|
||||
language_name: &language::LanguageName,
|
||||
server_name: &lsp::LanguageServerName,
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_lsp_adapter(language_name, server_name);
|
||||
}
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
languages_to_remove: &[language::LanguageName],
|
||||
grammars_to_remove: &[Arc<str>],
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_languages(&languages_to_remove, &grammars_to_remove);
|
||||
}
|
||||
|
||||
fn register_wasm_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
|
||||
self.language_registry.register_wasm_grammars(grammars)
|
||||
}
|
||||
|
||||
fn register_language(
|
||||
&self,
|
||||
language: language::LanguageName,
|
||||
grammar: Option<Arc<str>>,
|
||||
matcher: language::LanguageMatcher,
|
||||
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
|
||||
) {
|
||||
self.language_registry
|
||||
.register_language(language, grammar, matcher, load)
|
||||
}
|
||||
|
||||
fn reload_current_theme(&self, cx: &mut AppContext) {
|
||||
ThemeSettings::reload_current_theme(cx)
|
||||
}
|
||||
|
||||
fn list_theme_names(&self, path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
|
||||
self.executor.spawn(async move {
|
||||
let themes = theme::read_user_theme(&path, fs).await?;
|
||||
Ok(themes.themes.into_iter().map(|theme| theme.name).collect())
|
||||
})
|
||||
}
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
mod components;
|
||||
mod extension_registration_hooks;
|
||||
mod extension_suggest;
|
||||
mod extension_version_selector;
|
||||
|
||||
pub use extension_registration_hooks::ConcreteExtensionRegistrationHooks;
|
||||
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
|
@ -3,9 +3,33 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use extension::Extension;
|
||||
use extension::{Extension, ExtensionHostProxy, ExtensionIndexedDocsProviderProxy};
|
||||
use gpui::AppContext;
|
||||
|
||||
use crate::{IndexedDocsDatabase, IndexedDocsProvider, PackageName, ProviderId};
|
||||
use crate::{
|
||||
IndexedDocsDatabase, IndexedDocsProvider, IndexedDocsRegistry, PackageName, ProviderId,
|
||||
};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let proxy = ExtensionHostProxy::default_global(cx);
|
||||
proxy.register_indexed_docs_provider_proxy(IndexedDocsRegistryProxy {
|
||||
indexed_docs_registry: IndexedDocsRegistry::global(cx),
|
||||
});
|
||||
}
|
||||
|
||||
struct IndexedDocsRegistryProxy {
|
||||
indexed_docs_registry: Arc<IndexedDocsRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionIndexedDocsProviderProxy for IndexedDocsRegistryProxy {
|
||||
fn register_indexed_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
|
||||
self.indexed_docs_registry
|
||||
.register_provider(Box::new(ExtensionIndexedDocsProvider::new(
|
||||
extension,
|
||||
ProviderId(provider_id),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtensionIndexedDocsProvider {
|
||||
extension: Arc<dyn Extension>,
|
||||
|
@ -3,7 +3,14 @@ mod providers;
|
||||
mod registry;
|
||||
mod store;
|
||||
|
||||
use gpui::AppContext;
|
||||
|
||||
pub use crate::extension_indexed_docs_provider::ExtensionIndexedDocsProvider;
|
||||
pub use crate::providers::rustdoc::*;
|
||||
pub use crate::registry::*;
|
||||
pub use crate::store::*;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
IndexedDocsRegistry::init_global(cx);
|
||||
extension_indexed_docs_provider::init(cx);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ impl IndexedDocsRegistry {
|
||||
GlobalIndexedDocsRegistry::global(cx).0.clone()
|
||||
}
|
||||
|
||||
pub fn init_global(cx: &mut AppContext) {
|
||||
pub(crate) fn init_global(cx: &mut AppContext) {
|
||||
GlobalIndexedDocsRegistry::set_global(
|
||||
cx,
|
||||
GlobalIndexedDocsRegistry(Arc::new(Self::new(cx.background_executor().clone()))),
|
||||
|
25
crates/language_extension/Cargo.toml
Normal file
25
crates/language_extension/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "language_extension"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/language_extension.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
collections.workspace = true
|
||||
extension.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
lsp.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
util.workspace = true
|
1
crates/language_extension/LICENSE-GPL
Symbolic link
1
crates/language_extension/LICENSE-GPL
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
@ -1,22 +1,28 @@
|
||||
use std::any::Any;
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use extension::{Extension, WorktreeDelegate};
|
||||
use extension::{Extension, ExtensionLanguageServerProxy, WorktreeDelegate};
|
||||
use futures::{Future, FutureExt};
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{
|
||||
CodeLabel, HighlightId, Language, LanguageName, LanguageToolchainStore, LspAdapter,
|
||||
LspAdapterDelegate,
|
||||
CodeLabel, HighlightId, Language, LanguageName, LanguageServerBinaryStatus,
|
||||
LanguageToolchainStore, LspAdapter, LspAdapterDelegate,
|
||||
};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::ops::Range;
|
||||
use std::{any::Any, path::PathBuf, pin::Pin, sync::Arc};
|
||||
use util::{maybe, ResultExt};
|
||||
|
||||
use crate::LanguageServerRegistryProxy;
|
||||
|
||||
/// An adapter that allows an [`LspAdapterDelegate`] to be used as a [`WorktreeDelegate`].
|
||||
pub struct WorktreeDelegateAdapter(pub Arc<dyn LspAdapterDelegate>);
|
||||
struct WorktreeDelegateAdapter(pub Arc<dyn LspAdapterDelegate>);
|
||||
|
||||
#[async_trait]
|
||||
impl WorktreeDelegate for WorktreeDelegateAdapter {
|
||||
@ -44,14 +50,50 @@ impl WorktreeDelegate for WorktreeDelegateAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtensionLspAdapter {
|
||||
impl ExtensionLanguageServerProxy for LanguageServerRegistryProxy {
|
||||
fn register_language_server(
|
||||
&self,
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language: LanguageName,
|
||||
) {
|
||||
self.language_registry.register_lsp_adapter(
|
||||
language.clone(),
|
||||
Arc::new(ExtensionLspAdapter::new(
|
||||
extension,
|
||||
language_server_id,
|
||||
language,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
fn remove_language_server(
|
||||
&self,
|
||||
language: &LanguageName,
|
||||
language_server_id: &LanguageServerName,
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_lsp_adapter(language, language_server_id);
|
||||
}
|
||||
|
||||
fn update_language_server_status(
|
||||
&self,
|
||||
language_server_id: LanguageServerName,
|
||||
status: LanguageServerBinaryStatus,
|
||||
) {
|
||||
self.language_registry
|
||||
.update_lsp_status(language_server_id, status);
|
||||
}
|
||||
}
|
||||
|
||||
struct ExtensionLspAdapter {
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language_name: LanguageName,
|
||||
}
|
||||
|
||||
impl ExtensionLspAdapter {
|
||||
pub fn new(
|
||||
fn new(
|
||||
extension: Arc<dyn Extension>,
|
||||
language_server_id: LanguageServerName,
|
||||
language_name: LanguageName,
|
51
crates/language_extension/src/language_extension.rs
Normal file
51
crates/language_extension/src/language_extension.rs
Normal file
@ -0,0 +1,51 @@
|
||||
mod extension_lsp_adapter;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use extension::{ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageProxy};
|
||||
use language::{LanguageMatcher, LanguageName, LanguageRegistry, LoadedLanguage};
|
||||
|
||||
pub fn init(
|
||||
extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
) {
|
||||
let language_server_registry_proxy = LanguageServerRegistryProxy { language_registry };
|
||||
extension_host_proxy.register_grammar_proxy(language_server_registry_proxy.clone());
|
||||
extension_host_proxy.register_language_proxy(language_server_registry_proxy.clone());
|
||||
extension_host_proxy.register_language_server_proxy(language_server_registry_proxy);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LanguageServerRegistryProxy {
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionGrammarProxy for LanguageServerRegistryProxy {
|
||||
fn register_grammars(&self, grammars: Vec<(Arc<str>, PathBuf)>) {
|
||||
self.language_registry.register_wasm_grammars(grammars)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtensionLanguageProxy for LanguageServerRegistryProxy {
|
||||
fn register_language(
|
||||
&self,
|
||||
language: LanguageName,
|
||||
grammar: Option<Arc<str>>,
|
||||
matcher: LanguageMatcher,
|
||||
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
|
||||
) {
|
||||
self.language_registry
|
||||
.register_language(language, grammar, matcher, load);
|
||||
}
|
||||
|
||||
fn remove_languages(
|
||||
&self,
|
||||
languages_to_remove: &[LanguageName],
|
||||
grammars_to_remove: &[Arc<str>],
|
||||
) {
|
||||
self.language_registry
|
||||
.remove_languages(&languages_to_remove, &grammars_to_remove);
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ chrono.workspace = true
|
||||
clap.workspace = true
|
||||
client.workspace = true
|
||||
env_logger.workspace = true
|
||||
extension.workspace = true
|
||||
extension_host.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
@ -37,6 +38,7 @@ git_hosting_providers.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
language.workspace = true
|
||||
language_extension.workspace = true
|
||||
languages.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
|
@ -1,4 +1,5 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use extension::ExtensionHostProxy;
|
||||
use extension_host::headless_host::HeadlessExtensionStore;
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, AsyncAppContext, Context as _, Model, ModelContext, PromptLevel};
|
||||
@ -47,6 +48,7 @@ pub struct HeadlessAppState {
|
||||
pub http_client: Arc<dyn HttpClient>,
|
||||
pub node_runtime: NodeRuntime,
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
pub extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
}
|
||||
|
||||
impl HeadlessProject {
|
||||
@ -63,9 +65,11 @@ impl HeadlessProject {
|
||||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
extension_host_proxy: proxy,
|
||||
}: HeadlessAppState,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
language_extension::init(proxy.clone(), languages.clone());
|
||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
||||
|
||||
let worktree_store = cx.new_model(|cx| {
|
||||
@ -152,8 +156,8 @@ impl HeadlessProject {
|
||||
let extensions = HeadlessExtensionStore::new(
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
languages.clone(),
|
||||
paths::remote_extensions_dir().to_path_buf(),
|
||||
proxy,
|
||||
node_runtime,
|
||||
cx,
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::headless_project::HeadlessProject;
|
||||
use client::{Client, UserStore};
|
||||
use clock::FakeSystemClock;
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{FakeFs, Fs};
|
||||
use gpui::{Context, Model, SemanticVersion, TestAppContext};
|
||||
use http_client::{BlockedHttpClient, FakeHttpClient};
|
||||
@ -1234,6 +1235,7 @@ pub async fn init_test(
|
||||
let http_client = Arc::new(BlockedHttpClient);
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
let languages = Arc::new(LanguageRegistry::new(cx.executor()));
|
||||
let proxy = Arc::new(ExtensionHostProxy::new());
|
||||
server_cx.update(HeadlessProject::init);
|
||||
let headless = server_cx.new_model(|cx| {
|
||||
client::init_settings(cx);
|
||||
@ -1245,6 +1247,7 @@ pub async fn init_test(
|
||||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
extension_host_proxy: proxy,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ use crate::HeadlessProject;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use chrono::Utc;
|
||||
use client::{telemetry, ProxySettings};
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{Fs, RealFs};
|
||||
use futures::channel::mpsc;
|
||||
use futures::{select, select_biased, AsyncRead, AsyncWrite, AsyncWriteExt, FutureExt, SinkExt};
|
||||
@ -434,6 +435,9 @@ pub fn execute_run(
|
||||
GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
|
||||
git_hosting_providers::init(cx);
|
||||
|
||||
extension::init(cx);
|
||||
let extension_host_proxy = ExtensionHostProxy::global(cx);
|
||||
|
||||
let project = cx.new_model(|cx| {
|
||||
let fs = Arc::new(RealFs::new(Default::default(), None));
|
||||
let node_settings_rx = initialize_settings(session.clone(), fs.clone(), cx);
|
||||
@ -466,6 +470,7 @@ pub fn execute_run(
|
||||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
extension_host_proxy,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
|
@ -11,6 +11,7 @@ workspace = true
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
collections.workspace = true
|
||||
extension.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
|
26
crates/snippet_provider/src/extension_snippet.rs
Normal file
26
crates/snippet_provider/src/extension_snippet.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use extension::{ExtensionHostProxy, ExtensionSnippetProxy};
|
||||
use gpui::AppContext;
|
||||
|
||||
use crate::SnippetRegistry;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let proxy = ExtensionHostProxy::default_global(cx);
|
||||
proxy.register_snippet_proxy(SnippetRegistryProxy {
|
||||
snippet_registry: SnippetRegistry::global(cx),
|
||||
});
|
||||
}
|
||||
|
||||
struct SnippetRegistryProxy {
|
||||
snippet_registry: Arc<SnippetRegistry>,
|
||||
}
|
||||
|
||||
impl ExtensionSnippetProxy for SnippetRegistryProxy {
|
||||
fn register_snippet(&self, path: &PathBuf, snippet_contents: &str) -> Result<()> {
|
||||
self.snippet_registry
|
||||
.register_snippets(path, snippet_contents)
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
mod extension_snippet;
|
||||
mod format;
|
||||
mod registry;
|
||||
|
||||
@ -18,6 +19,7 @@ use util::ResultExt;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
SnippetRegistry::init_global(cx);
|
||||
extension_snippet::init(cx);
|
||||
}
|
||||
|
||||
// Is `None` if the snippet file is global.
|
||||
|
19
crates/theme_extension/Cargo.toml
Normal file
19
crates/theme_extension/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "theme_extension"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/theme_extension.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
extension.workspace = true
|
||||
fs.workspace = true
|
||||
gpui.workspace = true
|
||||
theme.workspace = true
|
1
crates/theme_extension/LICENSE-GPL
Symbolic link
1
crates/theme_extension/LICENSE-GPL
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
47
crates/theme_extension/src/theme_extension.rs
Normal file
47
crates/theme_extension/src/theme_extension.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use extension::{ExtensionHostProxy, ExtensionThemeProxy};
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, BackgroundExecutor, SharedString, Task};
|
||||
use theme::{ThemeRegistry, ThemeSettings};
|
||||
|
||||
pub fn init(
|
||||
extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
theme_registry: Arc<ThemeRegistry>,
|
||||
executor: BackgroundExecutor,
|
||||
) {
|
||||
extension_host_proxy.register_theme_proxy(ThemeRegistryProxy {
|
||||
theme_registry,
|
||||
executor,
|
||||
});
|
||||
}
|
||||
|
||||
struct ThemeRegistryProxy {
|
||||
theme_registry: Arc<ThemeRegistry>,
|
||||
executor: BackgroundExecutor,
|
||||
}
|
||||
|
||||
impl ExtensionThemeProxy for ThemeRegistryProxy {
|
||||
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
|
||||
self.executor.spawn(async move {
|
||||
let themes = theme::read_user_theme(&theme_path, fs).await?;
|
||||
Ok(themes.themes.into_iter().map(|theme| theme.name).collect())
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_user_themes(&self, themes: Vec<SharedString>) {
|
||||
self.theme_registry.remove_user_themes(&themes);
|
||||
}
|
||||
|
||||
fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
|
||||
let theme_registry = self.theme_registry.clone();
|
||||
self.executor
|
||||
.spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
|
||||
}
|
||||
|
||||
fn reload_current_theme(&self, cx: &mut AppContext) {
|
||||
ThemeSettings::reload_current_theme(cx)
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ activity_indicator.workspace = true
|
||||
anyhow.workspace = true
|
||||
assets.workspace = true
|
||||
assistant.workspace = true
|
||||
assistant_slash_command.workspace = true
|
||||
async-watch.workspace = true
|
||||
audio.workspace = true
|
||||
auto_update.workspace = true
|
||||
@ -36,12 +35,12 @@ collab_ui.workspace = true
|
||||
collections.workspace = true
|
||||
command_palette.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
context_servers.workspace = true
|
||||
copilot.workspace = true
|
||||
db.workspace = true
|
||||
diagnostics.workspace = true
|
||||
editor.workspace = true
|
||||
env_logger.workspace = true
|
||||
extension.workspace = true
|
||||
extension_host.workspace = true
|
||||
extensions_ui.workspace = true
|
||||
feature_flags.workspace = true
|
||||
@ -56,11 +55,11 @@ go_to_line.workspace = true
|
||||
gpui = { workspace = true, features = ["wayland", "x11", "font-kit"] }
|
||||
http_client.workspace = true
|
||||
image_viewer.workspace = true
|
||||
indexed_docs.workspace = true
|
||||
inline_completion_button.workspace = true
|
||||
install_cli.workspace = true
|
||||
journal.workspace = true
|
||||
language.workspace = true
|
||||
language_extension.workspace = true
|
||||
language_model.workspace = true
|
||||
language_models.workspace = true
|
||||
language_selector.workspace = true
|
||||
@ -109,6 +108,7 @@ tasks_ui.workspace = true
|
||||
telemetry_events.workspace = true
|
||||
terminal_view.workspace = true
|
||||
theme.workspace = true
|
||||
theme_extension.workspace = true
|
||||
theme_selector.workspace = true
|
||||
time.workspace = true
|
||||
toolchain_selector.workspace = true
|
||||
|
@ -5,16 +5,15 @@ mod reliability;
|
||||
mod zed;
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use assistant_slash_command::SlashCommandRegistry;
|
||||
use chrono::Offset;
|
||||
use clap::{command, Parser};
|
||||
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||
use client::{parse_zed_link, Client, ProxySettings, UserStore};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use context_servers::ContextServerFactoryRegistry;
|
||||
use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE};
|
||||
use editor::Editor;
|
||||
use env_logger::Builder;
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{Fs, RealFs};
|
||||
use futures::{future, StreamExt};
|
||||
use git::GitHostingProviderRegistry;
|
||||
@ -23,7 +22,6 @@ use gpui::{
|
||||
VisualContext,
|
||||
};
|
||||
use http_client::{read_proxy_from_env, Uri};
|
||||
use indexed_docs::IndexedDocsRegistry;
|
||||
use language::LanguageRegistry;
|
||||
use log::LevelFilter;
|
||||
use reqwest_client::ReqwestClient;
|
||||
@ -40,7 +38,6 @@ use settings::{
|
||||
};
|
||||
use simplelog::ConfigBuilder;
|
||||
use smol::process::Command;
|
||||
use snippet_provider::SnippetRegistry;
|
||||
use std::{
|
||||
env,
|
||||
fs::OpenOptions,
|
||||
@ -284,6 +281,9 @@ fn main() {
|
||||
|
||||
OpenListener::set_global(cx, open_listener.clone());
|
||||
|
||||
extension::init(cx);
|
||||
let extension_host_proxy = ExtensionHostProxy::global(cx);
|
||||
|
||||
let client = Client::production(cx);
|
||||
cx.set_http_client(client.http_client().clone());
|
||||
let mut languages = LanguageRegistry::new(cx.background_executor().clone());
|
||||
@ -317,6 +317,7 @@ fn main() {
|
||||
let node_runtime = NodeRuntime::new(client.http_client(), rx);
|
||||
|
||||
language::init(cx);
|
||||
language_extension::init(extension_host_proxy.clone(), languages.clone());
|
||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
||||
let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
|
||||
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
|
||||
@ -326,7 +327,6 @@ fn main() {
|
||||
zed::init(cx);
|
||||
project::Project::init(&client, cx);
|
||||
client::init(&client, cx);
|
||||
language::init(cx);
|
||||
let telemetry = client.telemetry();
|
||||
telemetry.start(
|
||||
system_id.as_ref().map(|id| id.to_string()),
|
||||
@ -376,6 +376,11 @@ fn main() {
|
||||
|
||||
SystemAppearance::init(cx);
|
||||
theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
|
||||
theme_extension::init(
|
||||
extension_host_proxy.clone(),
|
||||
ThemeRegistry::global(cx),
|
||||
cx.background_executor().clone(),
|
||||
);
|
||||
command_palette::init(cx);
|
||||
let copilot_language_server_id = app_state.languages.next_language_server_id();
|
||||
copilot::init(
|
||||
@ -407,17 +412,8 @@ fn main() {
|
||||
app_state.client.telemetry().clone(),
|
||||
cx,
|
||||
);
|
||||
let api = extensions_ui::ConcreteExtensionRegistrationHooks::new(
|
||||
ThemeRegistry::global(cx),
|
||||
SlashCommandRegistry::global(cx),
|
||||
IndexedDocsRegistry::global(cx),
|
||||
SnippetRegistry::global(cx),
|
||||
app_state.languages.clone(),
|
||||
ContextServerFactoryRegistry::global(cx),
|
||||
cx,
|
||||
);
|
||||
extension_host::init(
|
||||
api,
|
||||
extension_host_proxy,
|
||||
app_state.fs.clone(),
|
||||
app_state.client.clone(),
|
||||
app_state.node_runtime.clone(),
|
||||
|
Loading…
Reference in New Issue
Block a user