debugger/extensions: Revert changes to extension store related to language config (#30225)

Revert #29945 

Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Piotr Osiewicz 2025-05-08 14:01:39 +02:00 committed by GitHub
parent 20387f24aa
commit b091581e4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 159 additions and 218 deletions

View File

@ -1,12 +1,11 @@
use std::{
borrow::Cow,
cmp::Reverse,
ops::Not,
path::{Path, PathBuf},
sync::Arc,
usize,
};
use collections::{HashMap, HashSet};
use dap::{
DapRegistry, DebugRequest,
adapters::{DebugAdapterName, DebugTaskDefinition},
@ -192,25 +191,22 @@ impl NewSessionModal {
cx: &mut Context<Self>,
) -> Option<ui::DropdownMenu> {
let workspace = self.workspace.clone();
let language_registry = self
.workspace
.update(cx, |this, _| this.app_state().languages.clone())
.ok()?;
let weak = cx.weak_entity();
let label = self
.debugger
.as_ref()
.map(|d| d.0.clone())
.unwrap_or_else(|| SELECT_DEBUGGER_LABEL.clone());
let active_buffer_language_name =
self.task_contexts
.active_item_context
.as_ref()
.and_then(|item| {
item.1
.as_ref()
.and_then(|location| location.buffer.read(cx).language()?.name().into())
});
let active_buffer_language = self
.task_contexts
.active_item_context
.as_ref()
.and_then(|item| {
item.1
.as_ref()
.and_then(|location| location.buffer.read(cx).language())
})
.cloned();
DropdownMenu::new(
"dap-adapter-picker",
label,
@ -229,42 +225,19 @@ impl NewSessionModal {
}
};
let available_languages = language_registry.language_names();
let mut debugger_to_languages = HashMap::default();
for language in available_languages {
let Some(language) =
language_registry.available_language_for_name(language.as_str())
else {
continue;
};
language.config().debuggers.iter().for_each(|adapter| {
debugger_to_languages
.entry(adapter.clone())
.or_insert_with(HashSet::default)
.insert(language.name());
});
}
let mut available_adapters = workspace
.update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters())
.ok()
.unwrap_or_default();
available_adapters.sort_by_key(|name| {
let languages_for_debugger = debugger_to_languages.get(name.as_ref());
let languages_count =
languages_for_debugger.map_or(0, |languages| languages.len());
let contains_language_of_active_buffer = languages_for_debugger
.zip(active_buffer_language_name.as_ref())
.map_or(false, |(languages, active_buffer_language)| {
languages.contains(active_buffer_language)
});
(
Reverse(contains_language_of_active_buffer),
Reverse(languages_count),
)
});
if let Some(language) = active_buffer_language {
available_adapters.sort_by_key(|adapter| {
language
.config()
.debuggers
.get_index_of(adapter.0.as_ref())
.unwrap_or(usize::MAX)
});
}
for adapter in available_adapters.into_iter() {
menu = menu.entry(adapter.0.clone(), None, setter_for_name(adapter.clone()));

View File

@ -4,7 +4,7 @@ use std::sync::Arc;
use anyhow::Result;
use fs::Fs;
use gpui::{App, Global, ReadGlobal, SharedString, Task};
use language::{BinaryStatus, LanguageConfig, LanguageName, LoadedLanguage};
use language::{BinaryStatus, LanguageMatcher, LanguageName, LoadedLanguage};
use lsp::LanguageServerName;
use parking_lot::RwLock;
@ -224,7 +224,10 @@ impl ExtensionGrammarProxy for ExtensionHostProxy {
pub trait ExtensionLanguageProxy: Send + Sync + 'static {
fn register_language(
&self,
config: LanguageConfig,
language: LanguageName,
grammar: Option<Arc<str>>,
matcher: LanguageMatcher,
hidden: bool,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
);
@ -238,14 +241,17 @@ pub trait ExtensionLanguageProxy: Send + Sync + 'static {
impl ExtensionLanguageProxy for ExtensionHostProxy {
fn register_language(
&self,
language: LanguageConfig,
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, load)
proxy.register_language(language, grammar, matcher, hidden, load)
}
fn remove_languages(

View File

@ -34,7 +34,8 @@ use gpui::{
};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use language::{
LanguageConfig, LanguageName, LanguageQueries, LoadedLanguage, QUERY_FILENAME_PREFIXES, Rope,
LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
QUERY_FILENAME_PREFIXES, Rope,
};
use node_runtime::NodeRuntime;
use project::ContextProviderWithTasks;
@ -139,7 +140,7 @@ struct GlobalExtensionStore(Entity<ExtensionStore>);
impl Global for GlobalExtensionStore {}
#[derive(Deserialize, Serialize, Default)]
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
pub struct ExtensionIndex {
pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
@ -166,12 +167,13 @@ pub struct ExtensionIndexIconThemeEntry {
pub path: PathBuf,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
pub struct ExtensionIndexLanguageEntry {
pub extension: Arc<str>,
pub path: PathBuf,
#[serde(skip)]
pub config: LanguageConfig,
pub matcher: LanguageMatcher,
pub hidden: bool,
pub grammar: Option<Arc<str>>,
}
actions!(zed, [ReloadExtensions]);
@ -1181,8 +1183,44 @@ impl ExtensionStore {
}
self.proxy.register_grammars(grammars_to_add);
let languages_to_add = new_index
.languages
.iter_mut()
.filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
.collect::<Vec<_>>();
for (language_name, language) in languages_to_add {
let mut language_path = self.installed_dir.clone();
language_path.extend([
Path::new(language.extension.as_ref()),
language.path.as_path(),
]);
self.proxy.register_language(
language_name.clone(),
language.grammar.clone(),
language.matcher.clone(),
language.hidden,
Arc::new(move || {
let config = std::fs::read_to_string(language_path.join("config.toml"))?;
let config: LanguageConfig = ::toml::from_str(&config)?;
let queries = load_plugin_queries(&language_path);
let context_provider =
std::fs::read_to_string(language_path.join("tasks.json"))
.ok()
.and_then(|contents| {
let definitions =
serde_json_lenient::from_str(&contents).log_err()?;
Some(Arc::new(ContextProviderWithTasks::new(definitions)) as Arc<_>)
});
let installed_dir = self.installed_dir.clone();
Ok(LoadedLanguage {
config,
queries,
context_provider,
toolchain_provider: None,
})
}),
);
}
let fs = self.fs.clone();
let wasm_host = self.wasm_host.clone();
@ -1192,60 +1230,11 @@ impl ExtensionStore {
.iter()
.filter_map(|name| new_index.extensions.get(name).cloned())
.collect::<Vec<_>>();
self.extension_index = new_index;
cx.notify();
cx.emit(Event::ExtensionsUpdated);
cx.spawn(async move |this, cx| {
let languages_to_add = new_index
.languages
.iter_mut()
.filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
.collect::<Vec<_>>();
for (_, language) in languages_to_add {
let mut language_path = installed_dir.clone();
language_path.extend([
Path::new(language.extension.as_ref()),
language.path.as_path(),
]);
let Some(config) = fs.load(&language_path.join("config.toml")).await.ok() else {
log::error!("Could not load config.toml in {:?}", language_path);
continue;
};
let Some(config) = ::toml::from_str::<LanguageConfig>(&config).ok() else {
log::error!(
"Could not parse language config.toml in {:?}",
language_path
);
continue;
};
language.config = config.clone();
proxy.register_language(
language.config.clone(),
Arc::new(move || {
let queries = load_plugin_queries(&language_path);
let context_provider =
std::fs::read_to_string(language_path.join("tasks.json"))
.ok()
.and_then(|contents| {
let definitions =
serde_json_lenient::from_str(&contents).log_err()?;
Some(Arc::new(ContextProviderWithTasks::new(definitions))
as Arc<_>)
});
Ok(LoadedLanguage {
config: config.clone(),
queries,
context_provider,
toolchain_provider: None,
})
}),
);
}
this.update(cx, |this, cx| {
this.extension_index = new_index;
cx.notify();
cx.emit(Event::ExtensionsUpdated);
})
.ok();
cx.background_spawn({
let fs = fs.clone();
async move {
@ -1448,7 +1437,9 @@ impl ExtensionStore {
ExtensionIndexLanguageEntry {
extension: extension_id.clone(),
path: relative_path,
config,
matcher: config.matcher,
hidden: config.hidden,
grammar: config.grammar,
},
);
}

View File

@ -10,7 +10,7 @@ use fs::{FakeFs, Fs, RealFs};
use futures::{AsyncReadExt, StreamExt, io::BufReader};
use gpui::{AppContext as _, SemanticVersion, SharedString, TestAppContext};
use http_client::{FakeHttpClient, Response};
use language::{BinaryStatus, LanguageConfig, LanguageMatcher, LanguageRegistry};
use language::{BinaryStatus, LanguageMatcher, LanguageRegistry};
use lsp::LanguageServerName;
use node_runtime::NodeRuntime;
use parking_lot::Mutex;
@ -206,14 +206,11 @@ async fn test_extension_store(cx: &mut TestAppContext) {
ExtensionIndexLanguageEntry {
extension: "zed-ruby".into(),
path: "languages/erb".into(),
config: LanguageConfig {
grammar: Some("embedded_template".into()),
hidden: false,
matcher: LanguageMatcher {
path_suffixes: vec!["erb".into()],
first_line_pattern: None,
},
..Default::default()
grammar: Some("embedded_template".into()),
hidden: false,
matcher: LanguageMatcher {
path_suffixes: vec!["erb".into()],
first_line_pattern: None,
},
},
),
@ -222,14 +219,11 @@ async fn test_extension_store(cx: &mut TestAppContext) {
ExtensionIndexLanguageEntry {
extension: "zed-ruby".into(),
path: "languages/ruby".into(),
config: LanguageConfig {
grammar: Some("ruby".into()),
hidden: false,
matcher: LanguageMatcher {
path_suffixes: vec!["rb".into()],
first_line_pattern: None,
},
..Default::default()
grammar: Some("ruby".into()),
hidden: false,
matcher: LanguageMatcher {
path_suffixes: vec!["rb".into()],
first_line_pattern: None,
},
},
),
@ -301,18 +295,9 @@ async fn test_extension_store(cx: &mut TestAppContext) {
index.languages.iter().zip(expected_index.languages.iter())
{
assert_eq!(actual_key, expected_key);
assert_eq!(
actual_language.config.grammar,
expected_language.config.grammar
);
assert_eq!(
actual_language.config.matcher,
expected_language.config.matcher
);
assert_eq!(
actual_language.config.hidden,
expected_language.config.hidden
);
assert_eq!(actual_language.grammar, expected_language.grammar);
assert_eq!(actual_language.matcher, expected_language.matcher);
assert_eq!(actual_language.hidden, expected_language.hidden);
}
assert_eq!(index.themes, expected_index.themes);
@ -405,18 +390,9 @@ async fn test_extension_store(cx: &mut TestAppContext) {
index.languages.iter().zip(expected_index.languages.iter())
{
assert_eq!(actual_key, expected_key);
assert_eq!(
actual_language.config.grammar,
expected_language.config.grammar
);
assert_eq!(
actual_language.config.matcher,
expected_language.config.matcher
);
assert_eq!(
actual_language.config.hidden,
expected_language.config.hidden
);
assert_eq!(actual_language.grammar, expected_language.grammar);
assert_eq!(actual_language.matcher, expected_language.matcher);
assert_eq!(actual_language.hidden, expected_language.hidden);
}
assert_eq!(index.extensions, expected_index.extensions);
@ -470,18 +446,9 @@ async fn test_extension_store(cx: &mut TestAppContext) {
.zip(expected_index.languages.iter())
{
assert_eq!(actual_key, expected_key);
assert_eq!(
actual_language.config.grammar,
expected_language.config.grammar
);
assert_eq!(
actual_language.config.matcher,
expected_language.config.matcher
);
assert_eq!(
actual_language.config.hidden,
expected_language.config.hidden
);
assert_eq!(actual_language.grammar, expected_language.grammar);
assert_eq!(actual_language.matcher, expected_language.matcher);
assert_eq!(actual_language.hidden, expected_language.hidden);
}
assert_eq!(
@ -534,18 +501,9 @@ async fn test_extension_store(cx: &mut TestAppContext) {
.zip(expected_index.languages.iter())
{
assert_eq!(actual_key, expected_key);
assert_eq!(
actual_language.config.grammar,
expected_language.config.grammar
);
assert_eq!(
actual_language.config.matcher,
expected_language.config.matcher
);
assert_eq!(
actual_language.config.hidden,
expected_language.config.hidden
);
assert_eq!(actual_language.grammar, expected_language.grammar);
assert_eq!(actual_language.matcher, expected_language.matcher);
assert_eq!(actual_language.hidden, expected_language.hidden);
}
assert_eq!(language_registry.language_names(), ["Plain Text"]);

View File

@ -149,7 +149,10 @@ impl HeadlessExtensionStore {
config.grammar = None;
this.proxy.register_language(
config.clone(),
config.name.clone(),
None,
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),

View File

@ -666,7 +666,7 @@ pub struct CodeLabel {
pub filter_range: Range<usize>,
}
#[derive(Clone, Deserialize, JsonSchema, Serialize, Debug)]
#[derive(Clone, Deserialize, JsonSchema)]
pub struct LanguageConfig {
/// Human-readable name of the language.
pub name: LanguageName,
@ -694,20 +694,12 @@ pub struct LanguageConfig {
pub auto_indent_on_paste: Option<bool>,
/// A regex that is used to determine whether the indentation level should be
/// increased in the following line.
#[serde(
default,
deserialize_with = "deserialize_regex",
serialize_with = "serialize_regex"
)]
#[serde(default, deserialize_with = "deserialize_regex")]
#[schemars(schema_with = "regex_json_schema")]
pub increase_indent_pattern: Option<Regex>,
/// A regex that is used to determine whether the indentation level should be
/// decreased in the following line.
#[serde(
default,
deserialize_with = "deserialize_regex",
serialize_with = "serialize_regex"
)]
#[serde(default, deserialize_with = "deserialize_regex")]
#[schemars(schema_with = "regex_json_schema")]
pub decrease_indent_pattern: Option<Regex>,
/// A list of characters that trigger the automatic insertion of a closing
@ -762,7 +754,7 @@ pub struct LanguageConfig {
pub completion_query_characters: HashSet<char>,
/// A list of preferred debuggers for this language.
#[serde(default)]
pub debuggers: IndexSet<String>,
pub debuggers: IndexSet<SharedString>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
@ -781,7 +773,7 @@ pub struct LanguageMatcher {
}
/// The configuration for JSX tag auto-closing.
#[derive(Clone, Deserialize, JsonSchema, Serialize, Debug)]
#[derive(Clone, Deserialize, JsonSchema)]
pub struct JsxTagAutoCloseConfig {
/// The name of the node for a opening tag
pub open_tag_node_name: String,
@ -822,7 +814,7 @@ pub struct LanguageScope {
override_id: Option<u32>,
}
#[derive(Clone, Deserialize, Default, Debug, JsonSchema, Serialize)]
#[derive(Clone, Deserialize, Default, Debug, JsonSchema)]
pub struct LanguageConfigOverride {
#[serde(default)]
pub line_comments: Override<Vec<Arc<str>>>,
@ -949,7 +941,7 @@ pub struct FakeLspAdapter {
///
/// This struct includes settings for defining which pairs of characters are considered brackets and
/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.
#[derive(Clone, Debug, Default, JsonSchema, Serialize)]
#[derive(Clone, Debug, Default, JsonSchema)]
pub struct BracketPairConfig {
/// A list of character pairs that should be treated as brackets in the context of a given language.
pub pairs: Vec<BracketPair>,
@ -999,7 +991,7 @@ impl<'de> Deserialize<'de> for BracketPairConfig {
/// Describes a single bracket pair and how an editor should react to e.g. inserting
/// an opening bracket or to a newline character insertion in between `start` and `end` characters.
#[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema)]
pub struct BracketPair {
/// Starting substring for a bracket.
pub start: String,

View File

@ -145,24 +145,25 @@ pub enum BinaryStatus {
#[derive(Clone)]
pub struct AvailableLanguage {
id: LanguageId,
config: LanguageConfig,
name: LanguageName,
grammar: Option<Arc<str>>,
matcher: LanguageMatcher,
hidden: bool,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
loaded: bool,
}
impl AvailableLanguage {
pub fn name(&self) -> LanguageName {
self.config.name.clone()
self.name.clone()
}
pub fn matcher(&self) -> &LanguageMatcher {
&self.config.matcher
&self.matcher
}
pub fn hidden(&self) -> bool {
self.config.hidden
}
pub fn config(&self) -> &LanguageConfig {
&self.config
self.hidden
}
}
@ -326,7 +327,10 @@ impl LanguageRegistry {
#[cfg(any(feature = "test-support", test))]
pub fn register_test_language(&self, config: LanguageConfig) {
self.register_language(
config.clone(),
config.name.clone(),
config.grammar.clone(),
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
@ -485,14 +489,18 @@ impl LanguageRegistry {
/// Adds a language to the registry, which can be loaded if needed.
pub fn register_language(
&self,
config: LanguageConfig,
name: LanguageName,
grammar_name: Option<Arc<str>>,
matcher: LanguageMatcher,
hidden: bool,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
) {
let state = &mut *self.state.write();
for existing_language in &mut state.available_languages {
if existing_language.config.name == config.name {
existing_language.config = config;
if existing_language.name == name {
existing_language.grammar = grammar_name;
existing_language.matcher = matcher;
existing_language.load = load;
return;
}
@ -500,8 +508,11 @@ impl LanguageRegistry {
state.available_languages.push(AvailableLanguage {
id: LanguageId::new(),
config,
name,
grammar: grammar_name,
matcher,
load,
hidden,
loaded: false,
});
state.version += 1;
@ -547,7 +558,7 @@ impl LanguageRegistry {
let mut result = state
.available_languages
.iter()
.filter_map(|l| l.loaded.not().then_some(l.config.name.to_string()))
.filter_map(|l| l.loaded.not().then_some(l.name.to_string()))
.chain(state.languages.iter().map(|l| l.config.name.to_string()))
.collect::<Vec<_>>();
result.sort_unstable_by_key(|language_name| language_name.to_lowercase());
@ -566,7 +577,10 @@ impl LanguageRegistry {
let mut state = self.state.write();
state.available_languages.push(AvailableLanguage {
id: language.id,
config: language.config.clone(),
name: language.name(),
grammar: language.config.grammar.clone(),
matcher: language.config.matcher.clone(),
hidden: language.config.hidden,
load: Arc::new(|| Err(anyhow!("already loaded"))),
loaded: true,
});
@ -635,7 +649,7 @@ impl LanguageRegistry {
state
.available_languages
.iter()
.find(|l| l.config.name.0.as_ref() == name)
.find(|l| l.name.0.as_ref() == name)
.cloned()
}
@ -752,11 +766,8 @@ impl LanguageRegistry {
let current_match_type = best_language_match
.as_ref()
.map_or(LanguageMatchPrecedence::default(), |(_, score)| *score);
let language_score = callback(
&language.config.name,
&language.config.matcher,
current_match_type,
);
let language_score =
callback(&language.name, &language.matcher, current_match_type);
debug_assert!(
language_score.is_none_or(|new_score| new_score > current_match_type),
"Matching callback should only return a better match than the current one"
@ -804,7 +815,7 @@ impl LanguageRegistry {
let this = self.clone();
let id = language.id;
let name = language.config.name.clone();
let name = language.name.clone();
let language_load = language.load.clone();
self.executor
@ -1120,7 +1131,7 @@ impl LanguageRegistryState {
self.languages
.retain(|language| !languages_to_remove.contains(&language.name()));
self.available_languages
.retain(|language| !languages_to_remove.contains(&language.config.name));
.retain(|language| !languages_to_remove.contains(&language.name));
self.grammars
.retain(|name, _| !grammars_to_remove.contains(name));
self.version += 1;

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use anyhow::Result;
use extension::{ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageProxy};
use language::{LanguageConfig, LanguageName, LanguageRegistry, LoadedLanguage};
use language::{LanguageMatcher, LanguageName, LanguageRegistry, LoadedLanguage};
pub fn init(
extension_host_proxy: Arc<ExtensionHostProxy>,
@ -31,10 +31,14 @@ impl ExtensionGrammarProxy for LanguageServerRegistryProxy {
impl ExtensionLanguageProxy for LanguageServerRegistryProxy {
fn register_language(
&self,
language: LanguageConfig,
language: LanguageName,
grammar: Option<Arc<str>>,
matcher: LanguageMatcher,
hidden: bool,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
) {
self.language_registry.register_language(language, load);
self.language_registry
.register_language(language, grammar, matcher, hidden, load);
}
fn remove_languages(

View File

@ -325,7 +325,10 @@ fn register_language(
languages.register_lsp_adapter(config.name.clone(), adapter);
}
languages.register_language(
config.clone(),
config.name.clone(),
config.grammar.clone(),
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),