debugger: Rework language association with the debuggers (#29945)
- Languages now define their preferred debuggers in `config.toml`. - `LanguageRegistry` now exposes language config even for languages that are not yet loaded. This necessitated extension registry changes (we now deserialize config.toml of all language entries when loading new extension index), but it should be backwards compatible with the old format. /cc @maxdeviant Release Notes: - N/A --------- Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Remco Smits <djsmits12@gmail.com> Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
parent
544e8fc46c
commit
09d3ff9dbe
27 changed files with 386 additions and 216 deletions
|
@ -34,8 +34,7 @@ use gpui::{
|
|||
};
|
||||
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
||||
use language::{
|
||||
LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
|
||||
QUERY_FILENAME_PREFIXES, Rope,
|
||||
LanguageConfig, LanguageName, LanguageQueries, LoadedLanguage, QUERY_FILENAME_PREFIXES, Rope,
|
||||
};
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::ContextProviderWithTasks;
|
||||
|
@ -140,7 +139,7 @@ struct GlobalExtensionStore(Entity<ExtensionStore>);
|
|||
|
||||
impl Global for GlobalExtensionStore {}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
|
||||
#[derive(Deserialize, Serialize, Default)]
|
||||
pub struct ExtensionIndex {
|
||||
pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
|
||||
pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
|
||||
|
@ -167,13 +166,12 @@ pub struct ExtensionIndexIconThemeEntry {
|
|||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ExtensionIndexLanguageEntry {
|
||||
pub extension: Arc<str>,
|
||||
pub path: PathBuf,
|
||||
pub matcher: LanguageMatcher,
|
||||
pub hidden: bool,
|
||||
pub grammar: Option<Arc<str>>,
|
||||
#[serde(skip)]
|
||||
pub config: LanguageConfig,
|
||||
}
|
||||
|
||||
actions!(zed, [ReloadExtensions]);
|
||||
|
@ -1015,7 +1013,7 @@ impl ExtensionStore {
|
|||
/// added to the manifest, or whose files have changed on disk.
|
||||
fn extensions_updated(
|
||||
&mut self,
|
||||
new_index: ExtensionIndex,
|
||||
mut new_index: ExtensionIndex,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<()> {
|
||||
let old_index = &self.extension_index;
|
||||
|
@ -1143,11 +1141,6 @@ impl ExtensionStore {
|
|||
self.proxy
|
||||
.remove_languages(&languages_to_remove, &grammars_to_remove);
|
||||
|
||||
let languages_to_add = new_index
|
||||
.languages
|
||||
.iter()
|
||||
.filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
|
||||
.collect::<Vec<_>>();
|
||||
let mut grammars_to_add = Vec::new();
|
||||
let mut themes_to_add = Vec::new();
|
||||
let mut icon_themes_to_add = Vec::new();
|
||||
|
@ -1189,39 +1182,7 @@ impl ExtensionStore {
|
|||
|
||||
self.proxy.register_grammars(grammars_to_add);
|
||||
|
||||
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<_>)
|
||||
});
|
||||
|
||||
Ok(LoadedLanguage {
|
||||
config,
|
||||
queries,
|
||||
context_provider,
|
||||
toolchain_provider: None,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
let installed_dir = self.installed_dir.clone();
|
||||
|
||||
let fs = self.fs.clone();
|
||||
let wasm_host = self.wasm_host.clone();
|
||||
|
@ -1232,11 +1193,59 @@ impl ExtensionStore {
|
|||
.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 {
|
||||
|
@ -1439,9 +1448,7 @@ impl ExtensionStore {
|
|||
ExtensionIndexLanguageEntry {
|
||||
extension: extension_id.clone(),
|
||||
path: relative_path,
|
||||
matcher: config.matcher,
|
||||
hidden: config.hidden,
|
||||
grammar: config.grammar,
|
||||
config,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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, LanguageMatcher, LanguageRegistry};
|
||||
use language::{BinaryStatus, LanguageConfig, LanguageMatcher, LanguageRegistry};
|
||||
use lsp::LanguageServerName;
|
||||
use node_runtime::NodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
|
@ -206,11 +206,14 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
ExtensionIndexLanguageEntry {
|
||||
extension: "zed-ruby".into(),
|
||||
path: "languages/erb".into(),
|
||||
grammar: Some("embedded_template".into()),
|
||||
hidden: false,
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["erb".into()],
|
||||
first_line_pattern: None,
|
||||
config: LanguageConfig {
|
||||
grammar: Some("embedded_template".into()),
|
||||
hidden: false,
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["erb".into()],
|
||||
first_line_pattern: None,
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
),
|
||||
|
@ -219,11 +222,14 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
ExtensionIndexLanguageEntry {
|
||||
extension: "zed-ruby".into(),
|
||||
path: "languages/ruby".into(),
|
||||
grammar: Some("ruby".into()),
|
||||
hidden: false,
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rb".into()],
|
||||
first_line_pattern: None,
|
||||
config: LanguageConfig {
|
||||
grammar: Some("ruby".into()),
|
||||
hidden: false,
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rb".into()],
|
||||
first_line_pattern: None,
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
),
|
||||
|
@ -290,7 +296,24 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
store.read_with(cx, |store, _| {
|
||||
let index = &store.extension_index;
|
||||
assert_eq!(index.extensions, expected_index.extensions);
|
||||
assert_eq!(index.languages, expected_index.languages);
|
||||
|
||||
for ((actual_key, actual_language), (expected_key, expected_language)) in
|
||||
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!(index.themes, expected_index.themes);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -377,8 +400,26 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION);
|
||||
store.read_with(cx, |store, _| {
|
||||
let index = &store.extension_index;
|
||||
|
||||
for ((actual_key, actual_language), (expected_key, expected_language)) in
|
||||
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!(index.extensions, expected_index.extensions);
|
||||
assert_eq!(index.languages, expected_index.languages);
|
||||
assert_eq!(index.themes, expected_index.themes);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -415,7 +456,34 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
|
||||
cx.executor().run_until_parked();
|
||||
store.read_with(cx, |store, _| {
|
||||
assert_eq!(store.extension_index, expected_index);
|
||||
assert_eq!(store.extension_index.extensions, expected_index.extensions);
|
||||
assert_eq!(store.extension_index.themes, expected_index.themes);
|
||||
assert_eq!(
|
||||
store.extension_index.icon_themes,
|
||||
expected_index.icon_themes
|
||||
);
|
||||
|
||||
for ((actual_key, actual_language), (expected_key, expected_language)) in store
|
||||
.extension_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!(
|
||||
language_registry.language_names(),
|
||||
["ERB", "Plain Text", "Ruby"]
|
||||
|
@ -452,7 +520,34 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
expected_index.languages.remove("ERB");
|
||||
|
||||
store.read_with(cx, |store, _| {
|
||||
assert_eq!(store.extension_index, expected_index);
|
||||
assert_eq!(store.extension_index.extensions, expected_index.extensions);
|
||||
assert_eq!(store.extension_index.themes, expected_index.themes);
|
||||
assert_eq!(
|
||||
store.extension_index.icon_themes,
|
||||
expected_index.icon_themes
|
||||
);
|
||||
|
||||
for ((actual_key, actual_language), (expected_key, expected_language)) in store
|
||||
.extension_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!(language_registry.language_names(), ["Plain Text"]);
|
||||
assert_eq!(language_registry.grammar_names(), []);
|
||||
});
|
||||
|
|
|
@ -149,10 +149,7 @@ impl HeadlessExtensionStore {
|
|||
config.grammar = None;
|
||||
|
||||
this.proxy.register_language(
|
||||
config.name.clone(),
|
||||
None,
|
||||
config.matcher.clone(),
|
||||
config.hidden,
|
||||
config.clone(),
|
||||
Arc::new(move || {
|
||||
Ok(LoadedLanguage {
|
||||
config: config.clone(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue