WIP: Add interactions query language feature

Sketch in interaction provider API
Wire interaction data through buffer chunk APIs
This commit is contained in:
Mikayla 2023-08-31 16:31:02 -07:00
parent 166585a2a8
commit ecf8533d28
No known key found for this signature in database
9 changed files with 313 additions and 72 deletions

View file

@ -38,8 +38,8 @@ use gpui::{
use itertools::Itertools; use itertools::Itertools;
use json::json; use json::json;
use language::{ use language::{
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, InteractionId,
Selection, OffsetUtf16, Selection,
}; };
use project::{ use project::{
project_settings::{GitGutterSetting, ProjectSettings}, project_settings::{GitGutterSetting, ProjectSettings},
@ -1659,6 +1659,7 @@ impl EditorElement {
chunk: chunk.text, chunk: chunk.text,
style: highlight_style, style: highlight_style,
is_tab: chunk.is_tab, is_tab: chunk.is_tab,
interaction: chunk.interaction_id,
} }
}); });
@ -1900,6 +1901,7 @@ impl EditorElement {
struct HighlightedChunk<'a> { struct HighlightedChunk<'a> {
chunk: &'a str, chunk: &'a str,
style: Option<HighlightStyle>, style: Option<HighlightStyle>,
interaction: Option<InteractionId>,
is_tab: bool, is_tab: bool,
} }
@ -1930,6 +1932,7 @@ impl LineWithInvisibles {
for highlighted_chunk in chunks.chain([HighlightedChunk { for highlighted_chunk in chunks.chain([HighlightedChunk {
chunk: "\n", chunk: "\n",
style: None, style: None,
interaction: None,
is_tab: false, is_tab: false,
}]) { }]) {
for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() { for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
@ -1960,6 +1963,12 @@ impl LineWithInvisibles {
Cow::Borrowed(text_style) Cow::Borrowed(text_style)
}; };
let interaction = if let Some(interaction) = highlighted_chunk.interaction {
Some(interaction)
} else {
None
};
if line.len() + line_chunk.len() > max_line_len { if line.len() + line_chunk.len() > max_line_len {
let mut chunk_len = max_line_len - line.len(); let mut chunk_len = max_line_len - line.len();
while !line_chunk.is_char_boundary(chunk_len) { while !line_chunk.is_char_boundary(chunk_len) {

View file

@ -5,6 +5,7 @@ pub use crate::{
}; };
use crate::{ use crate::{
diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
interaction_map::{InteractionId, InteractionMap},
language_settings::{language_settings, LanguageSettings}, language_settings::{language_settings, LanguageSettings},
outline::OutlineItem, outline::OutlineItem,
syntax_map::{ syntax_map::{
@ -285,11 +286,74 @@ struct IndentSuggestion {
within_error: bool, within_error: bool,
} }
struct BufferChunkHighlights<'a> { pub(crate) trait QueryFeatureMap {
type Id: Copy + Clone + std::fmt::Debug;
fn get(&self, capture_id: u32) -> Self::Id;
}
type BufferChunkHighlights<'a> = BufferChunkQuery<'a, HighlightMap>;
type BufferChunkInteractions<'a> = BufferChunkQuery<'a, InteractionMap>;
struct BufferChunkQuery<'a, T: QueryFeatureMap> {
captures: SyntaxMapCaptures<'a>, captures: SyntaxMapCaptures<'a>,
next_capture: Option<SyntaxMapCapture<'a>>, next_capture: Option<SyntaxMapCapture<'a>>,
stack: Vec<(usize, HighlightId)>, maps: Vec<T>,
highlight_maps: Vec<HighlightMap>, stack: Vec<(usize, T::Id)>,
}
impl<'a, T: QueryFeatureMap> BufferChunkQuery<'a, T> {
fn seek(&mut self, offset: usize, byte_range: Range<usize>) {
self.stack.retain(|(end_offset, _)| *end_offset > offset);
if let Some(capture) = &self.next_capture {
if offset >= capture.node.start_byte() {
let next_capture_end = capture.node.end_byte();
if offset < next_capture_end {
self.stack.push((
next_capture_end,
self.maps[capture.grammar_index].get(capture.index),
));
}
self.next_capture.take();
}
}
self.captures.set_byte_range(byte_range);
}
fn select_current_id(&self, chunk_end: &mut usize, id: &mut Option<T::Id>) {
if let Some((parent_capture_end, parent_id)) = self.stack.last() {
*chunk_end = (*chunk_end).min(*parent_capture_end);
*id = Some(parent_id.clone());
}
}
fn next(&mut self, range_start: usize, next_capture_start: &mut usize) {
while let Some((parent_capture_end, _)) = self.stack.last() {
if *parent_capture_end <= range_start {
self.stack.pop();
} else {
break;
}
}
if self.next_capture.is_none() {
self.next_capture = self.captures.next();
}
while let Some(capture) = self.next_capture.as_ref() {
if range_start < capture.node.start_byte() {
if capture.node.start_byte() < *next_capture_start {
*next_capture_start = capture.node.start_byte();
}
break;
} else {
let highlight_id = self.maps[capture.grammar_index].get(capture.index);
self.stack.push((capture.node.end_byte(), highlight_id));
self.next_capture = self.captures.next();
}
}
}
} }
pub struct BufferChunks<'a> { pub struct BufferChunks<'a> {
@ -302,12 +366,14 @@ pub struct BufferChunks<'a> {
hint_depth: usize, hint_depth: usize,
unnecessary_depth: usize, unnecessary_depth: usize,
highlights: Option<BufferChunkHighlights<'a>>, highlights: Option<BufferChunkHighlights<'a>>,
interactions: Option<BufferChunkInteractions<'a>>,
} }
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct Chunk<'a> { pub struct Chunk<'a> {
pub text: &'a str, pub text: &'a str,
pub syntax_highlight_id: Option<HighlightId>, pub syntax_highlight_id: Option<HighlightId>,
pub interaction_id: Option<InteractionId>,
pub highlight_style: Option<HighlightStyle>, pub highlight_style: Option<HighlightStyle>,
pub diagnostic_severity: Option<DiagnosticSeverity>, pub diagnostic_severity: Option<DiagnosticSeverity>,
pub is_unnecessary: bool, pub is_unnecessary: bool,
@ -2089,17 +2155,27 @@ impl BufferSnapshot {
let range = range.start.to_offset(self)..range.end.to_offset(self); let range = range.start.to_offset(self)..range.end.to_offset(self);
let mut syntax = None; let mut syntax = None;
let mut interactions = None;
let mut diagnostic_endpoints = Vec::new(); let mut diagnostic_endpoints = Vec::new();
if language_aware { if language_aware {
let captures = self.syntax.captures(range.clone(), &self.text, |grammar| { let captures = self.syntax.captures(range.clone(), &self.text, |grammar| {
grammar.highlights_query.as_ref() grammar.highlights_query.as_ref()
}); });
let interaction_captures = self.syntax.captures(range.clone(), &self.text, |grammar| {
grammar.interactions_query.as_ref()
});
let highlight_maps = captures let highlight_maps = captures
.grammars() .grammars()
.into_iter() .into_iter()
.map(|grammar| grammar.highlight_map()) .map(|grammar| grammar.highlight_map())
.collect(); .collect();
syntax = Some((captures, highlight_maps)); syntax = Some((captures, highlight_maps));
let interaction_maps = interaction_captures
.grammars()
.into_iter()
.map(|grammar| grammar.interaction_map())
.collect();
interactions = Some((interaction_captures, interaction_maps));
for entry in self.diagnostics_in_range::<_, usize>(range.clone(), false) { for entry in self.diagnostics_in_range::<_, usize>(range.clone(), false) {
diagnostic_endpoints.push(DiagnosticEndpoint { diagnostic_endpoints.push(DiagnosticEndpoint {
offset: entry.range.start, offset: entry.range.start,
@ -2118,7 +2194,13 @@ impl BufferSnapshot {
.sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start)); .sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start));
} }
BufferChunks::new(self.text.as_rope(), range, syntax, diagnostic_endpoints) BufferChunks::new(
self.text.as_rope(),
range,
syntax,
interactions,
diagnostic_endpoints,
)
} }
pub fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) { pub fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) {
@ -2735,6 +2817,7 @@ impl<'a> BufferChunks<'a> {
text: &'a Rope, text: &'a Rope,
range: Range<usize>, range: Range<usize>,
syntax: Option<(SyntaxMapCaptures<'a>, Vec<HighlightMap>)>, syntax: Option<(SyntaxMapCaptures<'a>, Vec<HighlightMap>)>,
interactions_syntax: Option<(SyntaxMapCaptures<'a>, Vec<InteractionMap>)>,
diagnostic_endpoints: Vec<DiagnosticEndpoint>, diagnostic_endpoints: Vec<DiagnosticEndpoint>,
) -> Self { ) -> Self {
let mut highlights = None; let mut highlights = None;
@ -2743,7 +2826,17 @@ impl<'a> BufferChunks<'a> {
captures, captures,
next_capture: None, next_capture: None,
stack: Default::default(), stack: Default::default(),
highlight_maps, maps: highlight_maps,
})
}
let mut interactions = None;
if let Some((captures, interaction_maps)) = interactions_syntax {
interactions = Some(BufferChunkInteractions {
captures,
next_capture: None,
stack: Default::default(),
maps: interaction_maps,
}) })
} }
@ -2760,6 +2853,7 @@ impl<'a> BufferChunks<'a> {
hint_depth: 0, hint_depth: 0,
unnecessary_depth: 0, unnecessary_depth: 0,
highlights, highlights,
interactions,
} }
} }
@ -2767,22 +2861,10 @@ impl<'a> BufferChunks<'a> {
self.range.start = offset; self.range.start = offset;
self.chunks.seek(self.range.start); self.chunks.seek(self.range.start);
if let Some(highlights) = self.highlights.as_mut() { if let Some(highlights) = self.highlights.as_mut() {
highlights highlights.seek(offset, self.range.clone());
.stack }
.retain(|(end_offset, _)| *end_offset > offset); if let Some(interactions) = self.interactions.as_mut() {
if let Some(capture) = &highlights.next_capture { interactions.seek(offset, self.range.clone());
if offset >= capture.node.start_byte() {
let next_capture_end = capture.node.end_byte();
if offset < next_capture_end {
highlights.stack.push((
next_capture_end,
highlights.highlight_maps[capture.grammar_index].get(capture.index),
));
}
highlights.next_capture.take();
}
}
highlights.captures.set_byte_range(self.range.clone());
} }
} }
@ -2840,31 +2922,10 @@ impl<'a> Iterator for BufferChunks<'a> {
let mut next_diagnostic_endpoint = usize::MAX; let mut next_diagnostic_endpoint = usize::MAX;
if let Some(highlights) = self.highlights.as_mut() { if let Some(highlights) = self.highlights.as_mut() {
while let Some((parent_capture_end, _)) = highlights.stack.last() { highlights.next(self.range.start, &mut next_capture_start);
if *parent_capture_end <= self.range.start { }
highlights.stack.pop(); if let Some(interactions) = self.interactions.as_mut() {
} else { interactions.next(self.range.start, &mut next_capture_start);
break;
}
}
if highlights.next_capture.is_none() {
highlights.next_capture = highlights.captures.next();
}
while let Some(capture) = highlights.next_capture.as_ref() {
if self.range.start < capture.node.start_byte() {
next_capture_start = capture.node.start_byte();
break;
} else {
let highlight_id =
highlights.highlight_maps[capture.grammar_index].get(capture.index);
highlights
.stack
.push((capture.node.end_byte(), highlight_id));
highlights.next_capture = highlights.captures.next();
}
}
} }
while let Some(endpoint) = self.diagnostic_endpoints.peek().copied() { while let Some(endpoint) = self.diagnostic_endpoints.peek().copied() {
@ -2884,10 +2945,11 @@ impl<'a> Iterator for BufferChunks<'a> {
.min(next_diagnostic_endpoint); .min(next_diagnostic_endpoint);
let mut highlight_id = None; let mut highlight_id = None;
if let Some(highlights) = self.highlights.as_ref() { if let Some(highlights) = self.highlights.as_ref() {
if let Some((parent_capture_end, parent_highlight_id)) = highlights.stack.last() { highlights.select_current_id(&mut chunk_end, &mut highlight_id);
chunk_end = chunk_end.min(*parent_capture_end); }
highlight_id = Some(*parent_highlight_id); let mut interaction_id = None;
} if let Some(interactions) = self.interactions.as_ref() {
interactions.select_current_id(&mut chunk_end, &mut interaction_id);
} }
let slice = let slice =
@ -2900,6 +2962,7 @@ impl<'a> Iterator for BufferChunks<'a> {
Some(Chunk { Some(Chunk {
text: slice, text: slice,
syntax_highlight_id: highlight_id, syntax_highlight_id: highlight_id,
interaction_id,
diagnostic_severity: self.current_diagnostic_severity(), diagnostic_severity: self.current_diagnostic_severity(),
is_unnecessary: self.current_code_is_unnecessary(), is_unnecessary: self.current_code_is_unnecessary(),
..Default::default() ..Default::default()

View file

@ -2,12 +2,25 @@ use gpui::fonts::HighlightStyle;
use std::sync::Arc; use std::sync::Arc;
use theme::SyntaxTheme; use theme::SyntaxTheme;
use crate::QueryFeatureMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct HighlightMap(Arc<[HighlightId]>); pub struct HighlightMap(Arc<[HighlightId]>);
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HighlightId(pub u32); pub struct HighlightId(pub u32);
impl QueryFeatureMap for HighlightMap {
type Id = HighlightId;
fn get(&self, capture_id: u32) -> HighlightId {
self.0
.get(capture_id as usize)
.copied()
.unwrap_or(DEFAULT_SYNTAX_HIGHLIGHT_ID)
}
}
const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX); const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
impl HighlightMap { impl HighlightMap {
@ -41,13 +54,6 @@ impl HighlightMap {
.collect(), .collect(),
) )
} }
pub fn get(&self, capture_id: u32) -> HighlightId {
self.0
.get(capture_id as usize)
.copied()
.unwrap_or(DEFAULT_SYNTAX_HIGHLIGHT_ID)
}
} }
impl HighlightId { impl HighlightId {

View file

@ -0,0 +1,59 @@
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct InteractionMap(Arc<[Option<InteractionType>]>);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InteractionId {
event_type: InteractionType,
capture: u32,
}
const DEFAULT_INTERACTION_ID: InteractionId = InteractionId {
event_type: InteractionType::Click,
capture: u32::MAX,
};
impl super::buffer::QueryFeatureMap for InteractionMap {
type Id = InteractionId;
fn get(&self, capture_id: u32) -> Self::Id {
match self.0.get(capture_id as usize) {
Some(Some(event)) => InteractionId {
event_type: *event,
capture: capture_id,
},
_ => DEFAULT_INTERACTION_ID,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum InteractionType {
Click,
}
impl InteractionMap {
pub fn new(capture_names: &[String]) -> Self {
InteractionMap(
capture_names
.iter()
.map(|capture_name| {
let mut capture_parts = capture_name.split(".").peekable();
if let Some(str) = capture_parts.next() {
if str == "click" && capture_parts.peek().is_some() {
return Some(InteractionType::Click);
}
}
None
})
.collect(),
)
}
}
impl Default for InteractionMap {
fn default() -> Self {
Self(Arc::new([]))
}
}

View file

@ -1,6 +1,7 @@
mod buffer; mod buffer;
mod diagnostic_set; mod diagnostic_set;
mod highlight_map; mod highlight_map;
mod interaction_map;
pub mod language_settings; pub mod language_settings;
mod outline; mod outline;
pub mod proto; pub mod proto;
@ -18,7 +19,7 @@ use futures::{
FutureExt, TryFutureExt as _, FutureExt, TryFutureExt as _,
}; };
use gpui::{executor::Background, AppContext, AsyncAppContext, Task}; use gpui::{executor::Background, AppContext, AsyncAppContext, Task};
pub use highlight_map::HighlightMap; use interaction_map::InteractionMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lsp::{CodeActionKind, LanguageServerBinary}; use lsp::{CodeActionKind, LanguageServerBinary};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
@ -54,6 +55,8 @@ use futures::channel::mpsc;
pub use buffer::Operation; pub use buffer::Operation;
pub use buffer::*; pub use buffer::*;
pub use diagnostic_set::DiagnosticEntry; pub use diagnostic_set::DiagnosticEntry;
pub use highlight_map::HighlightMap;
pub use interaction_map::InteractionId;
pub use lsp::LanguageServerId; pub use lsp::LanguageServerId;
pub use outline::{Outline, OutlineItem}; pub use outline::{Outline, OutlineItem};
pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo}; pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo};
@ -315,6 +318,12 @@ pub trait LspAdapter: 'static + Send + Sync {
} }
} }
pub trait InteractionProvider: 'static + Send + Sync {
fn on_click(&self, _capture: &str, _text: &str) -> Option<String> {
None
}
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct CodeLabel { pub struct CodeLabel {
pub text: String, pub text: String,
@ -358,6 +367,7 @@ pub struct LanguageQueries {
pub embedding: Option<Cow<'static, str>>, pub embedding: Option<Cow<'static, str>>,
pub injections: Option<Cow<'static, str>>, pub injections: Option<Cow<'static, str>>,
pub overrides: Option<Cow<'static, str>>, pub overrides: Option<Cow<'static, str>>,
pub interactions: Option<Cow<'static, str>>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -488,6 +498,7 @@ pub struct Language {
pub(crate) config: LanguageConfig, pub(crate) config: LanguageConfig,
pub(crate) grammar: Option<Arc<Grammar>>, pub(crate) grammar: Option<Arc<Grammar>>,
pub(crate) adapters: Vec<Arc<CachedLspAdapter>>, pub(crate) adapters: Vec<Arc<CachedLspAdapter>>,
pub(crate) interaction_provider: Option<Arc<dyn InteractionProvider>>,
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
fake_adapter: Option<( fake_adapter: Option<(
@ -501,6 +512,7 @@ pub struct Grammar {
pub ts_language: tree_sitter::Language, pub ts_language: tree_sitter::Language,
pub(crate) error_query: Query, pub(crate) error_query: Query,
pub(crate) highlights_query: Option<Query>, pub(crate) highlights_query: Option<Query>,
pub(crate) interactions_query: Option<Query>,
pub(crate) brackets_config: Option<BracketConfig>, pub(crate) brackets_config: Option<BracketConfig>,
pub(crate) indents_config: Option<IndentConfig>, pub(crate) indents_config: Option<IndentConfig>,
pub outline_config: Option<OutlineConfig>, pub outline_config: Option<OutlineConfig>,
@ -508,6 +520,7 @@ pub struct Grammar {
pub(crate) injection_config: Option<InjectionConfig>, pub(crate) injection_config: Option<InjectionConfig>,
pub(crate) override_config: Option<OverrideConfig>, pub(crate) override_config: Option<OverrideConfig>,
pub(crate) highlight_map: Mutex<HighlightMap>, pub(crate) highlight_map: Mutex<HighlightMap>,
pub(crate) interaction_map: Mutex<InteractionMap>,
} }
struct IndentConfig { struct IndentConfig {
@ -578,6 +591,7 @@ struct AvailableLanguage {
config: LanguageConfig, config: LanguageConfig,
grammar: tree_sitter::Language, grammar: tree_sitter::Language,
lsp_adapters: Vec<Arc<dyn LspAdapter>>, lsp_adapters: Vec<Arc<dyn LspAdapter>>,
interaction_provider: Option<Arc<dyn InteractionProvider>>,
get_queries: fn(&str) -> LanguageQueries, get_queries: fn(&str) -> LanguageQueries,
loaded: bool, loaded: bool,
} }
@ -660,6 +674,7 @@ impl LanguageRegistry {
config: LanguageConfig, config: LanguageConfig,
grammar: tree_sitter::Language, grammar: tree_sitter::Language,
lsp_adapters: Vec<Arc<dyn LspAdapter>>, lsp_adapters: Vec<Arc<dyn LspAdapter>>,
interaction_provider: Option<Arc<dyn InteractionProvider>>,
get_queries: fn(&str) -> LanguageQueries, get_queries: fn(&str) -> LanguageQueries,
) { ) {
let state = &mut *self.state.write(); let state = &mut *self.state.write();
@ -670,6 +685,7 @@ impl LanguageRegistry {
grammar, grammar,
lsp_adapters, lsp_adapters,
get_queries, get_queries,
interaction_provider,
loaded: false, loaded: false,
}); });
} }
@ -832,6 +848,7 @@ impl LanguageRegistry {
let queries = (language.get_queries)(&language.path); let queries = (language.get_queries)(&language.path);
let language = let language =
Language::new(language.config, Some(language.grammar)) Language::new(language.config, Some(language.grammar))
.with_interaction_provider(language.interaction_provider)
.with_lsp_adapters(language.lsp_adapters) .with_lsp_adapters(language.lsp_adapters)
.await; .await;
let name = language.name(); let name = language.name();
@ -1170,12 +1187,15 @@ impl Language {
indents_config: None, indents_config: None,
injection_config: None, injection_config: None,
override_config: None, override_config: None,
interactions_query: None,
error_query: Query::new(ts_language, "(ERROR) @error").unwrap(), error_query: Query::new(ts_language, "(ERROR) @error").unwrap(),
ts_language, ts_language,
highlight_map: Default::default(), highlight_map: Default::default(),
interaction_map: Default::default(),
}) })
}), }),
adapters: Vec::new(), adapters: Vec::new(),
interaction_provider: None,
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
fake_adapter: None, fake_adapter: None,
@ -1226,6 +1246,19 @@ impl Language {
.with_override_query(query.as_ref()) .with_override_query(query.as_ref())
.context("Error loading override query")?; .context("Error loading override query")?;
} }
if let Some(query) = queries.interactions {
self = self
.with_interaction_query(query.as_ref())
.context("Error loading interaction query")?;
}
Ok(self)
}
pub fn with_interaction_query(mut self, source: &str) -> Result<Self> {
let grammar = self.grammar_mut();
let query = Query::new(grammar.ts_language, source)?;
*grammar.interaction_map.lock() = InteractionMap::new(query.capture_names());
grammar.interactions_query = Some(query);
Ok(self) Ok(self)
} }
@ -1457,6 +1490,15 @@ impl Language {
self self
} }
pub fn with_interaction_provider(
mut self,
interaction_provider: Option<Arc<dyn InteractionProvider>>,
) -> Self {
self.interaction_provider = interaction_provider;
self
}
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub async fn set_fake_lsp_adapter( pub async fn set_fake_lsp_adapter(
&mut self, &mut self,
@ -1534,7 +1576,9 @@ impl Language {
}); });
let highlight_maps = vec![grammar.highlight_map()]; let highlight_maps = vec![grammar.highlight_map()];
let mut offset = 0; let mut offset = 0;
for chunk in BufferChunks::new(text, range, Some((captures, highlight_maps)), vec![]) { for chunk in
BufferChunks::new(text, range, Some((captures, highlight_maps)), None, vec![])
{
let end_offset = offset + chunk.text.len(); let end_offset = offset + chunk.text.len();
if let Some(highlight_id) = chunk.syntax_highlight_id { if let Some(highlight_id) = chunk.syntax_highlight_id {
if !highlight_id.is_default() { if !highlight_id.is_default() {
@ -1680,6 +1724,10 @@ impl Grammar {
self.highlight_map.lock().clone() self.highlight_map.lock().clone()
} }
pub fn interaction_map(&self) -> InteractionMap {
self.interaction_map.lock().clone()
}
pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> { pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {
let capture_id = self let capture_id = self
.highlights_query .highlights_query
@ -1824,6 +1872,7 @@ mod tests {
}, },
tree_sitter_typescript::language_tsx(), tree_sitter_typescript::language_tsx(),
vec![], vec![],
None,
|_| Default::default(), |_| Default::default(),
); );
@ -1860,6 +1909,7 @@ mod tests {
}, },
tree_sitter_json::language(), tree_sitter_json::language(),
vec![], vec![],
None,
|_| Default::default(), |_| Default::default(),
); );
languages.register( languages.register(
@ -1871,6 +1921,7 @@ mod tests {
}, },
tree_sitter_rust::language(), tree_sitter_rust::language(),
vec![], vec![],
None,
|_| Default::default(), |_| Default::default(),
); );
assert_eq!( assert_eq!(

View file

@ -2601,6 +2601,7 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
}, },
tree_sitter_rust::language(), tree_sitter_rust::language(),
vec![], vec![],
None,
|_| Default::default(), |_| Default::default(),
); );

View file

@ -13,6 +13,7 @@ mod json;
#[cfg(feature = "plugin_runtime")] #[cfg(feature = "plugin_runtime")]
mod language_plugin; mod language_plugin;
mod lua; mod lua;
mod markdown;
mod php; mod php;
mod python; mod python;
mod ruby; mod ruby;
@ -36,36 +37,48 @@ mod yaml;
struct LanguageDir; struct LanguageDir;
pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) { pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
let language = |name, grammar, adapters| { let language = |name, grammar, adapters, interactions| {
languages.register(name, load_config(name), grammar, adapters, load_queries) languages.register(
name,
load_config(name),
grammar,
adapters,
interactions,
load_queries,
)
}; };
language("bash", tree_sitter_bash::language(), vec![]); language("bash", tree_sitter_bash::language(), vec![], None);
language( language(
"c", "c",
tree_sitter_c::language(), tree_sitter_c::language(),
vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>], vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>],
None,
); );
language( language(
"cpp", "cpp",
tree_sitter_cpp::language(), tree_sitter_cpp::language(),
vec![Arc::new(c::CLspAdapter)], vec![Arc::new(c::CLspAdapter)],
None,
); );
language("css", tree_sitter_css::language(), vec![]); language("css", tree_sitter_css::language(), vec![], None);
language( language(
"elixir", "elixir",
tree_sitter_elixir::language(), tree_sitter_elixir::language(),
vec![Arc::new(elixir::ElixirLspAdapter)], vec![Arc::new(elixir::ElixirLspAdapter)],
None,
); );
language( language(
"go", "go",
tree_sitter_go::language(), tree_sitter_go::language(),
vec![Arc::new(go::GoLspAdapter)], vec![Arc::new(go::GoLspAdapter)],
None,
); );
language( language(
"heex", "heex",
tree_sitter_heex::language(), tree_sitter_heex::language(),
vec![Arc::new(elixir::ElixirLspAdapter)], vec![Arc::new(elixir::ElixirLspAdapter)],
None,
); );
language( language(
"json", "json",
@ -74,21 +87,29 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
node_runtime.clone(), node_runtime.clone(),
languages.clone(), languages.clone(),
))], ))],
None,
);
language(
"markdown",
tree_sitter_markdown::language(),
vec![],
Some(Arc::new(MarkdownInteractions)),
); );
language("markdown", tree_sitter_markdown::language(), vec![]);
language( language(
"python", "python",
tree_sitter_python::language(), tree_sitter_python::language(),
vec![Arc::new(python::PythonLspAdapter::new( vec![Arc::new(python::PythonLspAdapter::new(
node_runtime.clone(), node_runtime.clone(),
))], ))],
None,
); );
language( language(
"rust", "rust",
tree_sitter_rust::language(), tree_sitter_rust::language(),
vec![Arc::new(rust::RustLspAdapter)], vec![Arc::new(rust::RustLspAdapter)],
None,
); );
language("toml", tree_sitter_toml::language(), vec![]); language("toml", tree_sitter_toml::language(), vec![], None);
language( language(
"tsx", "tsx",
tree_sitter_typescript::language_tsx(), tree_sitter_typescript::language_tsx(),
@ -96,6 +117,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
], ],
None,
); );
language( language(
"typescript", "typescript",
@ -104,6 +126,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
], ],
None,
); );
language( language(
"javascript", "javascript",
@ -112,33 +135,39 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
], ],
None,
); );
language( language(
"html", "html",
tree_sitter_html::language(), tree_sitter_html::language(),
vec![Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))], vec![Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))],
None,
); );
language( language(
"ruby", "ruby",
tree_sitter_ruby::language(), tree_sitter_ruby::language(),
vec![Arc::new(ruby::RubyLanguageServer)], vec![Arc::new(ruby::RubyLanguageServer)],
None,
); );
language( language(
"erb", "erb",
tree_sitter_embedded_template::language(), tree_sitter_embedded_template::language(),
vec![Arc::new(ruby::RubyLanguageServer)], vec![Arc::new(ruby::RubyLanguageServer)],
None,
); );
language("scheme", tree_sitter_scheme::language(), vec![]); language("scheme", tree_sitter_scheme::language(), vec![], None);
language("racket", tree_sitter_racket::language(), vec![]); language("racket", tree_sitter_racket::language(), vec![], None);
language( language(
"lua", "lua",
tree_sitter_lua::language(), tree_sitter_lua::language(),
vec![Arc::new(lua::LuaLspAdapter)], vec![Arc::new(lua::LuaLspAdapter)],
None,
); );
language( language(
"yaml", "yaml",
tree_sitter_yaml::language(), tree_sitter_yaml::language(),
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))], vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
None,
); );
language( language(
"svelte", "svelte",
@ -146,16 +175,18 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
vec![Arc::new(svelte::SvelteLspAdapter::new( vec![Arc::new(svelte::SvelteLspAdapter::new(
node_runtime.clone(), node_runtime.clone(),
))], ))],
None,
); );
language( language(
"php", "php",
tree_sitter_php::language(), tree_sitter_php::language(),
vec![Arc::new(php::IntelephenseLspAdapter::new(node_runtime))], vec![Arc::new(php::IntelephenseLspAdapter::new(node_runtime))],
None,
); );
language("elm", tree_sitter_elm::language(), vec![]); language("elm", tree_sitter_elm::language(), vec![], None);
language("glsl", tree_sitter_glsl::language(), vec![]); language("glsl", tree_sitter_glsl::language(), vec![], None);
language("nix", tree_sitter_nix::language(), vec![]); language("nix", tree_sitter_nix::language(), vec![], None);
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
@ -192,6 +223,7 @@ fn load_queries(name: &str) -> LanguageQueries {
embedding: load_query(name, "/embedding"), embedding: load_query(name, "/embedding"),
injections: load_query(name, "/injections"), injections: load_query(name, "/injections"),
overrides: load_query(name, "/overrides"), overrides: load_query(name, "/overrides"),
interactions: load_query(name, "/interactions"),
} }
} }

View file

@ -0,0 +1,15 @@
use language::InteractionProvider;
pub struct MarkdownInteractions;
impl InteractionProvider for MarkdownInteractions {
fn on_click(&self, text: &str) -> Option<String> {
if text == "[ ]" {
return Some("[x]".to_string());
} else if text == "[x]" {
return Some("[ ]".to_string());
} else {
return None;
}
}
}

View file

@ -0,0 +1,5 @@
(
(list_item (paragraph (shortcut_link) @click.toggle_selected))
(#matches? @click.toggle_selected "^([ ]|[x])")
)