debugger: Show language icons in debug scenario picker (#30662)
We attempt to resolve the language name in this order 1. Based on debug adapter if they're for a singular language e.g. Delve 2. File extension if it exists 3. If a language name exists within a debug scenario's label In the future I want to use locators to also determine the language as well and refresh scenario list when a new scenario has been saved Release Notes: - N/A
This commit is contained in:
parent
9826b7b5c1
commit
f1fe505649
8 changed files with 115 additions and 14 deletions
|
@ -8,7 +8,7 @@ pub use dap_types::{StartDebuggingRequestArguments, StartDebuggingRequestArgumen
|
||||||
use futures::io::BufReader;
|
use futures::io::BufReader;
|
||||||
use gpui::{AsyncApp, SharedString};
|
use gpui::{AsyncApp, SharedString};
|
||||||
pub use http_client::{HttpClient, github::latest_github_release};
|
pub use http_client::{HttpClient, github::latest_github_release};
|
||||||
use language::LanguageToolchainStore;
|
use language::{LanguageName, LanguageToolchainStore};
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::WorktreeId;
|
use settings::WorktreeId;
|
||||||
|
@ -418,6 +418,11 @@ pub trait DebugAdapter: 'static + Send + Sync {
|
||||||
user_installed_path: Option<PathBuf>,
|
user_installed_path: Option<PathBuf>,
|
||||||
cx: &mut AsyncApp,
|
cx: &mut AsyncApp,
|
||||||
) -> Result<DebugAdapterBinary>;
|
) -> Result<DebugAdapterBinary>;
|
||||||
|
|
||||||
|
/// Returns the language name of an adapter if it only supports one language
|
||||||
|
fn adapter_language_name(&self) -> Option<LanguageName> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
|
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use collections::FxHashMap;
|
use collections::FxHashMap;
|
||||||
use gpui::{App, Global, SharedString};
|
use gpui::{App, Global, SharedString};
|
||||||
|
use language::LanguageName;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use task::{DebugRequest, DebugScenario, SpawnInTerminal, TaskTemplate};
|
use task::{DebugRequest, DebugScenario, SpawnInTerminal, TaskTemplate};
|
||||||
|
|
||||||
|
@ -59,6 +60,11 @@ impl DapRegistry {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn adapter_language(&self, adapter_name: &str) -> Option<LanguageName> {
|
||||||
|
self.adapter(adapter_name)
|
||||||
|
.and_then(|adapter| adapter.adapter_language_name())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_locator(&self, locator: Arc<dyn DapLocator>) {
|
pub fn add_locator(&self, locator: Arc<dyn DapLocator>) {
|
||||||
let _previous_value = self.0.write().locators.insert(locator.name(), locator);
|
let _previous_value = self.0.write().locators.insert(locator.name(), locator);
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
||||||
use gpui::AsyncApp;
|
use gpui::{AsyncApp, SharedString};
|
||||||
|
use language::LanguageName;
|
||||||
use std::{collections::HashMap, ffi::OsStr, path::PathBuf};
|
use std::{collections::HashMap, ffi::OsStr, path::PathBuf};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
@ -43,6 +44,10 @@ impl DebugAdapter for GoDebugAdapter {
|
||||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn adapter_language_name(&self) -> Option<LanguageName> {
|
||||||
|
Some(SharedString::new_static("Go").into())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_binary(
|
async fn get_binary(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn DapDelegate,
|
delegate: &dyn DapDelegate,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use adapters::latest_github_release;
|
use adapters::latest_github_release;
|
||||||
use dap::adapters::{DebugTaskDefinition, TcpArguments};
|
use dap::adapters::{DebugTaskDefinition, TcpArguments};
|
||||||
use gpui::AsyncApp;
|
use gpui::{AsyncApp, SharedString};
|
||||||
|
use language::LanguageName;
|
||||||
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
@ -119,6 +120,10 @@ impl DebugAdapter for PhpDebugAdapter {
|
||||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn adapter_language_name(&self) -> Option<LanguageName> {
|
||||||
|
Some(SharedString::new_static("PHP").into())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_binary(
|
async fn get_binary(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn DapDelegate,
|
delegate: &dyn DapDelegate,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use dap::{DebugRequest, StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
use dap::{DebugRequest, StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
||||||
use gpui::AsyncApp;
|
use gpui::{AsyncApp, SharedString};
|
||||||
|
use language::LanguageName;
|
||||||
use std::{collections::HashMap, ffi::OsStr, path::PathBuf, sync::OnceLock};
|
use std::{collections::HashMap, ffi::OsStr, path::PathBuf, sync::OnceLock};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
@ -165,6 +166,10 @@ impl DebugAdapter for PythonDebugAdapter {
|
||||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn adapter_language_name(&self) -> Option<LanguageName> {
|
||||||
|
Some(SharedString::new_static("Python").into())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_binary(
|
async fn get_binary(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn DapDelegate,
|
delegate: &dyn DapDelegate,
|
||||||
|
|
|
@ -6,7 +6,8 @@ use dap::{
|
||||||
self, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
|
self, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use gpui::AsyncApp;
|
use gpui::{AsyncApp, SharedString};
|
||||||
|
use language::LanguageName;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use util::command::new_smol_command;
|
use util::command::new_smol_command;
|
||||||
|
|
||||||
|
@ -25,6 +26,10 @@ impl DebugAdapter for RubyDebugAdapter {
|
||||||
DebugAdapterName(Self::ADAPTER_NAME.into())
|
DebugAdapterName(Self::ADAPTER_NAME.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn adapter_language_name(&self) -> Option<LanguageName> {
|
||||||
|
Some(SharedString::new_static("Ruby").into())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_binary(
|
async fn get_binary(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn DapDelegate,
|
delegate: &dyn DapDelegate,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use collections::FxHashMap;
|
use collections::FxHashMap;
|
||||||
|
use language::LanguageRegistry;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
ops::Not,
|
ops::Not,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
usize,
|
usize,
|
||||||
};
|
};
|
||||||
|
@ -81,6 +83,7 @@ impl NewSessionModal {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let task_store = workspace.project().read(cx).task_store().clone();
|
let task_store = workspace.project().read(cx).task_store().clone();
|
||||||
|
let languages = workspace.app_state().languages.clone();
|
||||||
|
|
||||||
cx.spawn_in(window, async move |workspace, cx| {
|
cx.spawn_in(window, async move |workspace, cx| {
|
||||||
workspace.update_in(cx, |workspace, window, cx| {
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
|
@ -131,9 +134,12 @@ impl NewSessionModal {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.launch_picker.update(cx, |picker, cx| {
|
this.launch_picker.update(cx, |picker, cx| {
|
||||||
picker
|
picker.delegate.task_contexts_loaded(
|
||||||
.delegate
|
task_contexts,
|
||||||
.task_contexts_loaded(task_contexts, window, cx);
|
languages,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
picker.refresh(window, cx);
|
picker.refresh(window, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
@ -944,9 +950,49 @@ impl DebugScenarioDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_scenario_kind(
|
||||||
|
languages: &Arc<LanguageRegistry>,
|
||||||
|
dap_registry: &DapRegistry,
|
||||||
|
scenario: DebugScenario,
|
||||||
|
) -> (Option<TaskSourceKind>, DebugScenario) {
|
||||||
|
let language_names = languages.language_names();
|
||||||
|
let language = dap_registry
|
||||||
|
.adapter_language(&scenario.adapter)
|
||||||
|
.map(|language| TaskSourceKind::Language {
|
||||||
|
name: language.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let language = language.or_else(|| {
|
||||||
|
scenario
|
||||||
|
.request
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|request| match request {
|
||||||
|
DebugRequest::Launch(launch) => launch
|
||||||
|
.program
|
||||||
|
.rsplit_once(".")
|
||||||
|
.and_then(|split| languages.language_name_for_extension(split.1))
|
||||||
|
.map(|name| TaskSourceKind::Language { name: name.into() }),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
scenario.label.split_whitespace().find_map(|word| {
|
||||||
|
language_names
|
||||||
|
.iter()
|
||||||
|
.find(|name| name.eq_ignore_ascii_case(word))
|
||||||
|
.map(|name| TaskSourceKind::Language {
|
||||||
|
name: name.to_owned().into(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
(language, scenario)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn task_contexts_loaded(
|
pub fn task_contexts_loaded(
|
||||||
&mut self,
|
&mut self,
|
||||||
task_contexts: TaskContexts,
|
task_contexts: TaskContexts,
|
||||||
|
languages: Arc<LanguageRegistry>,
|
||||||
_window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Picker<Self>>,
|
cx: &mut Context<Picker<Self>>,
|
||||||
) {
|
) {
|
||||||
|
@ -967,14 +1013,16 @@ impl DebugScenarioDelegate {
|
||||||
self.last_used_candidate_index = Some(recent.len() - 1);
|
self.last_used_candidate_index = Some(recent.len() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dap_registry = cx.global::<DapRegistry>();
|
||||||
|
|
||||||
self.candidates = recent
|
self.candidates = recent
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|scenario| (None, scenario))
|
.map(|scenario| Self::get_scenario_kind(&languages, &dap_registry, scenario))
|
||||||
.chain(
|
.chain(scenarios.into_iter().map(|(kind, scenario)| {
|
||||||
scenarios
|
let (language, scenario) =
|
||||||
.into_iter()
|
Self::get_scenario_kind(&languages, &dap_registry, scenario);
|
||||||
.map(|(kind, scenario)| (Some(kind), scenario)),
|
(language.or(Some(kind)), scenario)
|
||||||
)
|
}))
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,12 @@ impl From<LanguageName> for SharedString {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SharedString> for LanguageName {
|
||||||
|
fn from(value: SharedString) -> Self {
|
||||||
|
LanguageName(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<str> for LanguageName {
|
impl AsRef<str> for LanguageName {
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
self.0.as_ref()
|
self.0.as_ref()
|
||||||
|
@ -627,6 +633,22 @@ impl LanguageRegistry {
|
||||||
async move { rx.await? }
|
async move { rx.await? }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn language_name_for_extension(self: &Arc<Self>, extension: &str) -> Option<LanguageName> {
|
||||||
|
self.state.try_read().and_then(|state| {
|
||||||
|
state
|
||||||
|
.available_languages
|
||||||
|
.iter()
|
||||||
|
.find(|language| {
|
||||||
|
language
|
||||||
|
.matcher()
|
||||||
|
.path_suffixes
|
||||||
|
.iter()
|
||||||
|
.any(|suffix| *suffix == extension)
|
||||||
|
})
|
||||||
|
.map(|language| language.name.clone())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn language_for_name_or_extension(
|
pub fn language_for_name_or_extension(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
string: &str,
|
string: &str,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue