Detailed changes
@@ -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()));
@@ -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(
@@ -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,
},
);
}
@@ -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"]);
@@ -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(),
@@ -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,
@@ -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;
@@ -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(
@@ -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(),