diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 780a03c1380e..5a46042d5952 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -30,10 +30,6 @@ impl ActiveParameter { pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option { call_info(db, position)?.into_active_parameter() } - - pub(crate) fn at_token(sema: &Semantics, token: SyntaxToken) -> Option { - call_info_for_token(sema, token)?.into_active_parameter() - } } fn call_info_for_token(sema: &Semantics, token: SyntaxToken) -> Option { diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html deleted file mode 100644 index ea026d7a0464..000000000000 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ /dev/null @@ -1,40 +0,0 @@ - - -
fn fixture(ra_fixture: &str) {}
-
-fn main() {
-    fixture(r#"
-        trait Foo {
-            fn foo() {
-                println!("2 + 2 = {}", 4);
-            }
-        }"#
-    );
-}
\ No newline at end of file diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index de06daf72b08..c217eefa0975 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html @@ -27,57 +27,57 @@ .keyword.unsafe { color: #BC8383; font-weight: bold; } .control { font-style: italic; } -
macro_rules! println {
+
macro_rules! println {
     ($($arg:tt)*) => ({
-        $crate::io::_print($crate::format_args_nl!($($arg)*));
+        $crate::io::_print($crate::format_args_nl!($($arg)*));
     })
 }
 #[rustc_builtin_macro]
-macro_rules! format_args_nl {
-    ($fmt:expr) => {{ /* compiler built-in */ }};
-    ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+macro_rules! format_args_nl {
+    ($fmt:expr) => {{ /* compiler built-in */ }};
+    ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
 }
 
-fn main() {
-    // 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!("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 {{}}");
-    println!("{{ Hello");
+fn main() {
+    // 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!("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 {{}}");
+    println!("{{ Hello");
 
-    println!(r"Hello, {}!", "world");
+    println!(r"Hello, {}!", "world");
 
-    println!("{\x41}", A = 92);
-    println!("{ничоси}", ничоси = 92);
+    println!("{\x41}", A = 92);
+    println!("{ничоси}", ничоси = 92);
 }
\ No newline at end of file diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 4b12fe8238cf..ff2f1458bbdb 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -27,54 +27,54 @@ .keyword.unsafe { color: #BC8383; font-weight: bold; } .control { font-style: italic; } -
#[derive(Clone, Debug)]
-struct Foo {
-    pub x: i32,
-    pub y: i32,
+
#[derive(Clone, Debug)]
+struct Foo {
+    pub x: i32,
+    pub y: i32,
 }
 
-fn foo<'a, T>() -> T {
-    foo::<'a, i32>()
+fn foo<'a, T>() -> T {
+    foo::<'a, i32>()
 }
 
-macro_rules! def_fn {
+macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
 
-def_fn! {
-    fn bar() -> u32 {
-        100
+def_fn! {
+    fn bar() -> u32 {
+        100
     }
 }
 
-// comment
-fn main() {
-    println!("Hello, {}!", 92);
+// comment
+fn main() {
+    println!("Hello, {}!", 92);
 
-    let mut vec = Vec::new();
-    if true {
-        let x = 92;
-        vec.push(Foo { x, y: 1 });
+    let mut vec = Vec::new();
+    if true {
+        let x = 92;
+        vec.push(Foo { x, y: 1 });
     }
-    unsafe { vec.set_len(0); }
+    unsafe { vec.set_len(0); }
 
-    let mut x = 42;
-    let y = &mut x;
-    let z = &y;
+    let mut x = 42;
+    let y = &mut x;
+    let z = &y;
 
     y;
 }
 
-enum Option<T> {
+enum Option<T> {
     Some(T),
     None,
 }
-use Option::*;
+use Option::*;
 
-impl<T> Option<T> {
-    fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
-        match other {
-            None => unimplemented!(),
+impl<T> Option<T> {
+    fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
+        match other {
+            None => unimplemented!(),
             Nope => Nope,
         }
     }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 11e1f3e44e59..7e56f27d1665 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -27,15 +27,15 @@
 .keyword.unsafe     { color: #BC8383; font-weight: bold; }
 .control            { font-style: italic; }
 
-
fn main() {
-    let hello = "hello";
-    let x = hello.to_string();
-    let y = hello.to_string();
+
fn main() {
+    let hello = "hello";
+    let x = hello.to_string();
+    let y = hello.to_string();
 
-    let x = "other color please!";
-    let y = x.to_string();
+    let x = "other color please!";
+    let y = x.to_string();
 }
 
-fn bar() {
-    let mut hello = "hello";
+fn bar() {
+    let mut hello = "hello";
 }
\ No newline at end of file diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 6658c7bb270e..c4cc34670d26 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -12,14 +12,14 @@ use ra_ide_db::{ }; use ra_prof::profile; use ra_syntax::{ - ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, - AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, + ast::{self, HasFormatSpecifier}, + AstNode, AstToken, NodeOrToken, SyntaxElement, SyntaxKind::*, - SyntaxToken, TextRange, WalkEvent, T, + TextRange, WalkEvent, }; use rustc_hash::FxHashMap; -use crate::{call_info::ActiveParameter, Analysis, FileId}; +use crate::FileId; use ast::FormatSpecifier; pub(crate) use html::highlight_as_html; @@ -160,13 +160,6 @@ pub(crate) fn highlight( match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { WalkEvent::Enter(Some(mc)) => { current_macro_call = Some(mc.clone()); - if let Some(range) = macro_call_range(&mc) { - stack.add(HighlightedRange { - range, - highlight: HighlightTag::Macro.into(), - binding_hash: None, - }); - } continue; } WalkEvent::Leave(Some(mc)) => { @@ -226,51 +219,38 @@ pub(crate) fn highlight( element.clone() }; - if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { - let expanded = element_to_highlight.as_token().unwrap().clone(); - if highlight_injection(&mut stack, &sema, token, expanded).is_some() { - continue; - } - } - let is_format_string = format_string.as_ref() == Some(&element_to_highlight); if let Some((highlight, binding_hash)) = highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) { stack.add(HighlightedRange { range, highlight, binding_hash }); - if let Some(string) = - element_to_highlight.as_token().cloned().and_then(ast::String::cast) - { - stack.push(); - if is_format_string { - string.lex_format_specifier(|piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HighlightedRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } - }); - } - stack.pop(); - } else if let Some(string) = - element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) - { - stack.push(); - if is_format_string { - string.lex_format_specifier(|piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HighlightedRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } - }); - } - stack.pop(); + } + if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) { + if is_format_string { + string.lex_format_specifier(|piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HighlightedRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, + }); + } + }); + } + } else if let Some(string) = + element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) + { + if is_format_string { + string.lex_format_specifier(|piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HighlightedRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, + }); + } + }); } } } @@ -280,9 +260,9 @@ pub(crate) fn highlight( fn highlight_format_specifier(kind: FormatSpecifier) -> Option { Some(match kind { - FormatSpecifier::Open - | FormatSpecifier::Close - | FormatSpecifier::Colon + FormatSpecifier::Open => HighlightTag::FormatSpecifierOpen, + FormatSpecifier::Close => HighlightTag::FormatSpecifierClose, + FormatSpecifier::Colon | FormatSpecifier::Fill | FormatSpecifier::Align | FormatSpecifier::Sign @@ -296,22 +276,6 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option { }) } -fn macro_call_range(macro_call: &ast::MacroCall) -> Option { - let path = macro_call.path()?; - let name_ref = path.segment()?.name_ref()?; - - let range_start = name_ref.syntax().text_range().start(); - let mut range_end = name_ref.syntax().text_range().end(); - for sibling in path.syntax().siblings_with_tokens(Direction::Next) { - match sibling.kind() { - T![!] | IDENT => range_end = sibling.text_range().end(), - _ => (), - } - } - - Some(TextRange::new(range_start, range_end)) -} - fn highlight_element( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, @@ -340,9 +304,9 @@ fn highlight_element( match name_kind { Some(NameClass::Definition(def)) => { - highlight_name(db, def) | HighlightModifier::Definition + highlight_name(db, false, def)? | HighlightModifier::Definition } - Some(NameClass::ConstReference(def)) => highlight_name(db, def), + Some(NameClass::ConstReference(def)) => highlight_name(db, false, def)?, None => highlight_name_by_syntax(name) | HighlightModifier::Definition, } } @@ -350,6 +314,7 @@ fn highlight_element( // Highlight references like the definitions they resolve to NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, NAME_REF => { + let is_path = element.parent().map(|it| it.kind()) == Some(PATH_SEGMENT); let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); match classify_name_ref(sema, &name_ref) { Some(name_kind) => match name_kind { @@ -361,46 +326,20 @@ fn highlight_element( binding_hash = Some(calc_binding_hash(&name, *shadow_count)) } }; - highlight_name(db, def) + highlight_name(db, is_path, def)? + } + NameRefClass::FieldShorthand { local, .. } => { + let mut h = Highlight::new(HighlightTag::Local); + if local.is_mut(db) || local.ty(db).is_mutable_reference() { + h |= HighlightModifier::Mutable; + } + h } - NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), }, None => HighlightTag::UnresolvedReference.into(), } } - // Simple token-based highlighting - COMMENT => HighlightTag::Comment.into(), - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), - ATTR => HighlightTag::Attribute.into(), - INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), - BYTE => HighlightTag::ByteLiteral.into(), - CHAR => HighlightTag::CharLiteral.into(), - LIFETIME => { - let h = Highlight::new(HighlightTag::Lifetime); - match element.parent().map(|it| it.kind()) { - Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition, - _ => h, - } - } - - k if k.is_keyword() => { - let h = Highlight::new(HighlightTag::Keyword); - match k { - T![break] - | T![continue] - | T![else] - | T![for] - | T![if] - | T![loop] - | T![match] - | T![return] - | T![while] => h | HighlightModifier::ControlFlow, - T![unsafe] => h | HighlightModifier::Unsafe, - _ => h, - } - } - _ => return None, }; @@ -419,35 +358,34 @@ fn highlight_element( } } -fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { +fn highlight_name(db: &RootDatabase, is_path: bool, def: Definition) -> Option { match def { - Definition::Macro(_) => HighlightTag::Macro, - Definition::Field(_) => HighlightTag::Field, - Definition::ModuleDef(def) => match def { - hir::ModuleDef::Module(_) => HighlightTag::Module, - hir::ModuleDef::Function(_) => HighlightTag::Function, - hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, - hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, - hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, - hir::ModuleDef::EnumVariant(_) => HighlightTag::EnumVariant, - hir::ModuleDef::Const(_) => HighlightTag::Constant, - hir::ModuleDef::Static(_) => HighlightTag::Static, - hir::ModuleDef::Trait(_) => HighlightTag::Trait, - hir::ModuleDef::TypeAlias(_) => HighlightTag::TypeAlias, - hir::ModuleDef::BuiltinType(_) => HighlightTag::BuiltinType, - }, - Definition::SelfType(_) => HighlightTag::SelfType, - Definition::TypeParam(_) => HighlightTag::TypeParam, + Definition::ModuleDef(def) => { + let tag = match def { + hir::ModuleDef::Function(_) if is_path => HighlightTag::Function, + hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, + hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, + hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, + hir::ModuleDef::EnumVariant(_) => HighlightTag::EnumVariant, + hir::ModuleDef::Const(_) => HighlightTag::Constant, + hir::ModuleDef::Static(_) => HighlightTag::Static, + hir::ModuleDef::Trait(_) => HighlightTag::Trait, + hir::ModuleDef::TypeAlias(_) => HighlightTag::TypeAlias, + _ => return None, + }; + Some(Highlight::new(tag)) + } + Definition::TypeParam(_) => Some(Highlight::new(HighlightTag::TypeParam)), // FIXME: distinguish between locals and parameters Definition::Local(local) => { let mut h = Highlight::new(HighlightTag::Local); if local.is_mut(db) || local.ty(db).is_mutable_reference() { h |= HighlightModifier::Mutable; } - return h; + Some(h) } + _ => None, } - .into() } fn highlight_name_by_syntax(name: ast::Name) -> Highlight { @@ -465,46 +403,6 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { TRAIT_DEF => HighlightTag::Trait.into(), TYPE_ALIAS_DEF => HighlightTag::TypeAlias.into(), TYPE_PARAM => HighlightTag::TypeParam.into(), - RECORD_FIELD_DEF => HighlightTag::Field.into(), _ => default, } } - -fn highlight_injection( - acc: &mut HighlightedRangeStack, - sema: &Semantics, - literal: ast::RawString, - expanded: SyntaxToken, -) -> Option<()> { - let active_parameter = ActiveParameter::at_token(&sema, expanded)?; - if !active_parameter.name.starts_with("ra_fixture") { - return None; - } - let value = literal.value()?; - let (analysis, tmp_file_id) = Analysis::from_single_file(value); - - if let Some(range) = literal.open_quote_text_range() { - acc.add(HighlightedRange { - range, - highlight: HighlightTag::StringLiteral.into(), - binding_hash: None, - }) - } - - for mut h in analysis.highlight(tmp_file_id).unwrap() { - if let Some(r) = literal.map_range_up(h.range) { - h.range = r; - acc.add(h) - } - } - - if let Some(range) = literal.close_quote_text_range() { - acc.add(HighlightedRange { - range, - highlight: HighlightTag::StringLiteral.into(), - binding_hash: None, - }) - } - - Some(()) -} diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs index be1a0f12b25b..9dc2b9ba610a 100644 --- a/crates/ra_ide/src/syntax_highlighting/tags.rs +++ b/crates/ra_ide/src/syntax_highlighting/tags.rs @@ -14,24 +14,13 @@ pub struct HighlightModifiers(u32); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum HighlightTag { - Attribute, - BuiltinType, - ByteLiteral, - CharLiteral, - Comment, Constant, Enum, EnumVariant, - Field, Function, - Keyword, - Lifetime, - Macro, Module, NumericLiteral, - SelfType, Static, - StringLiteral, Struct, Trait, TypeAlias, @@ -40,6 +29,8 @@ pub enum HighlightTag { Local, UnresolvedReference, FormatSpecifier, + FormatSpecifierOpen, + FormatSpecifierClose, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -57,24 +48,13 @@ pub enum HighlightModifier { impl HighlightTag { fn as_str(self) -> &'static str { match self { - HighlightTag::Attribute => "attribute", - HighlightTag::BuiltinType => "builtin_type", - HighlightTag::ByteLiteral => "byte_literal", - HighlightTag::CharLiteral => "char_literal", - HighlightTag::Comment => "comment", HighlightTag::Constant => "constant", HighlightTag::Enum => "enum", HighlightTag::EnumVariant => "enum_variant", - HighlightTag::Field => "field", HighlightTag::Function => "function", - HighlightTag::Keyword => "keyword", - HighlightTag::Lifetime => "lifetime", - HighlightTag::Macro => "macro", HighlightTag::Module => "module", HighlightTag::NumericLiteral => "numeric_literal", - HighlightTag::SelfType => "self_type", HighlightTag::Static => "static", - HighlightTag::StringLiteral => "string_literal", HighlightTag::Struct => "struct", HighlightTag::Trait => "trait", HighlightTag::TypeAlias => "type_alias", @@ -83,6 +63,8 @@ impl HighlightTag { HighlightTag::Local => "variable", HighlightTag::UnresolvedReference => "unresolved_reference", HighlightTag::FormatSpecifier => "format_specifier", + HighlightTag::FormatSpecifierOpen => "format_specifier_open", + HighlightTag::FormatSpecifierClose => "format_specifier_close", } } } diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index d2926ba78029..885ba34cec90 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -117,44 +117,18 @@ fn accidentally_quadratic() { fn test_ranges() { let (analysis, file_id) = single_file( r#" - #[derive(Clone, Debug)] - struct Foo { - pub x: i32, - pub y: i32, + fn main() { + let x = 1; + println!("Hello, {}", x); }"#, ); // The "x" let highlights = &analysis - .highlight_range(FileRange { file_id, range: TextRange::at(82.into(), 1.into()) }) + .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) }) .unwrap(); - assert_eq!(&highlights[0].highlight.to_string(), "field.declaration"); -} - -#[test] -fn test_flattening() { - let (analysis, file_id) = single_file( - r##" -fn fixture(ra_fixture: &str) {} - -fn main() { - fixture(r#" - trait Foo { - fn foo() { - println!("2 + 2 = {}", 4); - } - }"# - ); -}"## - .trim(), - ); - - let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html"); - let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); - let expected_html = &read_text(&dst_file); - fs::write(dst_file, &actual_html).unwrap(); - assert_eq_text!(expected_html, actual_html); + assert_eq!(&highlights[0].highlight.to_string(), "variable.declaration"); } #[test] diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index f64c90b5b17a..33a9292e9408 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -25,8 +25,7 @@ use crate::{ Result, }; use semantic_tokens::{ - ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION, - UNRESOLVED_REFERENCE, + ENUM_MEMBER, FORMAT_SPECIFIER, FORMAT_SPECIFIER_CLOSE, FORMAT_SPECIFIER_OPEN, TYPE_ALIAS, UNION, }; pub trait Conv { @@ -346,7 +345,7 @@ impl ConvWith<&LineIndex> for InlayHint { } impl Conv for Highlight { - type Output = (u32, u32); + type Output = Option<(u32, u32)>; fn conv(self) -> Self::Output { let mut mods = ModifierSet::default(); @@ -356,9 +355,6 @@ impl Conv for Highlight { HighlightTag::Union => UNION, HighlightTag::TypeAlias => TYPE_ALIAS, HighlightTag::Trait => SemanticTokenType::INTERFACE, - HighlightTag::BuiltinType => BUILTIN_TYPE, - HighlightTag::SelfType => SemanticTokenType::TYPE, - HighlightTag::Field => SemanticTokenType::MEMBER, HighlightTag::Function => SemanticTokenType::FUNCTION, HighlightTag::Module => SemanticTokenType::NAMESPACE, HighlightTag::Constant => { @@ -371,17 +367,13 @@ impl Conv for Highlight { SemanticTokenType::VARIABLE } HighlightTag::EnumVariant => ENUM_MEMBER, - HighlightTag::Macro => SemanticTokenType::MACRO, HighlightTag::Local => SemanticTokenType::VARIABLE, HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER, - HighlightTag::Lifetime => LIFETIME, - HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => SemanticTokenType::NUMBER, - HighlightTag::CharLiteral | HighlightTag::StringLiteral => SemanticTokenType::STRING, - HighlightTag::Comment => SemanticTokenType::COMMENT, - HighlightTag::Attribute => ATTRIBUTE, - HighlightTag::Keyword => SemanticTokenType::KEYWORD, - HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, + HighlightTag::NumericLiteral => SemanticTokenType::NUMBER, + HighlightTag::UnresolvedReference => return None, HighlightTag::FormatSpecifier => FORMAT_SPECIFIER, + HighlightTag::FormatSpecifierOpen => FORMAT_SPECIFIER_OPEN, + HighlightTag::FormatSpecifierClose => FORMAT_SPECIFIER_CLOSE, }; for modifier in self.modifiers.iter() { @@ -394,7 +386,7 @@ impl Conv for Highlight { mods |= modifier; } - (semantic_tokens::type_index(type_), mods.0) + Some((semantic_tokens::type_index(type_), mods.0)) } } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index f4353af64703..a0046530eddb 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -1175,13 +1175,14 @@ pub fn handle_semantic_tokens( let mut builder = SemanticTokensBuilder::default(); for highlight_range in world.analysis().highlight(file_id)?.into_iter() { - let (token_index, modifier_bitset) = highlight_range.highlight.conv(); - for mut range in line_index.lines(highlight_range.range) { - if text[range].ends_with('\n') { - range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); + if let Some((token_index, modifier_bitset)) = highlight_range.highlight.conv() { + for mut range in line_index.lines(highlight_range.range) { + if text[range].ends_with('\n') { + range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); + } + let range = range.conv_with(&line_index); + builder.push(range, token_index, modifier_bitset); } - let range = range.conv_with(&line_index); - builder.push(range, token_index, modifier_bitset); } } @@ -1202,8 +1203,9 @@ pub fn handle_semantic_tokens_range( let mut builder = SemanticTokensBuilder::default(); for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { - let (token_type, token_modifiers) = highlight_range.highlight.conv(); - builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); + if let Some((token_type, token_modifiers)) = highlight_range.highlight.conv() { + builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); + } } let tokens = builder.build(); diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 2dc5cb1196fc..94562df71e87 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -43,6 +43,8 @@ define_semantic_token_types![ (UNION, "union"), (UNRESOLVED_REFERENCE, "unresolvedReference"), (FORMAT_SPECIFIER, "formatSpecifier"), + (FORMAT_SPECIFIER_OPEN, "formatSpecifierOpen"), + (FORMAT_SPECIFIER_CLOSE, "formatSpecifierClose"), ]; macro_rules! define_semantic_token_modifiers { diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore index ac411f8e2be4..257b744bfeaa 100644 --- a/editors/code/.vscodeignore +++ b/editors/code/.vscodeignore @@ -3,5 +3,6 @@ !package.json !package-lock.json !ra_syntax_tree.tmGrammar.json +!rust.tmGrammar.json !icon.png !README.md diff --git a/editors/code/package.json b/editors/code/package.json index c6fc13519e06..4b06c739241e 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -478,6 +478,11 @@ } ], "grammars": [ + { + "language": "rust", + "scopeName": "source.rust", + "path": "rust.tmGrammar.json" + }, { "language": "ra_syntax_tree", "scopeName": "source.ra_syntax_tree", @@ -596,28 +601,40 @@ "support.type.primitive" ], "lifetime": [ - "entity.name.lifetime.rust" + "entity.name.type.lifetime" ], "typeAlias": [ - "entity.name.typeAlias" + "entity.name.type.typeAlias" ], "union": [ - "entity.name.union" + "entity.name.type.union" ], "struct": [ "entity.name.type.struct" ], - "keyword.unsafe": [ - "keyword.other.unsafe" - ], - "keyword": [ - "keyword" - ], "keyword.controlFlow": [ "keyword.control" ], + "keyword": [ + "keyword.other" + ], "variable.constant": [ - "entity.name.constant" + "variable.other.constant" + ], + "formatSpecifier": [ + "punctuation.section.embedded" + ], + "formatSpecifierOpen": [ + "punctuation.definition.template-expression.begin" + ], + "formatSpecifierClose": [ + "punctuation.definition.template-expression.end" + ], + "*.mutable": [ + "markup.underline" + ], + "*.static": [ + "markup.italic" ] } } diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json new file mode 100644 index 000000000000..54ff4e4c0192 --- /dev/null +++ b/editors/code/rust.tmGrammar.json @@ -0,0 +1,293 @@ +{ + "name": "Rust", + "scopeName": "source.rust", + "patterns": [ + { + "comment": "Implementation", + "begin": "\\b(impl)\\b", + "end": "\\{", + "beginCaptures": { + "1": { + "name": "storage.type.rust" + } + }, + "patterns": [ + { + "include": "#block_comment" + }, + { + "include": "#line_comment" + }, + { + "include": "#sigils" + }, + { + "name": "keyword.other", + "match": "\\b(for|mut|dyn|where|self|Self)\\b" + } + ] + }, + { + "include": "#block_doc_comment" + }, + { + "include": "#block_comment" + }, + { + "include": "#line_doc_comment" + }, + { + "include": "#line_comment" + }, + { + "comment": "Attribute", + "name": "punctuation.definition.tag", + "begin": "#\\!?\\[", + "end": "\\]", + "patterns": [ + { + "include": "#string_literal" + }, + { + "include": "#block_doc_comment" + }, + { + "include": "#block_comment" + }, + { + "include": "#line_doc_comment" + }, + { + "include": "#line_comment" + } + ] + }, + { + "comment": "Single-quote string literal (character)", + "name": "string.quoted.single", + "match": "b?'([^'\\\\]|\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.))'" + }, + { + "include": "#string_literal" + }, + { + "include": "#raw_string_literal" + }, + { + "comment": "Floating point literal (fraction)", + "name": "constant.numeric.float", + "match": "\\b[0-9][0-9_]*\\.[0-9][0-9_]*([eE][+-]?[0-9_]+)?(f32|f64)?\\b" + }, + { + "comment": "Floating point literal (exponent)", + "name": "constant.numeric.float", + "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?[eE][+-]?[0-9_]+(f32|f64)?\\b" + }, + { + "comment": "Floating point literal (typed)", + "name": "constant.numeric.float", + "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?([eE][+-]?[0-9_]+)?(f32|f64)\\b" + }, + { + "comment": "Integer literal (decimal)", + "name": "constant.numeric.integer.decimal", + "match": "\\b[0-9][0-9_]*([ui](8|16|32|64|128|s|size))?\\b" + }, + { + "comment": "Integer literal (hexadecimal)", + "name": "constant.numeric.integer.hexadecimal", + "match": "\\b0x[a-fA-F0-9_]+([ui](8|16|32|64|128|s|size))?\\b" + }, + { + "comment": "Integer literal (octal)", + "name": "constant.numeric.integer.octal", + "match": "\\b0o[0-7_]+([ui](8|16|32|64|128|s|size))?\\b" + }, + { + "comment": "Integer literal (binary)", + "name": "constant.numeric.integer.binary", + "match": "\\b0b[01_]+([ui](8|16|32|64|128|s|size))?\\b" + }, + { + "comment": "Static storage modifier", + "name": "storage.modifier.static", + "match": "\\bstatic\\b" + }, + { + "comment": "Boolean constant", + "name": "constant.language.boolean", + "match": "\\b(true|false)\\b" + }, + { + "comment": "Control keyword", + "name": "keyword.control", + "match": "\\b(await|break|continue|else|if|in|for|loop|match|return|try|while)\\b" + }, + { + "comment": "Keyword", + "name": "keyword.other", + "match": "\\b(async|crate|extern|mod|let|ref|use|super|move|self|mut|dyn|impl|box|const|pub|unsafe|where|type|enum|struct|trait|union|Self)\\b" + }, + { + "comment": "Reserved keyword", + "name": "invalid.deprecated", + "match": "\\b(abstract|alignof|become|do|final|macro|offsetof|override|priv|proc|pure|sizeof|typeof|virtual|yield)\\b" + }, + { + "include": "#sigils" + }, + { + "comment": "Named lifetime", + "name": "entity.name.type.lifetime", + "match": "'([a-zA-Z_][a-zA-Z0-9_]*)\\b", + "captures": { + "1": { + "name": "entity.name.type.lifetime" + } + } + }, + { + "comment": "Reference with named lifetime", + "match": "&('([a-zA-Z_][a-zA-Z0-9_]*))\\b", + "captures": { + "1": { + "name": "entity.name.type.lifetime" + }, + "2": { + "name": "entity.name.type.lifetime" + } + } + }, + { + "comment": "Miscellaneous operator", + "name": "keyword.operator.misc", + "match": "(=>|::|\\bas\\b)" + }, + { + "comment": "Comparison operator", + "name": "keyword.operator.comparison", + "match": "(&&|\\|\\||==|!=)" + }, + { + "comment": "Assignment operator", + "name": "keyword.operator.assignment", + "match": "(\\+=|-=|/=|\\*=|%=|\\^=|&=|\\|=|<<=|>>=|=)" + }, + { + "comment": "Arithmetic operator", + "name": "keyword.operator.arithmetic", + "match": "(!|\\+|-|/|\\*|%|\\^|&|\\||<<|>>)" + }, + { + "comment": "Comparison operator (second group because of regex precedence)", + "name": "keyword.operator.comparison", + "match": "(<=|>=|<|>)" + }, + { + "comment": "Built-in/core type", + "name": "entity.name.type", + "match": "\\b(bool|char|usize|isize|u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f32|f64|str)\\b" + }, + { + "comment": "Function call", + "match": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*\\(", + "captures": { + "1": { + "name": "entity.name.function" + } + } + }, + { + "comment": "Function call with type parameters", + "match": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*(::)(?=\\s*<.*>\\s*\\()", + "captures": { + "1": { + "name": "entity.name.function" + }, + "2": { + "name": "keyword.operator.misc" + } + } + }, + { + "comment": "Function definition", + "match": "\\b(fn)\\s+([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)", + "captures": { + "1": { + "name": "keyword.other.fn" + }, + "2": { + "name": "entity.name.function" + } + } + } + ], + "repository": { + "block_doc_comment": { + "comment": "Block documentation comment", + "name": "comment.block.documentation", + "begin": "/\\*[\\*!](?![\\*/])", + "end": "\\*/", + "patterns": [ + { + "include": "#block_doc_comment" + }, + { + "include": "#block_comment" + } + ] + }, + "block_comment": { + "comment": "Block comment", + "name": "comment.block", + "begin": "/\\*", + "end": "\\*/", + "patterns": [ + { + "include": "#block_doc_comment" + }, + { + "include": "#block_comment" + } + ] + }, + "line_doc_comment": { + "comment": "Single-line documentation comment", + "name": "comment.line.documentation", + "begin": "//[!/](?=[^/])", + "end": "$" + }, + "line_comment": { + "comment": "Single-line comment", + "name": "comment.line.double-slash", + "begin": "//", + "end": "$" + }, + "escaped_character": { + "name": "constant.character.escape", + "match": "\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)" + }, + "string_literal": { + "comment": "Double-quote string literal", + "name": "string.quoted.double", + "begin": "b?\"", + "end": "\"", + "patterns": [ + { + "include": "#escaped_character" + } + ] + }, + "raw_string_literal": { + "comment": "Raw double-quote string literal", + "name": "string.quoted.double.raw", + "begin": "b?r(#*)\"", + "end": "\"\\1" + }, + "sigils": { + "comment": "Sigil", + "name": "keyword.other.sigil", + "match": "&" + } + } +} \ No newline at end of file