From 8e116855f5326ea5bda7181857400c2e27831259 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 May 2023 11:43:39 +0200 Subject: [PATCH] Add macro modifier for highlighting tokens in macro calls --- crates/ide/src/syntax_highlighting.rs | 78 +++++++------ crates/ide/src/syntax_highlighting/inject.rs | 29 ++++- crates/ide/src/syntax_highlighting/tags.rs | 8 +- .../test_data/highlight_doctest.html | 6 +- .../test_data/highlight_general.html | 6 +- .../test_data/highlight_injection.html | 24 ++-- .../test_data/highlight_keywords.html | 2 +- .../test_data/highlight_macros.html | 28 ++--- .../test_data/highlight_strings.html | 110 +++++++++--------- .../test_data/highlight_unsafe.html | 10 +- crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 1 + 12 files changed, 172 insertions(+), 131 deletions(-) diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 6bfc71f9397..751e51da0d2 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -166,6 +166,7 @@ pub struct HighlightConfig { // injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation. // intraDocLink:: Emitted for intra doc links in doc-strings. // library:: Emitted for items that are defined outside of the current crate. +// macro:: Emitted for tokens inside macro calls. // mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`. // public:: Emitted for items that are from the current crate and are `pub`. // reference:: Emitted for locals behind a reference and functions taking `self` by reference. @@ -240,6 +241,7 @@ fn traverse( let mut current_macro: Option = None; let mut macro_highlighter = MacroHighlighter::default(); let mut inside_attribute = false; + let mut inside_macro_call = false; // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. @@ -270,46 +272,50 @@ fn traverse( inside_attribute = false } - Enter(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { - match ast::Item::cast(node.clone()) { - Some(ast::Item::MacroRules(mac)) => { - macro_highlighter.init(); - current_macro = Some(mac.into()); - continue; - } - Some(ast::Item::MacroDef(mac)) => { - macro_highlighter.init(); - current_macro = Some(mac.into()); - continue; - } - Some(item) => { - if matches!(node.kind(), FN | CONST | STATIC) { - bindings_shadow_count.clear(); + Enter(NodeOrToken::Node(node)) => match ast::Item::cast(node.clone()) { + Some(item) => { + match item { + ast::Item::MacroRules(mac) => { + macro_highlighter.init(); + current_macro = Some(mac.into()); + continue; } + ast::Item::MacroDef(mac) => { + macro_highlighter.init(); + current_macro = Some(mac.into()); + continue; + } + ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => { + bindings_shadow_count.clear() + } + ast::Item::MacroCall(_) => { + inside_macro_call = true; + } + _ => (), + } - if attr_or_derive_item.is_none() { - if sema.is_attr_macro_call(&item) { - attr_or_derive_item = Some(AttrOrDerive::Attr(item)); - } else { - let adt = match item { - ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), - ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), - ast::Item::Union(it) => Some(ast::Adt::Union(it)), - _ => None, - }; - match adt { - Some(adt) if sema.is_derive_annotated(&adt) => { - attr_or_derive_item = - Some(AttrOrDerive::Derive(ast::Item::from(adt))); - } - _ => (), + if attr_or_derive_item.is_none() { + if sema.is_attr_macro_call(&item) { + attr_or_derive_item = Some(AttrOrDerive::Attr(item)); + } else { + let adt = match item { + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + }; + match adt { + Some(adt) if sema.is_derive_annotated(&adt) => { + attr_or_derive_item = + Some(AttrOrDerive::Derive(ast::Item::from(adt))); } + _ => (), } } } - _ => (), } - } + _ => (), + }, Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { match ast::Item::cast(node.clone()) { Some(ast::Item::MacroRules(mac)) => { @@ -327,6 +333,9 @@ fn traverse( { attr_or_derive_item = None; } + Some(ast::Item::MacroCall(_)) => { + inside_macro_call = false; + } _ => (), } } @@ -476,6 +485,9 @@ fn traverse( if inside_attribute { highlight |= HlMod::Attribute } + if inside_macro_call && tt_level > 0 { + highlight |= HlMod::Macro + } hl.add(HlRange { range, highlight, binding_hash }); } diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 3c4cfc78152..901df147d32 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -52,7 +52,11 @@ pub(super) fn ra_fixture( if let Some(next) = text.strip_prefix(marker) { if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) { - hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None }); + hl.add(HlRange { + range, + highlight: HlTag::Keyword | HlMod::Injected, + binding_hash: None, + }); } text = next; @@ -66,7 +70,16 @@ pub(super) fn ra_fixture( for mut hl_range in analysis .highlight( - HighlightConfig { syntactic_name_ref_highlighting: false, ..config }, + HighlightConfig { + syntactic_name_ref_highlighting: false, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + }, tmp_file_id, ) .unwrap() @@ -74,6 +87,7 @@ pub(super) fn ra_fixture( for range in inj.map_range_up(hl_range.range) { if let Some(range) = literal.map_range_up(range) { hl_range.range = range; + hl_range.highlight |= HlMod::Injected; hl.add(hl_range); } } @@ -217,7 +231,16 @@ pub(super) fn doc_comment( if let Ok(ranges) = analysis.with_db(|db| { super::highlight( db, - HighlightConfig { syntactic_name_ref_highlighting: true, ..config }, + HighlightConfig { + syntactic_name_ref_highlighting: true, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + }, tmp_file_id, None, ) diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index a81c4ee0cbd..f983109115f 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -49,7 +49,7 @@ pub enum HlMod { Associated = 0, /// Used with keywords like `async` and `await`. Async, - /// Used to differentiate individual elements within attributes. + /// Used to differentiate individual elements within attribute calls. Attribute, /// Callable item or value. Callable, @@ -72,6 +72,8 @@ pub enum HlMod { IntraDocLink, /// Used for items from other crates. Library, + /// Used to differentiate individual elements within macro calls. + Macro, /// Mutable binding. Mutable, /// Used for public items. @@ -200,7 +202,7 @@ impl fmt::Display for HlTag { } impl HlMod { - const ALL: &'static [HlMod; 19] = &[ + const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[ HlMod::Associated, HlMod::Async, HlMod::Attribute, @@ -214,6 +216,7 @@ impl HlMod { HlMod::Injected, HlMod::IntraDocLink, HlMod::Library, + HlMod::Macro, HlMod::Mutable, HlMod::Public, HlMod::Reference, @@ -237,6 +240,7 @@ impl HlMod { HlMod::Injected => "injected", HlMod::IntraDocLink => "intra_doc_link", HlMod::Library => "library", + HlMod::Macro => "macro", HlMod::Mutable => "mutable", HlMod::Public => "public", HlMod::Reference => "reference", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 18045f1f55a..35f240d4284 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -93,7 +93,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// let foo = Foo::new(); /// /// // calls bar on foo - /// assert!(foo.bar()); + /// assert!(foo.bar()); /// /// let bar = foo.bar || Foo::bar; /// @@ -145,7 +145,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ``` /// macro_rules! noop { ($expr:expr) => { $expr }} -/// noop!(1); +/// noop!(1); /// ``` macro_rules! noop { ($expr:expr) => { @@ -165,7 +165,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// #[cfg_attr(feature = "alloc", doc = "```rust")] #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] -/// let _ = example(&alloc::vec![1, 2, 3]); +/// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 9f2b1926b51..6b049f379ac 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -178,7 +178,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd impl<T> Option<T> { fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { - None => unimplemented!(), + None => unimplemented!(), Nope => Nope, } } @@ -192,7 +192,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd async fn async_main() { let f1 = learn_and_sing(); let f2 = dance(); - futures::join!(f1, f2); + futures::join!(f1, f2); } fn use_foo_items() { @@ -204,7 +204,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let control_flow = foo::identity(foo::ControlFlow::Continue); if control_flow.should_die() { - foo::die!(); + foo::die!(); } } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index abcd80c280b..d9c3db6fbb5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -45,18 +45,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
fn fixture(ra_fixture: &str) {}
 
 fn main() {
-    fixture(r#"
-trait Foo {
-    fn foo() {
-        println!("2 + 2 = {}", 4);
-    }
-}"#
+    fixture(r#"
+trait Foo {
+    fn foo() {
+        println!("2 + 2 = {}", 4);
+    }
+}"#
     );
-    fixture(r"
-fn foo() {
-    foo($0{
-        92
-    }$0)
-}"
+    fixture(r"
+fn foo() {
+    foo($0{
+        92
+    }$0)
+}"
     );
 }
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 66f9ede9629..3900959bedf 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -53,6 +53,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd macro_rules! void { ($($tt:tt)*) => {} } -void!(Self); +void!(Self); struct __ where Self:; fn __(_: Self) {} \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 54d4279525d..2cbbf696415 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -42,21 +42,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
proc_macros::mirror! {
-    {
-        ,i32 :x pub
-        ,i32 :y pub
-    } Foo struct
-}
+
proc_macros::mirror! {
+    {
+        ,i32 :x pub
+        ,i32 :y pub
+    } Foo struct
+}
 macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
 
-def_fn! {
-    fn bar() -> u32 {
-        100
-    }
-}
+def_fn! {
+    fn bar() -> u32 {
+        100
+    }
+}
 
 macro_rules! dont_color_me_braces {
     () => {0}
@@ -90,7 +90,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 fn main() {
-    println!("Hello, {}!", 92);
-    dont_color_me_braces!();
-    noop!(noop!(1));
+    println!("Hello, {}!", 92);
+    dont_color_me_braces!();
+    noop!(noop!(1));
 }
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 4c6d69bc632..6acc62e0f1e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -103,73 +103,73 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let a = '\x65'; let a = '\x00'; - println!("Hello {{Hello}}"); + println!("Hello {{Hello}}"); // from https://doc.rust-lang.org/std/fmt/index.html - println!("Hello"); // => "Hello" - println!("Hello, {}!", "world"); // => "Hello, world!" - println!("The number is {}", 1); // => "The number is 1" - println!("{:?}", (3, 4)); // => "(3, 4)" - println!("{value}", value=4); // => "4" - println!("{} {}", 1, 2); // => "1 2" - println!("{:04}", 42); // => "0042" with leading zerosV - println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" - println!("{argument}", argument = "test"); // => "test" - println!("{name} {}", 1, name = 2); // => "2 1" - println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" - println!("{{{}}}", 2); // => "{2}" - println!("Hello {:5}!", "x"); - println!("Hello {:1$}!", "x", 5); - println!("Hello {1:0$}!", 5, "x"); - println!("Hello {:width$}!", "x", width = 5); - println!("Hello {:<5}!", "x"); - println!("Hello {:-<5}!", "x"); - println!("Hello {:^5}!", "x"); - println!("Hello {:>5}!", "x"); - println!("Hello {:+}!", 5); - println!("{:#x}!", 27); - println!("Hello {:05}!", 5); - println!("Hello {:05}!", -5); - println!("{:#010x}!", 27); - println!("Hello {0} is {1:.5}", "x", 0.01); - println!("Hello {1} is {2:.0$}", 5, "x", 0.01); - println!("Hello {0} is {2:.1$}", "x", 5, 0.01); - println!("Hello {} is {:.*}", "x", 5, 0.01); - println!("Hello {} is {2:.*}", "x", 5, 0.01); - println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); - println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); - println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); - println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); + println!("Hello"); // => "Hello" + println!("Hello, {}!", "world"); // => "Hello, world!" + println!("The number is {}", 1); // => "The number is 1" + println!("{:?}", (3, 4)); // => "(3, 4)" + println!("{value}", value=4); // => "4" + println!("{} {}", 1, 2); // => "1 2" + println!("{:04}", 42); // => "0042" with leading zerosV + println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" + println!("{argument}", argument = "test"); // => "test" + println!("{name} {}", 1, name = 2); // => "2 1" + println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" + println!("{{{}}}", 2); // => "{2}" + println!("Hello {:5}!", "x"); + println!("Hello {:1$}!", "x", 5); + println!("Hello {1:0$}!", 5, "x"); + println!("Hello {:width$}!", "x", width = 5); + println!("Hello {:<5}!", "x"); + println!("Hello {:-<5}!", "x"); + println!("Hello {:^5}!", "x"); + println!("Hello {:>5}!", "x"); + println!("Hello {:+}!", 5); + println!("{:#x}!", 27); + println!("Hello {:05}!", 5); + println!("Hello {:05}!", -5); + println!("{:#010x}!", 27); + println!("Hello {0} is {1:.5}", "x", 0.01); + println!("Hello {1} is {2:.0$}", 5, "x", 0.01); + println!("Hello {0} is {2:.1$}", "x", 5, 0.01); + println!("Hello {} is {:.*}", "x", 5, 0.01); + println!("Hello {} is {2:.*}", "x", 5, 0.01); + println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); + println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); + println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); + println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); let _ = "{}" let _ = "{{}}"; - println!("Hello {{}}"); - println!("{{ Hello"); - println!("Hello }}"); - println!("{{Hello}}"); - println!("{{ Hello }}"); - println!("{{Hello }}"); - println!("{{ Hello}}"); + println!("Hello {{}}"); + println!("{{ Hello"); + println!("Hello }}"); + println!("{{Hello}}"); + println!("{{ Hello }}"); + println!("{{Hello }}"); + println!("{{ Hello}}"); - println!(r"Hello, {}!", "world"); + println!(r"Hello, {}!", "world"); // escape sequences - println!("Hello\nWorld"); - println!("\u{48}\x65\x6C\x6C\x6F World"); + println!("Hello\nWorld"); + println!("\u{48}\x65\x6C\x6C\x6F World"); let _ = "\x28\x28\x00\x63\n"; let _ = b"\x28\x28\x00\x63\n"; let _ = r"\\"; - println!("{\x41}", A = 92); - println!("{ничоси}", ничоси = 92); + println!("{\x41}", A = 92); + println!("{ничоси}", ничоси = 92); - println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); - panic!("more {}", 1); - assert!(true, "{}", 1); - assert!(true, "{} asdasd", 1); - toho!("{}fmt", 0); - asm!("mov eax, {0}"); - format_args!(concat!("{}"), "{}"); + println!("{:x?} {} ", thingy, n2); + panic!("{}", 0); + panic!("more {}", 1); + assert!(true, "{}", 1); + assert!(true, "{} asdasd", 1); + toho!("{}fmt", 0); + asm!("mov eax, {0}"); + format_args!(concat!("{}"), "{}"); }
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 1992bdc6ae3..654d51b8a43 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -89,13 +89,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let x = &5 as *const _ as *const usize; let u = Union { b: 0 }; - id! { - unsafe { unsafe_deref!() } - }; + id! { + unsafe { unsafe_deref!() } + }; unsafe { - unsafe_deref!(); - id! { unsafe_deref!() }; + unsafe_deref!(); + id! { unsafe_deref!() }; // unsafe fn and method calls unsafe_fn(); diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index e5b43c5a10c..d4bb20c8f44 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -141,6 +141,7 @@ define_semantic_token_modifiers![ (INJECTED, "injected"), (INTRA_DOC_LINK, "intraDocLink"), (LIBRARY, "library"), + (MACRO_MODIFIER, "macro"), (MUTABLE, "mutable"), (PUBLIC, "public"), (REFERENCE, "reference"), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 616bdddd92b..3428f7eba30 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -746,6 +746,7 @@ fn semantic_token_type_and_modifiers( HlMod::Injected => semantic_tokens::INJECTED, HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, HlMod::Library => semantic_tokens::LIBRARY, + HlMod::Macro => semantic_tokens::MACRO_MODIFIER, HlMod::Mutable => semantic_tokens::MUTABLE, HlMod::Public => semantic_tokens::PUBLIC, HlMod::Reference => semantic_tokens::REFERENCE,