mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-23 18:32:17 +00:00
WIP: Add interactions query language feature
Sketch in interaction provider API Wire interaction data through buffer chunk APIs
This commit is contained in:
parent
166585a2a8
commit
ecf8533d28
9 changed files with 313 additions and 72 deletions
|
@ -38,8 +38,8 @@ use gpui::{
|
|||
use itertools::Itertools;
|
||||
use json::json;
|
||||
use language::{
|
||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16,
|
||||
Selection,
|
||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, InteractionId,
|
||||
OffsetUtf16, Selection,
|
||||
};
|
||||
use project::{
|
||||
project_settings::{GitGutterSetting, ProjectSettings},
|
||||
|
@ -1659,6 +1659,7 @@ impl EditorElement {
|
|||
chunk: chunk.text,
|
||||
style: highlight_style,
|
||||
is_tab: chunk.is_tab,
|
||||
interaction: chunk.interaction_id,
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1900,6 +1901,7 @@ impl EditorElement {
|
|||
struct HighlightedChunk<'a> {
|
||||
chunk: &'a str,
|
||||
style: Option<HighlightStyle>,
|
||||
interaction: Option<InteractionId>,
|
||||
is_tab: bool,
|
||||
}
|
||||
|
||||
|
@ -1930,6 +1932,7 @@ impl LineWithInvisibles {
|
|||
for highlighted_chunk in chunks.chain([HighlightedChunk {
|
||||
chunk: "\n",
|
||||
style: None,
|
||||
interaction: None,
|
||||
is_tab: false,
|
||||
}]) {
|
||||
for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() {
|
||||
|
@ -1960,6 +1963,12 @@ impl LineWithInvisibles {
|
|||
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 {
|
||||
let mut chunk_len = max_line_len - line.len();
|
||||
while !line_chunk.is_char_boundary(chunk_len) {
|
||||
|
|
|
@ -5,6 +5,7 @@ pub use crate::{
|
|||
};
|
||||
use crate::{
|
||||
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
|
||||
interaction_map::{InteractionId, InteractionMap},
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
outline::OutlineItem,
|
||||
syntax_map::{
|
||||
|
@ -285,11 +286,74 @@ struct IndentSuggestion {
|
|||
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>,
|
||||
next_capture: Option<SyntaxMapCapture<'a>>,
|
||||
stack: Vec<(usize, HighlightId)>,
|
||||
highlight_maps: Vec<HighlightMap>,
|
||||
maps: Vec<T>,
|
||||
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> {
|
||||
|
@ -302,12 +366,14 @@ pub struct BufferChunks<'a> {
|
|||
hint_depth: usize,
|
||||
unnecessary_depth: usize,
|
||||
highlights: Option<BufferChunkHighlights<'a>>,
|
||||
interactions: Option<BufferChunkInteractions<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Chunk<'a> {
|
||||
pub text: &'a str,
|
||||
pub syntax_highlight_id: Option<HighlightId>,
|
||||
pub interaction_id: Option<InteractionId>,
|
||||
pub highlight_style: Option<HighlightStyle>,
|
||||
pub diagnostic_severity: Option<DiagnosticSeverity>,
|
||||
pub is_unnecessary: bool,
|
||||
|
@ -2089,17 +2155,27 @@ impl BufferSnapshot {
|
|||
let range = range.start.to_offset(self)..range.end.to_offset(self);
|
||||
|
||||
let mut syntax = None;
|
||||
let mut interactions = None;
|
||||
let mut diagnostic_endpoints = Vec::new();
|
||||
if language_aware {
|
||||
let captures = self.syntax.captures(range.clone(), &self.text, |grammar| {
|
||||
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
|
||||
.grammars()
|
||||
.into_iter()
|
||||
.map(|grammar| grammar.highlight_map())
|
||||
.collect();
|
||||
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) {
|
||||
diagnostic_endpoints.push(DiagnosticEndpoint {
|
||||
offset: entry.range.start,
|
||||
|
@ -2118,7 +2194,13 @@ impl BufferSnapshot {
|
|||
.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)) {
|
||||
|
@ -2735,6 +2817,7 @@ impl<'a> BufferChunks<'a> {
|
|||
text: &'a Rope,
|
||||
range: Range<usize>,
|
||||
syntax: Option<(SyntaxMapCaptures<'a>, Vec<HighlightMap>)>,
|
||||
interactions_syntax: Option<(SyntaxMapCaptures<'a>, Vec<InteractionMap>)>,
|
||||
diagnostic_endpoints: Vec<DiagnosticEndpoint>,
|
||||
) -> Self {
|
||||
let mut highlights = None;
|
||||
|
@ -2743,7 +2826,17 @@ impl<'a> BufferChunks<'a> {
|
|||
captures,
|
||||
next_capture: None,
|
||||
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,
|
||||
unnecessary_depth: 0,
|
||||
highlights,
|
||||
interactions,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2767,22 +2861,10 @@ impl<'a> BufferChunks<'a> {
|
|||
self.range.start = offset;
|
||||
self.chunks.seek(self.range.start);
|
||||
if let Some(highlights) = self.highlights.as_mut() {
|
||||
highlights
|
||||
.stack
|
||||
.retain(|(end_offset, _)| *end_offset > offset);
|
||||
if let Some(capture) = &highlights.next_capture {
|
||||
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());
|
||||
highlights.seek(offset, self.range.clone());
|
||||
}
|
||||
if let Some(interactions) = self.interactions.as_mut() {
|
||||
interactions.seek(offset, self.range.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2840,31 +2922,10 @@ impl<'a> Iterator for BufferChunks<'a> {
|
|||
let mut next_diagnostic_endpoint = usize::MAX;
|
||||
|
||||
if let Some(highlights) = self.highlights.as_mut() {
|
||||
while let Some((parent_capture_end, _)) = highlights.stack.last() {
|
||||
if *parent_capture_end <= self.range.start {
|
||||
highlights.stack.pop();
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
highlights.next(self.range.start, &mut next_capture_start);
|
||||
}
|
||||
if let Some(interactions) = self.interactions.as_mut() {
|
||||
interactions.next(self.range.start, &mut next_capture_start);
|
||||
}
|
||||
|
||||
while let Some(endpoint) = self.diagnostic_endpoints.peek().copied() {
|
||||
|
@ -2884,10 +2945,11 @@ impl<'a> Iterator for BufferChunks<'a> {
|
|||
.min(next_diagnostic_endpoint);
|
||||
let mut highlight_id = None;
|
||||
if let Some(highlights) = self.highlights.as_ref() {
|
||||
if let Some((parent_capture_end, parent_highlight_id)) = highlights.stack.last() {
|
||||
chunk_end = chunk_end.min(*parent_capture_end);
|
||||
highlight_id = Some(*parent_highlight_id);
|
||||
}
|
||||
highlights.select_current_id(&mut chunk_end, &mut 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 =
|
||||
|
@ -2900,6 +2962,7 @@ impl<'a> Iterator for BufferChunks<'a> {
|
|||
Some(Chunk {
|
||||
text: slice,
|
||||
syntax_highlight_id: highlight_id,
|
||||
interaction_id,
|
||||
diagnostic_severity: self.current_diagnostic_severity(),
|
||||
is_unnecessary: self.current_code_is_unnecessary(),
|
||||
..Default::default()
|
||||
|
|
|
@ -2,12 +2,25 @@ use gpui::fonts::HighlightStyle;
|
|||
use std::sync::Arc;
|
||||
use theme::SyntaxTheme;
|
||||
|
||||
use crate::QueryFeatureMap;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HighlightMap(Arc<[HighlightId]>);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
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);
|
||||
|
||||
impl HighlightMap {
|
||||
|
@ -41,13 +54,6 @@ impl HighlightMap {
|
|||
.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 {
|
||||
|
|
59
crates/language/src/interaction_map.rs
Normal file
59
crates/language/src/interaction_map.rs
Normal 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([]))
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
mod buffer;
|
||||
mod diagnostic_set;
|
||||
mod highlight_map;
|
||||
mod interaction_map;
|
||||
pub mod language_settings;
|
||||
mod outline;
|
||||
pub mod proto;
|
||||
|
@ -18,7 +19,7 @@ use futures::{
|
|||
FutureExt, TryFutureExt as _,
|
||||
};
|
||||
use gpui::{executor::Background, AppContext, AsyncAppContext, Task};
|
||||
pub use highlight_map::HighlightMap;
|
||||
use interaction_map::InteractionMap;
|
||||
use lazy_static::lazy_static;
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
@ -54,6 +55,8 @@ use futures::channel::mpsc;
|
|||
pub use buffer::Operation;
|
||||
pub use buffer::*;
|
||||
pub use diagnostic_set::DiagnosticEntry;
|
||||
pub use highlight_map::HighlightMap;
|
||||
pub use interaction_map::InteractionId;
|
||||
pub use lsp::LanguageServerId;
|
||||
pub use outline::{Outline, OutlineItem};
|
||||
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)]
|
||||
pub struct CodeLabel {
|
||||
pub text: String,
|
||||
|
@ -358,6 +367,7 @@ pub struct LanguageQueries {
|
|||
pub embedding: Option<Cow<'static, str>>,
|
||||
pub injections: Option<Cow<'static, str>>,
|
||||
pub overrides: Option<Cow<'static, str>>,
|
||||
pub interactions: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -488,6 +498,7 @@ pub struct Language {
|
|||
pub(crate) config: LanguageConfig,
|
||||
pub(crate) grammar: Option<Arc<Grammar>>,
|
||||
pub(crate) adapters: Vec<Arc<CachedLspAdapter>>,
|
||||
pub(crate) interaction_provider: Option<Arc<dyn InteractionProvider>>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fake_adapter: Option<(
|
||||
|
@ -501,6 +512,7 @@ pub struct Grammar {
|
|||
pub ts_language: tree_sitter::Language,
|
||||
pub(crate) error_query: Query,
|
||||
pub(crate) highlights_query: Option<Query>,
|
||||
pub(crate) interactions_query: Option<Query>,
|
||||
pub(crate) brackets_config: Option<BracketConfig>,
|
||||
pub(crate) indents_config: Option<IndentConfig>,
|
||||
pub outline_config: Option<OutlineConfig>,
|
||||
|
@ -508,6 +520,7 @@ pub struct Grammar {
|
|||
pub(crate) injection_config: Option<InjectionConfig>,
|
||||
pub(crate) override_config: Option<OverrideConfig>,
|
||||
pub(crate) highlight_map: Mutex<HighlightMap>,
|
||||
pub(crate) interaction_map: Mutex<InteractionMap>,
|
||||
}
|
||||
|
||||
struct IndentConfig {
|
||||
|
@ -578,6 +591,7 @@ struct AvailableLanguage {
|
|||
config: LanguageConfig,
|
||||
grammar: tree_sitter::Language,
|
||||
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
|
||||
interaction_provider: Option<Arc<dyn InteractionProvider>>,
|
||||
get_queries: fn(&str) -> LanguageQueries,
|
||||
loaded: bool,
|
||||
}
|
||||
|
@ -660,6 +674,7 @@ impl LanguageRegistry {
|
|||
config: LanguageConfig,
|
||||
grammar: tree_sitter::Language,
|
||||
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
|
||||
interaction_provider: Option<Arc<dyn InteractionProvider>>,
|
||||
get_queries: fn(&str) -> LanguageQueries,
|
||||
) {
|
||||
let state = &mut *self.state.write();
|
||||
|
@ -670,6 +685,7 @@ impl LanguageRegistry {
|
|||
grammar,
|
||||
lsp_adapters,
|
||||
get_queries,
|
||||
interaction_provider,
|
||||
loaded: false,
|
||||
});
|
||||
}
|
||||
|
@ -832,6 +848,7 @@ impl LanguageRegistry {
|
|||
let queries = (language.get_queries)(&language.path);
|
||||
let language =
|
||||
Language::new(language.config, Some(language.grammar))
|
||||
.with_interaction_provider(language.interaction_provider)
|
||||
.with_lsp_adapters(language.lsp_adapters)
|
||||
.await;
|
||||
let name = language.name();
|
||||
|
@ -1170,12 +1187,15 @@ impl Language {
|
|||
indents_config: None,
|
||||
injection_config: None,
|
||||
override_config: None,
|
||||
interactions_query: None,
|
||||
error_query: Query::new(ts_language, "(ERROR) @error").unwrap(),
|
||||
ts_language,
|
||||
highlight_map: Default::default(),
|
||||
interaction_map: Default::default(),
|
||||
})
|
||||
}),
|
||||
adapters: Vec::new(),
|
||||
interaction_provider: None,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fake_adapter: None,
|
||||
|
@ -1226,6 +1246,19 @@ impl Language {
|
|||
.with_override_query(query.as_ref())
|
||||
.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)
|
||||
}
|
||||
|
||||
|
@ -1457,6 +1490,15 @@ impl Language {
|
|||
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"))]
|
||||
pub async fn set_fake_lsp_adapter(
|
||||
&mut self,
|
||||
|
@ -1534,7 +1576,9 @@ impl Language {
|
|||
});
|
||||
let highlight_maps = vec![grammar.highlight_map()];
|
||||
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();
|
||||
if let Some(highlight_id) = chunk.syntax_highlight_id {
|
||||
if !highlight_id.is_default() {
|
||||
|
@ -1680,6 +1724,10 @@ impl Grammar {
|
|||
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> {
|
||||
let capture_id = self
|
||||
.highlights_query
|
||||
|
@ -1824,6 +1872,7 @@ mod tests {
|
|||
},
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
vec![],
|
||||
None,
|
||||
|_| Default::default(),
|
||||
);
|
||||
|
||||
|
@ -1860,6 +1909,7 @@ mod tests {
|
|||
},
|
||||
tree_sitter_json::language(),
|
||||
vec![],
|
||||
None,
|
||||
|_| Default::default(),
|
||||
);
|
||||
languages.register(
|
||||
|
@ -1871,6 +1921,7 @@ mod tests {
|
|||
},
|
||||
tree_sitter_rust::language(),
|
||||
vec![],
|
||||
None,
|
||||
|_| Default::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
|
@ -2601,6 +2601,7 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
|
|||
},
|
||||
tree_sitter_rust::language(),
|
||||
vec![],
|
||||
None,
|
||||
|_| Default::default(),
|
||||
);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ mod json;
|
|||
#[cfg(feature = "plugin_runtime")]
|
||||
mod language_plugin;
|
||||
mod lua;
|
||||
mod markdown;
|
||||
mod php;
|
||||
mod python;
|
||||
mod ruby;
|
||||
|
@ -36,36 +37,48 @@ mod yaml;
|
|||
struct LanguageDir;
|
||||
|
||||
pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
|
||||
let language = |name, grammar, adapters| {
|
||||
languages.register(name, load_config(name), grammar, adapters, load_queries)
|
||||
let language = |name, grammar, adapters, interactions| {
|
||||
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(
|
||||
"c",
|
||||
tree_sitter_c::language(),
|
||||
vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"cpp",
|
||||
tree_sitter_cpp::language(),
|
||||
vec![Arc::new(c::CLspAdapter)],
|
||||
None,
|
||||
);
|
||||
language("css", tree_sitter_css::language(), vec![]);
|
||||
language("css", tree_sitter_css::language(), vec![], None);
|
||||
language(
|
||||
"elixir",
|
||||
tree_sitter_elixir::language(),
|
||||
vec![Arc::new(elixir::ElixirLspAdapter)],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"go",
|
||||
tree_sitter_go::language(),
|
||||
vec![Arc::new(go::GoLspAdapter)],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"heex",
|
||||
tree_sitter_heex::language(),
|
||||
vec![Arc::new(elixir::ElixirLspAdapter)],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"json",
|
||||
|
@ -74,21 +87,29 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
|
|||
node_runtime.clone(),
|
||||
languages.clone(),
|
||||
))],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"markdown",
|
||||
tree_sitter_markdown::language(),
|
||||
vec![],
|
||||
Some(Arc::new(MarkdownInteractions)),
|
||||
);
|
||||
language("markdown", tree_sitter_markdown::language(), vec![]);
|
||||
language(
|
||||
"python",
|
||||
tree_sitter_python::language(),
|
||||
vec![Arc::new(python::PythonLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"rust",
|
||||
tree_sitter_rust::language(),
|
||||
vec![Arc::new(rust::RustLspAdapter)],
|
||||
None,
|
||||
);
|
||||
language("toml", tree_sitter_toml::language(), vec![]);
|
||||
language("toml", tree_sitter_toml::language(), vec![], None);
|
||||
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::EsLintLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"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::EsLintLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"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::EsLintLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"html",
|
||||
tree_sitter_html::language(),
|
||||
vec![Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"ruby",
|
||||
tree_sitter_ruby::language(),
|
||||
vec![Arc::new(ruby::RubyLanguageServer)],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"erb",
|
||||
tree_sitter_embedded_template::language(),
|
||||
vec![Arc::new(ruby::RubyLanguageServer)],
|
||||
None,
|
||||
);
|
||||
language("scheme", tree_sitter_scheme::language(), vec![]);
|
||||
language("racket", tree_sitter_racket::language(), vec![]);
|
||||
language("scheme", tree_sitter_scheme::language(), vec![], None);
|
||||
language("racket", tree_sitter_racket::language(), vec![], None);
|
||||
language(
|
||||
"lua",
|
||||
tree_sitter_lua::language(),
|
||||
vec![Arc::new(lua::LuaLspAdapter)],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"yaml",
|
||||
tree_sitter_yaml::language(),
|
||||
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"svelte",
|
||||
|
@ -146,16 +175,18 @@ pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
|
|||
vec![Arc::new(svelte::SvelteLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))],
|
||||
None,
|
||||
);
|
||||
language(
|
||||
"php",
|
||||
tree_sitter_php::language(),
|
||||
vec![Arc::new(php::IntelephenseLspAdapter::new(node_runtime))],
|
||||
None,
|
||||
);
|
||||
|
||||
language("elm", tree_sitter_elm::language(), vec![]);
|
||||
language("glsl", tree_sitter_glsl::language(), vec![]);
|
||||
language("nix", tree_sitter_nix::language(), vec![]);
|
||||
language("elm", tree_sitter_elm::language(), vec![], None);
|
||||
language("glsl", tree_sitter_glsl::language(), vec![], None);
|
||||
language("nix", tree_sitter_nix::language(), vec![], None);
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -192,6 +223,7 @@ fn load_queries(name: &str) -> LanguageQueries {
|
|||
embedding: load_query(name, "/embedding"),
|
||||
injections: load_query(name, "/injections"),
|
||||
overrides: load_query(name, "/overrides"),
|
||||
interactions: load_query(name, "/interactions"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
15
crates/zed/src/languages/markdown.rs
Normal file
15
crates/zed/src/languages/markdown.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
5
crates/zed/src/languages/markdown/interactions.scm
Normal file
5
crates/zed/src/languages/markdown/interactions.scm
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
(
|
||||
(list_item (paragraph (shortcut_link) @click.toggle_selected))
|
||||
(#matches? @click.toggle_selected "^([ ]|[x])")
|
||||
)
|
Loading…
Reference in a new issue