Detailed changes
@@ -908,6 +908,8 @@
"hard_tabs": false,
// How many columns a tab should occupy.
"tab_size": 4,
+ // What debuggers are preferred by default for all languages.
+ "debuggers": [],
// Control what info is collected by Zed.
"telemetry": {
// Send debug info like crash reports.
@@ -621,20 +621,32 @@ impl DebugPanel {
move |_, window, cx| {
let weak_panel = weak_panel.clone();
let past_debug_definition = past_debug_definition.clone();
+ let workspace = workspace.clone();
+ window
+ .spawn(cx, async move |cx| {
+ let task_contexts = workspace
+ .update_in(cx, |workspace, window, cx| {
+ tasks_ui::task_contexts(workspace, window, cx)
+ })?
+ .await;
+
+ workspace.update_in(cx, |this, window, cx| {
+ this.toggle_modal(window, cx, |window, cx| {
+ NewSessionModal::new(
+ past_debug_definition,
+ weak_panel,
+ workspace.clone(),
+ None,
+ task_contexts,
+ window,
+ cx,
+ )
+ });
+ })?;
- let _ = workspace.update(cx, |this, cx| {
- let workspace = cx.weak_entity();
- this.toggle_modal(window, cx, |window, cx| {
- NewSessionModal::new(
- past_debug_definition,
- weak_panel,
- workspace,
- None,
- window,
- cx,
- )
- });
- });
+ Result::<_, anyhow::Error>::Ok(())
+ })
+ .detach();
}
})
.tooltip({
@@ -153,16 +153,29 @@ pub fn init(cx: &mut App) {
let weak_panel = debug_panel.downgrade();
let weak_workspace = cx.weak_entity();
- workspace.toggle_modal(window, cx, |window, cx| {
- NewSessionModal::new(
- debug_panel.read(cx).past_debug_definition.clone(),
- weak_panel,
- weak_workspace,
- None,
- window,
- cx,
- )
- });
+ cx.spawn_in(window, async move |this, cx| {
+ let task_contexts = this
+ .update_in(cx, |workspace, window, cx| {
+ tasks_ui::task_contexts(workspace, window, cx)
+ })?
+ .await;
+ this.update_in(cx, |workspace, window, cx| {
+ workspace.toggle_modal(window, cx, |window, cx| {
+ NewSessionModal::new(
+ debug_panel.read(cx).past_debug_definition.clone(),
+ weak_panel,
+ weak_workspace,
+ None,
+ task_contexts,
+ window,
+ cx,
+ )
+ });
+ })?;
+
+ Result::<_, anyhow::Error>::Ok(())
+ })
+ .detach();
}
},
)
@@ -172,16 +185,30 @@ pub fn init(cx: &mut App) {
let weak_workspace = cx.weak_entity();
let task_store = workspace.project().read(cx).task_store().clone();
- workspace.toggle_modal(window, cx, |window, cx| {
- NewSessionModal::new(
- debug_panel.read(cx).past_debug_definition.clone(),
- weak_panel,
- weak_workspace,
- Some(task_store),
- window,
- cx,
- )
- });
+ cx.spawn_in(window, async move |this, cx| {
+ let task_contexts = this
+ .update_in(cx, |workspace, window, cx| {
+ tasks_ui::task_contexts(workspace, window, cx)
+ })?
+ .await;
+
+ this.update_in(cx, |workspace, window, cx| {
+ workspace.toggle_modal(window, cx, |window, cx| {
+ NewSessionModal::new(
+ debug_panel.read(cx).past_debug_definition.clone(),
+ weak_panel,
+ weak_workspace,
+ Some(task_store),
+ task_contexts,
+ window,
+ cx,
+ )
+ });
+ })?;
+
+ anyhow::Ok(())
+ })
+ .detach()
}
});
})
@@ -1,9 +1,12 @@
use std::{
borrow::Cow,
+ cmp::Reverse,
ops::Not,
path::{Path, PathBuf},
+ sync::Arc,
};
+use collections::{HashMap, HashSet};
use dap::{
DapRegistry, DebugRequest,
adapters::{DebugAdapterName, DebugTaskDefinition},
@@ -15,10 +18,9 @@ use gpui::{
Subscription, TextStyle, WeakEntity,
};
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
-use project::{TaskSourceKind, task_store::TaskStore};
+use project::{TaskContexts, TaskSourceKind, task_store::TaskStore};
use settings::Settings;
use task::{DebugScenario, LaunchRequest};
-use tasks_ui::task_contexts;
use theme::ThemeSettings;
use ui::{
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context,
@@ -32,7 +34,6 @@ use workspace::{ModalView, Workspace};
use crate::{attach_modal::AttachModal, debugger_panel::DebugPanel};
-#[derive(Clone)]
pub(super) struct NewSessionModal {
workspace: WeakEntity<Workspace>,
debug_panel: WeakEntity<DebugPanel>,
@@ -41,6 +42,7 @@ pub(super) struct NewSessionModal {
initialize_args: Option<serde_json::Value>,
debugger: Option<DebugAdapterName>,
last_selected_profile_name: Option<SharedString>,
+ task_contexts: Arc<TaskContexts>,
}
fn suggested_label(request: &DebugRequest, debugger: &str) -> SharedString {
@@ -67,6 +69,7 @@ impl NewSessionModal {
debug_panel: WeakEntity<DebugPanel>,
workspace: WeakEntity<Workspace>,
task_store: Option<Entity<TaskStore>>,
+ task_contexts: TaskContexts,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -105,6 +108,7 @@ impl NewSessionModal {
.unwrap_or(ToggleState::Unselected),
last_selected_profile_name: None,
initialize_args: None,
+ task_contexts: Arc::new(task_contexts),
}
}
@@ -145,22 +149,10 @@ impl NewSessionModal {
};
let debug_panel = self.debug_panel.clone();
- let workspace = self.workspace.clone();
+ let task_contexts = self.task_contexts.clone();
cx.spawn_in(window, async move |this, cx| {
- let task_contexts = workspace
- .update_in(cx, |this, window, cx| task_contexts(this, window, cx))?
- .await;
+ let task_context = task_contexts.active_context().cloned().unwrap_or_default();
let worktree_id = task_contexts.worktree();
- let task_context = task_contexts
- .active_item_context
- .map(|(_, _, context)| context)
- .or_else(|| {
- task_contexts
- .active_worktree_context
- .map(|(_, context)| context)
- })
- .unwrap_or_default();
-
debug_panel.update_in(cx, |debug_panel, window, cx| {
debug_panel.start_session(config, task_context, None, worktree_id, window, cx)
})?;
@@ -198,14 +190,27 @@ impl NewSessionModal {
&self,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> ui::DropdownMenu {
+ ) -> 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())
+ });
DropdownMenu::new(
"dap-adapter-picker",
label,
@@ -224,17 +229,50 @@ impl NewSessionModal {
}
};
- let available_adapters = workspace
+ 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();
- for adapter in available_adapters {
+ 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),
+ )
+ });
+
+ for adapter in available_adapters.into_iter() {
menu = menu.entry(adapter.0.clone(), None, setter_for_name(adapter.clone()));
}
menu
}),
)
+ .into()
}
fn debug_config_drop_down_menu(
@@ -591,7 +629,9 @@ impl Render for NewSessionModal {
),
)
.justify_between()
- .child(self.adapter_drop_down_menu(window, cx))
+ .when(!matches!(self.mode, NewSessionMode::Scenario(_)), |this| {
+ this.children(self.adapter_drop_down_menu(window, cx))
+ })
.border_color(cx.theme().colors().border_variant)
.border_b_1(),
)
@@ -5206,12 +5206,22 @@ impl Editor {
let dap_store = project.read(cx).dap_store();
let mut scenarios = vec![];
let resolved_tasks = resolved_tasks.as_ref()?;
- let debug_adapter: SharedString = buffer
- .read(cx)
- .language()?
- .context_provider()?
- .debug_adapter()?
- .into();
+ let buffer = buffer.read(cx);
+ let language = buffer.language()?;
+ let file = buffer.file();
+ let debug_adapter =
+ language_settings(language.name().into(), file, cx)
+ .debuggers
+ .first()
+ .map(SharedString::from)
+ .or_else(|| {
+ language
+ .config()
+ .debuggers
+ .first()
+ .map(SharedString::from)
+ })?;
+
dap_store.update(cx, |this, cx| {
for (_, task) in &resolved_tasks.templates {
if let Some(scenario) = this
@@ -4,7 +4,7 @@ use std::sync::Arc;
use anyhow::Result;
use fs::Fs;
use gpui::{App, Global, ReadGlobal, SharedString, Task};
-use language::{BinaryStatus, LanguageMatcher, LanguageName, LoadedLanguage};
+use language::{BinaryStatus, LanguageConfig, LanguageName, LoadedLanguage};
use lsp::LanguageServerName;
use parking_lot::RwLock;
@@ -224,10 +224,7 @@ impl ExtensionGrammarProxy for ExtensionHostProxy {
pub trait ExtensionLanguageProxy: Send + Sync + 'static {
fn register_language(
&self,
- language: LanguageName,
- grammar: Option<Arc<str>>,
- matcher: LanguageMatcher,
- hidden: bool,
+ config: LanguageConfig,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
);
@@ -241,17 +238,14 @@ pub trait ExtensionLanguageProxy: Send + Sync + 'static {
impl ExtensionLanguageProxy for ExtensionHostProxy {
fn register_language(
&self,
- language: LanguageName,
- grammar: Option<Arc<str>>,
- matcher: LanguageMatcher,
- hidden: bool,
+ language: LanguageConfig,
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)
+ proxy.register_language(language, load)
}
fn remove_languages(
@@ -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(),
@@ -26,7 +26,7 @@ pub use crate::language_settings::EditPredictionsMode;
use crate::language_settings::SoftWrap;
use anyhow::{Context as _, Result, anyhow};
use async_trait::async_trait;
-use collections::{HashMap, HashSet};
+use collections::{HashMap, HashSet, IndexSet};
use fs::Fs;
use futures::Future;
use gpui::{App, AsyncApp, Entity, SharedString, Task};
@@ -666,7 +666,7 @@ pub struct CodeLabel {
pub filter_range: Range<usize>,
}
-#[derive(Clone, Deserialize, JsonSchema)]
+#[derive(Clone, Deserialize, JsonSchema, Serialize, Debug)]
pub struct LanguageConfig {
/// Human-readable name of the language.
pub name: LanguageName,
@@ -690,12 +690,20 @@ 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")]
+ #[serde(
+ default,
+ deserialize_with = "deserialize_regex",
+ serialize_with = "serialize_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")]
+ #[serde(
+ default,
+ deserialize_with = "deserialize_regex",
+ serialize_with = "serialize_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
@@ -748,6 +756,9 @@ pub struct LanguageConfig {
/// A list of characters that Zed should treat as word characters for completion queries.
#[serde(default)]
pub completion_query_characters: HashSet<char>,
+ /// A list of preferred debuggers for this language.
+ #[serde(default)]
+ pub debuggers: IndexSet<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
@@ -766,7 +777,7 @@ pub struct LanguageMatcher {
}
/// The configuration for JSX tag auto-closing.
-#[derive(Clone, Deserialize, JsonSchema)]
+#[derive(Clone, Deserialize, JsonSchema, Serialize, Debug)]
pub struct JsxTagAutoCloseConfig {
/// The name of the node for a opening tag
pub open_tag_node_name: String,
@@ -807,7 +818,7 @@ pub struct LanguageScope {
override_id: Option<u32>,
}
-#[derive(Clone, Deserialize, Default, Debug, JsonSchema)]
+#[derive(Clone, Deserialize, Default, Debug, JsonSchema, Serialize)]
pub struct LanguageConfigOverride {
#[serde(default)]
pub line_comments: Override<Vec<Arc<str>>>,
@@ -872,6 +883,7 @@ impl Default for LanguageConfig {
hidden: false,
jsx_tag_auto_close: None,
completion_query_characters: Default::default(),
+ debuggers: Default::default(),
}
}
}
@@ -932,7 +944,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)]
+#[derive(Clone, Debug, Default, JsonSchema, Serialize)]
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>,
@@ -982,7 +994,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)]
+#[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema, Serialize)]
pub struct BracketPair {
/// Starting substring for a bracket.
pub start: String,
@@ -145,24 +145,24 @@ pub enum BinaryStatus {
#[derive(Clone)]
pub struct AvailableLanguage {
id: LanguageId,
- name: LanguageName,
- grammar: Option<Arc<str>>,
- matcher: LanguageMatcher,
- hidden: bool,
+ config: LanguageConfig,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
loaded: bool,
}
impl AvailableLanguage {
pub fn name(&self) -> LanguageName {
- self.name.clone()
+ self.config.name.clone()
}
pub fn matcher(&self) -> &LanguageMatcher {
- &self.matcher
+ &self.config.matcher
}
pub fn hidden(&self) -> bool {
- self.hidden
+ self.config.hidden
+ }
+ pub fn config(&self) -> &LanguageConfig {
+ &self.config
}
}
@@ -326,10 +326,7 @@ impl LanguageRegistry {
#[cfg(any(feature = "test-support", test))]
pub fn register_test_language(&self, config: LanguageConfig) {
self.register_language(
- config.name.clone(),
- config.grammar.clone(),
- config.matcher.clone(),
- config.hidden,
+ config.clone(),
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
@@ -488,18 +485,14 @@ impl LanguageRegistry {
/// Adds a language to the registry, which can be loaded if needed.
pub fn register_language(
&self,
- name: LanguageName,
- grammar_name: Option<Arc<str>>,
- matcher: LanguageMatcher,
- hidden: bool,
+ config: LanguageConfig,
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.name == name {
- existing_language.grammar = grammar_name;
- existing_language.matcher = matcher;
+ if existing_language.config.name == config.name {
+ existing_language.config = config;
existing_language.load = load;
return;
}
@@ -507,11 +500,8 @@ impl LanguageRegistry {
state.available_languages.push(AvailableLanguage {
id: LanguageId::new(),
- name,
- grammar: grammar_name,
- matcher,
+ config,
load,
- hidden,
loaded: false,
});
state.version += 1;
@@ -557,7 +547,7 @@ impl LanguageRegistry {
let mut result = state
.available_languages
.iter()
- .filter_map(|l| l.loaded.not().then_some(l.name.to_string()))
+ .filter_map(|l| l.loaded.not().then_some(l.config.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());
@@ -576,10 +566,7 @@ impl LanguageRegistry {
let mut state = self.state.write();
state.available_languages.push(AvailableLanguage {
id: language.id,
- name: language.name(),
- grammar: language.config.grammar.clone(),
- matcher: language.config.matcher.clone(),
- hidden: language.config.hidden,
+ config: language.config.clone(),
load: Arc::new(|| Err(anyhow!("already loaded"))),
loaded: true,
});
@@ -648,7 +635,7 @@ impl LanguageRegistry {
state
.available_languages
.iter()
- .find(|l| l.name.0.as_ref() == name)
+ .find(|l| l.config.name.0.as_ref() == name)
.cloned()
}
@@ -765,8 +752,11 @@ impl LanguageRegistry {
let current_match_type = best_language_match
.as_ref()
.map_or(LanguageMatchPrecedence::default(), |(_, score)| *score);
- let language_score =
- callback(&language.name, &language.matcher, current_match_type);
+ let language_score = callback(
+ &language.config.name,
+ &language.config.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"
@@ -814,7 +804,7 @@ impl LanguageRegistry {
let this = self.clone();
let id = language.id;
- let name = language.name.clone();
+ let name = language.config.name.clone();
let language_load = language.load.clone();
self.executor
@@ -1130,7 +1120,7 @@ impl LanguageRegistryState {
self.languages
.retain(|language| !languages_to_remove.contains(&language.name()));
self.available_languages
- .retain(|language| !languages_to_remove.contains(&language.name));
+ .retain(|language| !languages_to_remove.contains(&language.config.name));
self.grammars
.retain(|name, _| !grammars_to_remove.contains(name));
self.version += 1;
@@ -153,6 +153,8 @@ pub struct LanguageSettings {
pub show_completion_documentation: bool,
/// Completion settings for this language.
pub completions: CompletionSettings,
+ /// Preferred debuggers for this language.
+ pub debuggers: Vec<String>,
}
impl LanguageSettings {
@@ -551,6 +553,10 @@ pub struct LanguageSettingsContent {
pub show_completion_documentation: Option<bool>,
/// Controls how completions are processed for this language.
pub completions: Option<CompletionSettings>,
+ /// Preferred debuggers for this language.
+ ///
+ /// Default: []
+ pub debuggers: Option<Vec<String>>,
}
/// The behavior of `editor::Rewrap`.
@@ -47,7 +47,4 @@ pub trait ContextProvider: Send + Sync {
fn lsp_task_source(&self) -> Option<LanguageServerName> {
None
}
-
- /// Default debug adapter for a given language.
- fn debug_adapter(&self) -> Option<String>;
}
@@ -5,7 +5,7 @@ use std::sync::Arc;
use anyhow::Result;
use extension::{ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageProxy};
-use language::{LanguageMatcher, LanguageName, LanguageRegistry, LoadedLanguage};
+use language::{LanguageConfig, LanguageName, LanguageRegistry, LoadedLanguage};
pub fn init(
extension_host_proxy: Arc<ExtensionHostProxy>,
@@ -31,14 +31,10 @@ impl ExtensionGrammarProxy for LanguageServerRegistryProxy {
impl ExtensionLanguageProxy for LanguageServerRegistryProxy {
fn register_language(
&self,
- language: LanguageName,
- grammar: Option<Arc<str>>,
- matcher: LanguageMatcher,
- hidden: bool,
+ language: LanguageConfig,
load: Arc<dyn Fn() -> Result<LoadedLanguage> + Send + Sync + 'static>,
) {
- self.language_registry
- .register_language(language, grammar, matcher, hidden, load);
+ self.language_registry.register_language(language, load);
}
fn remove_languages(
@@ -11,3 +11,4 @@ brackets = [
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
+debuggers = ["CodeLLDB", "GDB"]
@@ -11,3 +11,4 @@ brackets = [
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
+debuggers = ["CodeLLDB", "GDB"]
@@ -630,10 +630,6 @@ impl ContextProvider for GoContextProvider {
},
]))
}
-
- fn debug_adapter(&self) -> Option<String> {
- Some("Delve".into())
- }
}
fn extract_subtest_name(input: &str) -> Option<String> {
@@ -14,3 +14,4 @@ brackets = [
]
tab_size = 4
hard_tabs = true
+debuggers = ["Delve"]
@@ -19,6 +19,7 @@ word_characters = ["$", "#"]
tab_size = 2
scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"]
prettier_parser_name = "babel"
+debuggers = ["JavaScript"]
[jsx_tag_auto_close]
open_tag_node_name = "jsx_opening_element"
@@ -325,10 +325,7 @@ fn register_language(
languages.register_lsp_adapter(config.name.clone(), adapter);
}
languages.register_language(
- config.name.clone(),
- config.grammar.clone(),
- config.matcher.clone(),
- config.hidden,
+ config.clone(),
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
@@ -474,10 +474,6 @@ impl ContextProvider for PythonContextProvider {
Some(TaskTemplates(tasks))
}
-
- fn debug_adapter(&self) -> Option<String> {
- Some("Debugpy".into())
- }
}
fn selected_test_runner(location: Option<&Arc<dyn language::File>>, cx: &App) -> TestRunner {
@@ -29,3 +29,4 @@ brackets = [
auto_indent_using_last_non_empty_line = false
increase_indent_pattern = "^[^#].*:\\s*$"
decrease_indent_pattern = "^\\s*(else|elif|except|finally)\\b.*:"
+debuggers = ["Debugpy"]
@@ -803,10 +803,6 @@ impl ContextProvider for RustContextProvider {
fn lsp_task_source(&self) -> Option<LanguageServerName> {
Some(SERVER_NAME)
}
-
- fn debug_adapter(&self) -> Option<String> {
- Some("CodeLLDB".to_owned())
- }
}
/// Part of the data structure of Cargo metadata
@@ -15,3 +15,4 @@ brackets = [
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
collapsed_placeholder = " /* ... */ "
+debuggers = ["CodeLLDB", "GDB"]
@@ -17,6 +17,7 @@ word_characters = ["#", "$"]
scope_opt_in_language_servers = ["tailwindcss-language-server", "emmet-language-server"]
prettier_parser_name = "typescript"
tab_size = 2
+debuggers = ["JavaScript"]
[jsx_tag_auto_close]
open_tag_node_name = "jsx_opening_element"
@@ -17,6 +17,7 @@ brackets = [
word_characters = ["#", "$"]
prettier_parser_name = "typescript"
tab_size = 2
+debuggers = ["JavaScript"]
[overrides.string]
completion_query_characters = ["."]
@@ -808,10 +808,6 @@ impl ContextProvider for BasicContextProvider {
Task::ready(Ok(task_variables))
}
-
- fn debug_adapter(&self) -> Option<String> {
- None
- }
}
/// A ContextProvider that doesn't provide any task variables on it's own, though it has some associated tasks.
@@ -835,10 +831,6 @@ impl ContextProvider for ContextProviderWithTasks {
) -> Option<TaskTemplates> {
Some(self.templates.clone())
}
-
- fn debug_adapter(&self) -> Option<String> {
- None
- }
}
#[cfg(test)]