diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 00b87dbc34..5e14f5104f 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -600,6 +600,10 @@ impl Buffer { } } + pub fn is_parsing(&self) -> bool { + self.is_parsing + } + fn should_reparse(&self) -> bool { if let Some(syntax_tree) = self.syntax_tree.lock().as_ref() { !syntax_tree.parsed || syntax_tree.version != self.version @@ -3481,13 +3485,15 @@ mod tests { let text = "fn a() {}"; let buffer = Buffer::from_history(0, History::new(text.into()), None, rust_lang, ctx); - assert!(buffer.is_parsing); + assert!(buffer.is_parsing()); assert!(buffer.syntax_tree().is_none()); buffer }); // Wait for the initial text to parse - buffer.condition(&ctx, |buffer, _| !buffer.is_parsing).await; + buffer + .condition(&ctx, |buffer, _| !buffer.is_parsing()) + .await; assert_eq!( get_tree_sexp(&buffer, &ctx), concat!( @@ -3504,17 +3510,19 @@ mod tests { let offset = buf.text().find(")").unwrap(); buf.edit(vec![offset..offset], "b: C", Some(ctx)).unwrap(); - assert!(!buf.is_parsing); + assert!(!buf.is_parsing()); let offset = buf.text().find("}").unwrap(); buf.edit(vec![offset..offset], " d; ", Some(ctx)).unwrap(); - assert!(!buf.is_parsing); + assert!(!buf.is_parsing()); buf.end_transaction(None, Some(ctx)).unwrap(); assert_eq!(buf.text(), "fn a(b: C) { d; }"); - assert!(buf.is_parsing); + assert!(buf.is_parsing()); }); - buffer.condition(&ctx, |buffer, _| !buffer.is_parsing).await; + buffer + .condition(&ctx, |buffer, _| !buffer.is_parsing()) + .await; assert_eq!( get_tree_sexp(&buffer, &ctx), concat!( @@ -3532,21 +3540,23 @@ mod tests { let offset = buf.text().find(";").unwrap(); buf.edit(vec![offset..offset], ".e", Some(ctx)).unwrap(); assert_eq!(buf.text(), "fn a(b: C) { d.e; }"); - assert!(buf.is_parsing); + assert!(buf.is_parsing()); }); buffer.update(&mut ctx, |buf, ctx| { let offset = buf.text().find(";").unwrap(); buf.edit(vec![offset..offset], "(f)", Some(ctx)).unwrap(); assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }"); - assert!(buf.is_parsing); + assert!(buf.is_parsing()); }); buffer.update(&mut ctx, |buf, ctx| { let offset = buf.text().find("(f)").unwrap(); buf.edit(vec![offset..offset], "::", Some(ctx)).unwrap(); assert_eq!(buf.text(), "fn a(b: C) { d.e::(f); }"); - assert!(buf.is_parsing); + assert!(buf.is_parsing()); }); - buffer.condition(&ctx, |buffer, _| !buffer.is_parsing).await; + buffer + .condition(&ctx, |buffer, _| !buffer.is_parsing()) + .await; assert_eq!( get_tree_sexp(&buffer, &ctx), concat!( @@ -3563,9 +3573,11 @@ mod tests { buffer.update(&mut ctx, |buf, ctx| { buf.undo(Some(ctx)); assert_eq!(buf.text(), "fn a() {}"); - assert!(buf.is_parsing); + assert!(buf.is_parsing()); }); - buffer.condition(&ctx, |buffer, _| !buffer.is_parsing).await; + buffer + .condition(&ctx, |buffer, _| !buffer.is_parsing()) + .await; assert_eq!( get_tree_sexp(&buffer, &ctx), concat!( @@ -3578,9 +3590,11 @@ mod tests { buffer.update(&mut ctx, |buf, ctx| { buf.redo(Some(ctx)); assert_eq!(buf.text(), "fn a(b: C) { d.e::(f); }"); - assert!(buf.is_parsing); + assert!(buf.is_parsing()); }); - buffer.condition(&ctx, |buffer, _| !buffer.is_parsing).await; + buffer + .condition(&ctx, |buffer, _| !buffer.is_parsing()) + .await; assert_eq!( get_tree_sexp(&buffer, &ctx), concat!( diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 56cc06468f..54765ed0b9 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -445,7 +445,9 @@ pub fn collapse_tabs( #[cfg(test)] mod tests { use super::*; - use crate::test::*; + use crate::{language::Language, settings::Theme, test::*}; + use buffer::History; + use std::sync::Arc; #[gpui::test] fn test_chunks_at(app: &mut gpui::MutableAppContext) { @@ -486,6 +488,103 @@ mod tests { ); } + #[gpui::test] + async fn test_highlighted_chunks_at(mut app: gpui::TestAppContext) { + use unindent::Unindent as _; + + let grammar = tree_sitter_rust::language(); + let text = r#" + fn outer() {} + + mod module { + fn inner() {} + }"# + .unindent(); + let query = tree_sitter::Query::new( + grammar, + r#" + (mod_item name: (identifier) body: _ @mod.body) + (function_item name: (identifier) @fn.name)"#, + ) + .unwrap(); + let theme = Theme::parse( + r#" + [syntax] + "mod.body" = 0xff0000 + "fn.name" = 0x00ff00"#, + ) + .unwrap(); + let lang = Arc::new(Language { + name: "Test".to_string(), + grammar: grammar.clone(), + highlight_query: query, + path_suffixes: vec![".test".to_string()], + theme_mapping: Default::default(), + }); + lang.set_theme(&theme); + + let buffer = app.add_model(|ctx| { + Buffer::from_history(0, History::new(text.into()), None, Some(lang), ctx) + }); + buffer.condition(&app, |buf, _| !buf.is_parsing()).await; + + let mut map = app.read(|ctx| DisplayMap::new(buffer, 2, ctx)); + assert_eq!( + app.read(|ctx| highlighted_chunks(0, &map, &theme, ctx)), + vec![ + ("fn ".to_string(), None), + ("outer".to_string(), Some("fn.name")), + ("() {}\n\nmod module ".to_string(), None), + ("{\n fn ".to_string(), Some("mod.body")), + ("inner".to_string(), Some("fn.name")), + ("() {}\n}".to_string(), Some("mod.body")), + ] + ); + assert_eq!( + app.read(|ctx| highlighted_chunks(3, &map, &theme, ctx)), + vec![ + (" fn ".to_string(), Some("mod.body")), + ("inner".to_string(), Some("fn.name")), + ("() {}\n}".to_string(), Some("mod.body")), + ] + ); + + app.read(|ctx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], ctx)); + assert_eq!( + app.read(|ctx| highlighted_chunks(0, &map, &theme, ctx)), + vec![ + ("fn ".to_string(), None), + ("out".to_string(), Some("fn.name")), + ("…".to_string(), None), + (" fn ".to_string(), Some("mod.body")), + ("inner".to_string(), Some("fn.name")), + ("() {}\n}".to_string(), Some("mod.body")), + ] + ); + + fn highlighted_chunks<'a>( + row: u32, + map: &DisplayMap, + theme: &'a Theme, + ctx: &AppContext, + ) -> Vec<(String, Option<&'a str>)> { + let mut chunks: Vec<(String, Option<&str>)> = Vec::new(); + for (chunk, style_id) in map.snapshot(ctx).highlighted_chunks_at(row) { + let style_name = theme.syntax_style_name(style_id); + if let Some((last_chunk, last_style_name)) = chunks.last_mut() { + if style_name == *last_style_name { + last_chunk.push_str(chunk); + } else { + chunks.push((chunk.to_string(), style_name)); + } + } else { + chunks.push((chunk.to_string(), style_name)); + } + } + chunks + } + } + #[gpui::test] fn test_clip_point(app: &mut gpui::MutableAppContext) { use Bias::{Left, Right}; diff --git a/zed/src/language.rs b/zed/src/language.rs index d153760cfe..4e0bf6ff8d 100644 --- a/zed/src/language.rs +++ b/zed/src/language.rs @@ -14,8 +14,8 @@ pub struct Language { pub name: String, pub grammar: Grammar, pub highlight_query: Query, - path_suffixes: Vec, - theme_mapping: Mutex, + pub path_suffixes: Vec, + pub theme_mapping: Mutex, } pub struct LanguageRegistry { @@ -27,7 +27,7 @@ impl Language { self.theme_mapping.lock().clone() } - fn set_theme(&self, theme: &Theme) { + pub fn set_theme(&self, theme: &Theme) { *self.theme_mapping.lock() = ThemeMap::new(self.highlight_query.capture_names(), theme); } }