Detailed changes
@@ -1737,7 +1737,7 @@ impl Editor {
for (selection, autoclose_region) in
self.selections_with_autoclose_regions(selections, &snapshot)
{
- if let Some(language) = snapshot.language_config_at(selection.head()) {
+ if let Some(language) = snapshot.language_scope_at(selection.head()) {
// Determine if the inserted text matches the opening or closing
// bracket of any of this language's bracket pairs.
let mut bracket_pair = None;
@@ -1898,7 +1898,7 @@ impl Editor {
let end = selection.end;
let mut insert_extra_newline = false;
- if let Some(language) = buffer.language_config_at(start) {
+ if let Some(language) = buffer.language_scope_at(start) {
let leading_whitespace_len = buffer
.reversed_chars_at(start)
.take_while(|c| c.is_whitespace() && *c != '\n')
@@ -4535,7 +4535,7 @@ impl Editor {
for selection in &mut selections {
let start_column = snapshot.indent_size_for_line(selection.start.row).len;
let language = if let Some(language) =
- snapshot.language_config_at(Point::new(selection.start.row, start_column))
+ snapshot.language_scope_at(Point::new(selection.start.row, start_column))
{
language
} else {
@@ -10,9 +10,9 @@ use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
pub use language::Completion;
use language::{
char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
- DiagnosticEntry, IndentSize, Language, LanguageConfigYeet, OffsetRangeExt, OffsetUtf16,
- Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
- ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
+ DiagnosticEntry, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline,
+ OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _,
+ ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
};
use std::{
borrow::Cow,
@@ -2691,9 +2691,9 @@ impl MultiBufferSnapshot {
.and_then(|(buffer, offset)| buffer.language_at(offset))
}
- pub fn language_config_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageConfigYeet> {
+ pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageScope> {
self.point_to_buffer_offset(point)
- .and_then(|(buffer, offset)| buffer.language_config_at(offset))
+ .and_then(|(buffer, offset)| buffer.language_scope_at(offset))
}
pub fn is_dirty(&self) -> bool {
@@ -9,7 +9,7 @@ use crate::{
syntax_map::{
SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxSnapshot, ToTreeSitterPoint,
},
- CodeLabel, LanguageConfigYeet, Outline,
+ CodeLabel, LanguageScope, Outline,
};
use anyhow::{anyhow, Result};
use clock::ReplicaId;
@@ -2015,7 +2015,7 @@ impl BufferSnapshot {
.or(self.language.as_ref())
}
- pub fn language_config_at<D: ToOffset>(&self, position: D) -> Option<LanguageConfigYeet> {
+ pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
let offset = position.to_offset(self);
if let Some(layer_info) = self
@@ -2024,12 +2024,12 @@ impl BufferSnapshot {
.filter(|l| l.node.end_byte() > offset)
.last()
{
- Some(LanguageConfigYeet {
+ Some(LanguageScope {
language: layer_info.language.clone(),
override_id: layer_info.override_id(offset, &self.text),
})
} else {
- self.language.clone().map(|language| LanguageConfigYeet {
+ self.language.clone().map(|language| LanguageScope {
language,
override_id: None,
})
@@ -1369,6 +1369,89 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
});
}
+#[gpui::test]
+fn test_language_config_at(cx: &mut MutableAppContext) {
+ cx.set_global(Settings::test(cx));
+ cx.add_model(|cx| {
+ let language = Language::new(
+ LanguageConfig {
+ name: "JavaScript".into(),
+ line_comment: Some("// ".into()),
+ brackets: vec![
+ BracketPair {
+ start: "{".into(),
+ end: "}".into(),
+ close: true,
+ newline: false,
+ },
+ BracketPair {
+ start: "'".into(),
+ end: "'".into(),
+ close: true,
+ newline: false,
+ },
+ ],
+ overrides: [
+ (
+ "element".into(),
+ LanguageConfigOverride {
+ line_comment: Override::Remove { remove: true },
+ block_comment: Override::Set(("{/*".into(), "*/}".into())),
+ ..Default::default()
+ },
+ ),
+ (
+ "string".into(),
+ LanguageConfigOverride {
+ brackets: Override::Set(vec![BracketPair {
+ start: "{".into(),
+ end: "}".into(),
+ close: true,
+ newline: false,
+ }]),
+ ..Default::default()
+ },
+ ),
+ ]
+ .into_iter()
+ .collect(),
+ ..Default::default()
+ },
+ Some(tree_sitter_javascript::language()),
+ )
+ .with_override_query(
+ r#"
+ (jsx_element) @override.element
+ (string) @override.string
+ "#,
+ )
+ .unwrap();
+
+ let text = r#"a["b"] = <C d="e"></C>;"#;
+
+ let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
+ let snapshot = buffer.snapshot();
+
+ let config = snapshot.language_scope_at(0).unwrap();
+ assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
+ assert_eq!(config.brackets().len(), 2);
+
+ let string_config = snapshot.language_scope_at(3).unwrap();
+ assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
+ assert_eq!(string_config.brackets().len(), 1);
+
+ let element_config = snapshot.language_scope_at(10).unwrap();
+ assert_eq!(element_config.line_comment_prefix(), None);
+ assert_eq!(
+ element_config.block_comment_delimiters(),
+ Some((&"{/*".into(), &"*/}".into()))
+ );
+ assert_eq!(element_config.brackets().len(), 2);
+
+ buffer
+ });
+}
+
#[gpui::test]
fn test_serialization(cx: &mut gpui::MutableAppContext) {
let mut now = Instant::now();
@@ -22,10 +22,7 @@ use lazy_static::lazy_static;
use parking_lot::{Mutex, RwLock};
use postage::watch;
use regex::Regex;
-use serde::{
- de::{self},
- Deserialize, Deserializer,
-};
+use serde::{de, Deserialize, Deserializer};
use serde_json::Value;
use std::{
any::Any,
@@ -251,20 +248,22 @@ pub struct LanguageConfig {
}
#[derive(Clone)]
-pub struct LanguageConfigYeet {
+pub struct LanguageScope {
language: Arc<Language>,
override_id: Option<u32>,
}
-#[derive(Deserialize)]
+#[derive(Deserialize, Default, Debug)]
pub struct LanguageConfigOverride {
#[serde(default)]
pub line_comment: Override<Arc<str>>,
#[serde(default)]
pub block_comment: Override<(Arc<str>, Arc<str>)>,
+ #[serde(default)]
+ pub brackets: Override<Vec<BracketPair>>,
}
-#[derive(Deserialize)]
+#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub enum Override<T> {
Remove { remove: bool },
@@ -278,11 +277,11 @@ impl<T> Default for Override<T> {
}
impl<T> Override<T> {
- fn as_option<'a>(this: Option<&'a Self>, original: &'a Option<T>) -> Option<&'a T> {
+ fn as_option<'a>(this: Option<&'a Self>, original: Option<&'a T>) -> Option<&'a T> {
match this {
Some(Self::Set(value)) => Some(value),
Some(Self::Remove { remove: true }) => None,
- Some(Self::Remove { remove: false }) | None => original.as_ref(),
+ Some(Self::Remove { remove: false }) | None => original,
}
}
}
@@ -966,40 +965,39 @@ impl Language {
}
}
-impl LanguageConfigYeet {
+impl LanguageScope {
pub fn line_comment_prefix(&self) -> Option<&Arc<str>> {
Override::as_option(
- self.over_ride().map(|o| &o.line_comment),
- &self.language.config.line_comment,
+ self.config_override().map(|o| &o.line_comment),
+ self.language.config.line_comment.as_ref(),
)
}
pub fn block_comment_delimiters(&self) -> Option<(&Arc<str>, &Arc<str>)> {
Override::as_option(
- self.over_ride().map(|o| &o.block_comment),
- &self.language.config.block_comment,
+ self.config_override().map(|o| &o.block_comment),
+ self.language.config.block_comment.as_ref(),
)
.map(|e| (&e.0, &e.1))
}
pub fn brackets(&self) -> &[BracketPair] {
- &self.language.config.brackets
+ Override::as_option(
+ self.config_override().map(|o| &o.brackets),
+ Some(&self.language.config.brackets),
+ )
+ .map_or(&[], Vec::as_slice)
}
pub fn should_autoclose_before(&self, c: char) -> bool {
c.is_whitespace() || self.language.config.autoclose_before.contains(c)
}
- fn over_ride(&self) -> Option<&LanguageConfigOverride> {
- self.override_id.and_then(|id| {
- self.language
- .grammar
- .as_ref()?
- .override_config
- .as_ref()?
- .values
- .get(&id)
- })
+ fn config_override(&self) -> Option<&LanguageConfigOverride> {
+ let id = self.override_id?;
+ let grammar = self.language.grammar.as_ref()?;
+ let override_config = grammar.override_config.as_ref()?;
+ override_config.values.get(&id)
}
}
@@ -173,6 +173,11 @@ pub(crate) fn language(
.with_injection_query(query.as_ref())
.expect("failed to load injection query");
}
+ if let Some(query) = load_query(name, "/overrides") {
+ language = language
+ .with_override_query(query.as_ref())
+ .expect("failed to load override query");
+ }
if let Some(lsp_adapter) = lsp_adapter {
language = language.with_lsp_adapter(lsp_adapter)
}
@@ -1,2 +1,7 @@
-(jsx_element) @override.element
+[
+ (jsx_element)
+ (jsx_fragment)
+ (jsx_self_closing_element)
+ (jsx_expression)
+] @override.element
(string) @override.string