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 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) {

View file

@ -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()

View file

@ -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 {

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 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!(

View file

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

View file

@ -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"),
}
}

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])")
)