ZIm/crates/extension/src/extension_host_proxy.rs
Danilo Leal f795ce9623
Add language icons to the language selector (#21298)
Closes https://github.com/zed-industries/zed/issues/21290

This is a first attempt to show the language icons to the selector.
Ideally, I wouldn't like to have yet another place mapping extensions to
icons, as we already have the `file_types.json` file doing that, but I'm
not so sure how to pull from it yet. Maybe in a future pass we'll
improve this and make it more solid.

<img width="700" alt="Screenshot 2024-11-28 at 16 10 27"
src="https://github.com/user-attachments/assets/683c3bef-5389-470f-a41e-3d510b927b61">

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
2024-12-02 15:01:09 -03:00

326 lines
10 KiB
Rust

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,
hidden: bool,
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,
hidden: bool,
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, hidden, 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)
}
}