From 5bf3e949e8470a138a61c806769e1a329761cab6 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 23 May 2019 19:42:42 +0200 Subject: Semantic highlighting spike Very simple approach: For each identifier, set the hash of the range where it's defined as its 'id' and use it in the VSCode extension to generate unique colors. Thus, the generated colors are per-file. They are also quite fragile, and I'm not entirely sure why. Looks like we need to make sure the same ranges aren't overwritten by a later request? --- .../src/snapshots/tests__highlighting.snap | 192 +++++++++++++++++++++ .../src/snapshots/tests__sematic_highlighting.snap | 87 ++++++++++ crates/ra_ide_api/src/syntax_highlighting.rs | 101 +++++++---- 3 files changed, 345 insertions(+), 35 deletions(-) create mode 100644 crates/ra_ide_api/src/snapshots/tests__highlighting.snap create mode 100644 crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap new file mode 100644 index 000000000..208681f10 --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap @@ -0,0 +1,192 @@ +--- +created: "2019-05-25T10:53:54.439877Z" +creator: insta@0.8.1 +source: crates/ra_ide_api/src/syntax_highlighting.rs +expression: result +--- +Ok( + [ + HighlightedRange { + range: [1; 24), + tag: "attribute", + id: None, + }, + HighlightedRange { + range: [25; 31), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [32; 35), + tag: "variable", + id: Some( + 461893210254723387, + ), + }, + HighlightedRange { + range: [42; 45), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [46; 47), + tag: "variable", + id: Some( + 8312289520117458465, + ), + }, + HighlightedRange { + range: [49; 52), + tag: "text", + id: None, + }, + HighlightedRange { + range: [58; 61), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [62; 63), + tag: "variable", + id: Some( + 4497542318236667727, + ), + }, + HighlightedRange { + range: [65; 68), + tag: "text", + id: None, + }, + HighlightedRange { + range: [73; 75), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [76; 79), + tag: "variable", + id: Some( + 4506850079084802999, + ), + }, + HighlightedRange { + range: [80; 81), + tag: "type", + id: None, + }, + HighlightedRange { + range: [80; 81), + tag: "variable", + id: Some( + 16968185728268100018, + ), + }, + HighlightedRange { + range: [88; 89), + tag: "type", + id: None, + }, + HighlightedRange { + range: [96; 110), + tag: "macro", + id: None, + }, + HighlightedRange { + range: [117; 127), + tag: "comment", + id: None, + }, + HighlightedRange { + range: [128; 130), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [131; 135), + tag: "variable", + id: Some( + 14467718814232352107, + ), + }, + HighlightedRange { + range: [145; 153), + tag: "macro", + id: None, + }, + HighlightedRange { + range: [154; 166), + tag: "string", + id: None, + }, + HighlightedRange { + range: [168; 170), + tag: "literal", + id: None, + }, + HighlightedRange { + range: [178; 181), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [182; 185), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [186; 189), + tag: "macro", + id: None, + }, + HighlightedRange { + range: [197; 200), + tag: "macro", + id: None, + }, + HighlightedRange { + range: [192; 195), + tag: "text", + id: None, + }, + HighlightedRange { + range: [208; 211), + tag: "macro", + id: None, + }, + HighlightedRange { + range: [212; 216), + tag: "macro", + id: None, + }, + HighlightedRange { + range: [226; 227), + tag: "literal", + id: None, + }, + HighlightedRange { + range: [232; 233), + tag: "literal", + id: None, + }, + HighlightedRange { + range: [242; 248), + tag: "keyword.unsafe", + id: None, + }, + HighlightedRange { + range: [251; 254), + tag: "text", + id: None, + }, + HighlightedRange { + range: [255; 262), + tag: "text", + id: None, + }, + HighlightedRange { + range: [263; 264), + tag: "literal", + id: None, + }, + ], +) diff --git a/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap new file mode 100644 index 000000000..3b3fe32e9 --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap @@ -0,0 +1,87 @@ +--- +created: "2019-05-25T10:25:13.898113Z" +creator: insta@0.8.1 +source: crates/ra_ide_api/src/syntax_highlighting.rs +expression: result +--- +Ok( + [ + HighlightedRange { + range: [1; 3), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [4; 8), + tag: "variable", + id: Some( + 17119830160611610240, + ), + }, + HighlightedRange { + range: [17; 20), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [21; 26), + tag: "variable", + id: Some( + 2744494144922727377, + ), + }, + HighlightedRange { + range: [29; 36), + tag: "string", + id: None, + }, + HighlightedRange { + range: [42; 45), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [46; 47), + tag: "variable", + id: Some( + 10375904121795371996, + ), + }, + HighlightedRange { + range: [50; 55), + tag: "variable", + id: Some( + 2744494144922727377, + ), + }, + HighlightedRange { + range: [56; 65), + tag: "text", + id: None, + }, + HighlightedRange { + range: [73; 76), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [77; 78), + tag: "variable", + id: Some( + 8228548264153724449, + ), + }, + HighlightedRange { + range: [81; 86), + tag: "variable", + id: Some( + 2744494144922727377, + ), + }, + HighlightedRange { + range: [87; 96), + tag: "text", + id: None, + }, + ], +) diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 87e053364..da000c0c3 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs @@ -10,6 +10,7 @@ use crate::{FileId, db::RootDatabase}; pub struct HighlightedRange { pub range: TextRange, pub tag: &'static str, + pub id: Option, } fn is_control_keyword(kind: SyntaxKind) -> bool { @@ -32,6 +33,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec(x: T) -> u64 { + use std::{collections::hash_map::DefaultHasher, hash::Hasher}; + + let mut hasher = DefaultHasher::new(); + x.hash(&mut hasher); + hasher.finish() + } + // Visited nodes to handle highlighting priorities let mut highlighted: FxHashSet = FxHashSet::default(); let mut res = Vec::new(); @@ -39,52 +48,59 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec "comment", - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", - ATTR => "attribute", + let (tag, id) = match node.kind() { + COMMENT => ("comment", None), + STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => ("string", None), + ATTR => ("attribute", None), NAME_REF => { - if let Some(name_ref) = node.as_node().and_then(|n| ast::NameRef::cast(n)) { + if let Some(name_ref) = node.as_ast_node::() { use crate::name_ref_kind::{classify_name_ref, NameRefKind::*}; use hir::{ModuleDef, ImplItem}; // FIXME: try to reuse the SourceAnalyzers let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); match classify_name_ref(db, &analyzer, name_ref) { - Some(Method(_)) => "function", - Some(Macro(_)) => "macro", - Some(FieldAccess(_)) => "field", - Some(AssocItem(ImplItem::Method(_))) => "function", - Some(AssocItem(ImplItem::Const(_))) => "constant", - Some(AssocItem(ImplItem::TypeAlias(_))) => "type", - Some(Def(ModuleDef::Module(_))) => "module", - Some(Def(ModuleDef::Function(_))) => "function", - Some(Def(ModuleDef::Struct(_))) => "type", - Some(Def(ModuleDef::Union(_))) => "type", - Some(Def(ModuleDef::Enum(_))) => "type", - Some(Def(ModuleDef::EnumVariant(_))) => "constant", - Some(Def(ModuleDef::Const(_))) => "constant", - Some(Def(ModuleDef::Static(_))) => "constant", - Some(Def(ModuleDef::Trait(_))) => "type", - Some(Def(ModuleDef::TypeAlias(_))) => "type", - Some(SelfType(_)) => "type", - Some(Pat(_)) => "text", - Some(SelfParam(_)) => "type", - Some(GenericParam(_)) => "type", - None => "text", + Some(Method(_)) => ("function", None), + Some(Macro(_)) => ("macro", None), + Some(FieldAccess(_)) => ("field", None), + Some(AssocItem(ImplItem::Method(_))) => ("function", None), + Some(AssocItem(ImplItem::Const(_))) => ("constant", None), + Some(AssocItem(ImplItem::TypeAlias(_))) => ("type", None), + Some(Def(ModuleDef::Module(_))) => ("module", None), + Some(Def(ModuleDef::Function(_))) => ("function", None), + Some(Def(ModuleDef::Struct(_))) => ("type", None), + Some(Def(ModuleDef::Union(_))) => ("type", None), + Some(Def(ModuleDef::Enum(_))) => ("type", None), + Some(Def(ModuleDef::EnumVariant(_))) => ("constant", None), + Some(Def(ModuleDef::Const(_))) => ("constant", None), + Some(Def(ModuleDef::Static(_))) => ("constant", None), + Some(Def(ModuleDef::Trait(_))) => ("type", None), + Some(Def(ModuleDef::TypeAlias(_))) => ("type", None), + Some(SelfType(_)) => ("type", None), + Some(Pat(ptr)) => ("variable", Some(hash(ptr.syntax_node_ptr().range()))), + Some(SelfParam(_)) => ("type", None), + Some(GenericParam(_)) => ("type", None), + None => ("text", None), } } else { - "text" + ("text", None) } } - NAME => "function", - TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type", - INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", - LIFETIME => "parameter", - T![unsafe] => "keyword.unsafe", - k if is_control_keyword(k) => "keyword.control", - k if k.is_keyword() => "keyword", + NAME => { + if let Some(name) = node.as_ast_node::() { + ("variable", Some(hash(name.syntax().range()))) + } else { + ("text", None) + } + } + TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => ("type", None), + INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => ("literal", None), + LIFETIME => ("parameter", None), + T![unsafe] => ("keyword.unsafe", None), + k if is_control_keyword(k) => ("keyword.control", None), + k if k.is_keyword() => ("keyword", None), _ => { + // let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) { if let Some(path) = macro_call.path() { if let Some(segment) = path.segment() { @@ -101,6 +117,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec Vec Date: Sat, 25 May 2019 12:56:52 +0200 Subject: Hash based on binding name and shadow counter --- .../src/snapshots/tests__highlighting.snap | 14 +-- .../src/snapshots/tests__rainbow_highlighting.snap | 128 +++++++++++++++++++++ .../src/snapshots/tests__sematic_highlighting.snap | 87 -------------- crates/ra_ide_api/src/syntax_highlighting.rs | 26 ++++- 4 files changed, 155 insertions(+), 100 deletions(-) create mode 100644 crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap delete mode 100644 crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap index 208681f10..e50003b3c 100644 --- a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap +++ b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap @@ -1,5 +1,5 @@ --- -created: "2019-05-25T10:53:54.439877Z" +created: "2019-05-25T11:24:53.486036Z" creator: insta@0.8.1 source: crates/ra_ide_api/src/syntax_highlighting.rs expression: result @@ -20,7 +20,7 @@ Ok( range: [32; 35), tag: "variable", id: Some( - 461893210254723387, + 8465336196764640996, ), }, HighlightedRange { @@ -32,7 +32,7 @@ Ok( range: [46; 47), tag: "variable", id: Some( - 8312289520117458465, + 176272420896316891, ), }, HighlightedRange { @@ -49,7 +49,7 @@ Ok( range: [62; 63), tag: "variable", id: Some( - 4497542318236667727, + 15061637676198917049, ), }, HighlightedRange { @@ -66,7 +66,7 @@ Ok( range: [76; 79), tag: "variable", id: Some( - 4506850079084802999, + 14077410872302487760, ), }, HighlightedRange { @@ -78,7 +78,7 @@ Ok( range: [80; 81), tag: "variable", id: Some( - 16968185728268100018, + 8379786015941272633, ), }, HighlightedRange { @@ -105,7 +105,7 @@ Ok( range: [131; 135), tag: "variable", id: Some( - 14467718814232352107, + 5766414492220109266, ), }, HighlightedRange { diff --git a/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap new file mode 100644 index 000000000..84cd521a2 --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap @@ -0,0 +1,128 @@ +--- +created: "2019-05-25T11:21:56.117898Z" +creator: insta@0.8.1 +source: crates/ra_ide_api/src/syntax_highlighting.rs +expression: result +--- +Ok( + [ + HighlightedRange { + range: [1; 3), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [4; 8), + tag: "variable", + id: Some( + 5766414492220109266, + ), + }, + HighlightedRange { + range: [17; 20), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [21; 26), + tag: "variable", + id: Some( + 15975256018338854530, + ), + }, + HighlightedRange { + range: [29; 36), + tag: "string", + id: None, + }, + HighlightedRange { + range: [42; 45), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [46; 47), + tag: "variable", + id: Some( + 176272420896316891, + ), + }, + HighlightedRange { + range: [50; 55), + tag: "variable", + id: Some( + 15975256018338854530, + ), + }, + HighlightedRange { + range: [56; 65), + tag: "text", + id: None, + }, + HighlightedRange { + range: [73; 76), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [77; 78), + tag: "variable", + id: Some( + 15061637676198917049, + ), + }, + HighlightedRange { + range: [81; 86), + tag: "variable", + id: Some( + 15975256018338854530, + ), + }, + HighlightedRange { + range: [87; 96), + tag: "text", + id: None, + }, + HighlightedRange { + range: [105; 108), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [109; 110), + tag: "variable", + id: Some( + 1714508680417729339, + ), + }, + HighlightedRange { + range: [113; 134), + tag: "string", + id: None, + }, + HighlightedRange { + range: [140; 143), + tag: "keyword", + id: None, + }, + HighlightedRange { + range: [144; 145), + tag: "variable", + id: Some( + 15953336624848413466, + ), + }, + HighlightedRange { + range: [148; 149), + tag: "variable", + id: Some( + 1714508680417729339, + ), + }, + HighlightedRange { + range: [150; 159), + tag: "text", + id: None, + }, + ], +) diff --git a/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap deleted file mode 100644 index 3b3fe32e9..000000000 --- a/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap +++ /dev/null @@ -1,87 +0,0 @@ ---- -created: "2019-05-25T10:25:13.898113Z" -creator: insta@0.8.1 -source: crates/ra_ide_api/src/syntax_highlighting.rs -expression: result ---- -Ok( - [ - HighlightedRange { - range: [1; 3), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [4; 8), - tag: "variable", - id: Some( - 17119830160611610240, - ), - }, - HighlightedRange { - range: [17; 20), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [21; 26), - tag: "variable", - id: Some( - 2744494144922727377, - ), - }, - HighlightedRange { - range: [29; 36), - tag: "string", - id: None, - }, - HighlightedRange { - range: [42; 45), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [46; 47), - tag: "variable", - id: Some( - 10375904121795371996, - ), - }, - HighlightedRange { - range: [50; 55), - tag: "variable", - id: Some( - 2744494144922727377, - ), - }, - HighlightedRange { - range: [56; 65), - tag: "text", - id: None, - }, - HighlightedRange { - range: [73; 76), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [77; 78), - tag: "variable", - id: Some( - 8228548264153724449, - ), - }, - HighlightedRange { - range: [81; 86), - tag: "variable", - id: Some( - 2744494144922727377, - ), - }, - HighlightedRange { - range: [87; 96), - tag: "text", - id: None, - }, - ], -) diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index da000c0c3..407fcda4a 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs @@ -1,6 +1,6 @@ -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashSet, FxHashMap}; -use ra_syntax::{ast, AstNode, TextRange, Direction, SyntaxKind, SyntaxKind::*, SyntaxElement, T}; +use ra_syntax::{ast, AstNode, TextRange, Direction, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxElement, T}; use ra_db::SourceDatabase; use ra_prof::profile; @@ -43,6 +43,8 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec = FxHashSet::default(); + let mut bindings_shadow_count: FxHashMap = FxHashMap::default(); + let mut res = Vec::new(); for node in source_file.syntax().descendants_with_tokens() { if highlighted.contains(&node) { @@ -77,7 +79,11 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec ("type", None), Some(Def(ModuleDef::TypeAlias(_))) => ("type", None), Some(SelfType(_)) => ("type", None), - Some(Pat(ptr)) => ("variable", Some(hash(ptr.syntax_node_ptr().range()))), + Some(Pat(ptr)) => ("variable", Some(hash({ + let text = ptr.syntax_node_ptr().to_node(&source_file.syntax()).text().to_smol_string(); + let shadow_count = bindings_shadow_count.entry(text.clone()).or_default(); + (text, shadow_count) + }))), Some(SelfParam(_)) => ("type", None), Some(GenericParam(_)) => ("type", None), None => ("text", None), @@ -88,7 +94,12 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec { if let Some(name) = node.as_ast_node::() { - ("variable", Some(hash(name.syntax().range()))) + ("variable", Some(hash({ + let text = name.syntax().text().to_smol_string(); + let shadow_count = bindings_shadow_count.entry(text.clone()).or_insert(1); + *shadow_count += 1; + (text, shadow_count) + }))) } else { ("text", None) } @@ -240,16 +251,19 @@ fn main() { } #[test] - fn test_sematic_highlighting() { + fn test_rainbow_highlighting() { let (analysis, file_id) = single_file( r#" 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 result = analysis.highlight(file_id); - assert_debug_snapshot_matches!("sematic_highlighting", result); + assert_debug_snapshot_matches!("rainbow_highlighting", result); } } -- cgit v1.2.3 From 43d5a4965308ec4b594725c0bd02cb046bdb730c Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Sat, 25 May 2019 16:23:58 +0200 Subject: More clever highlighting, incl draft for structs --- crates/ra_ide_api/src/lib.rs | 2 +- crates/ra_ide_api/src/snapshots/highlighting.html | 24 +-- .../src/snapshots/rainbow_highlighting.html | 27 +++ .../src/snapshots/tests__highlighting.snap | 192 --------------------- .../src/snapshots/tests__rainbow_highlighting.snap | 128 -------------- crates/ra_ide_api/src/syntax_highlighting.rs | 185 ++++++++++++-------- 6 files changed, 151 insertions(+), 407 deletions(-) create mode 100644 crates/ra_ide_api/src/snapshots/rainbow_highlighting.html delete mode 100644 crates/ra_ide_api/src/snapshots/tests__highlighting.snap delete mode 100644 crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index d3456d5b2..65a3b591a 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -464,7 +464,7 @@ impl Analysis { /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId) -> Cancelable { - self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id)) + self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, true)) } /// Computes completions at the given position. diff --git a/crates/ra_ide_api/src/snapshots/highlighting.html b/crates/ra_ide_api/src/snapshots/highlighting.html index bfc0a67b1..4f4ed62a1 100644 --- a/crates/ra_ide_api/src/snapshots/highlighting.html +++ b/crates/ra_ide_api/src/snapshots/highlighting.html @@ -1,10 +1,7 @@ -

-#[derive(Clone, Debug)]
+
#[derive(Clone, Debug)]
 struct Foo {
-    pub x: i32,
-    pub y: i32,
+    pub x: i32,
+    pub y: i32,
 }
 
 fn foo<T>() -> T {
@@ -36,10 +31,9 @@ pre {
 fn main() {
     println!("Hello, {}!", 92);
 
-    let mut vec = Vec::new();
+    let mut vec = Vec::new();
     if true {
-        vec.push(Foo { x: 0, y: 1 });
+        vec.push(Foo { x: 0, y: 1 });
     }
-    unsafe { vec.set_len(0); }
-}
-
\ No newline at end of file + unsafe { vec.set_len(0); } +}
\ No newline at end of file diff --git a/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html b/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html new file mode 100644 index 000000000..729d129d0 --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html @@ -0,0 +1,27 @@ + + +
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();
+}
\ No newline at end of file diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap deleted file mode 100644 index e50003b3c..000000000 --- a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap +++ /dev/null @@ -1,192 +0,0 @@ ---- -created: "2019-05-25T11:24:53.486036Z" -creator: insta@0.8.1 -source: crates/ra_ide_api/src/syntax_highlighting.rs -expression: result ---- -Ok( - [ - HighlightedRange { - range: [1; 24), - tag: "attribute", - id: None, - }, - HighlightedRange { - range: [25; 31), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [32; 35), - tag: "variable", - id: Some( - 8465336196764640996, - ), - }, - HighlightedRange { - range: [42; 45), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [46; 47), - tag: "variable", - id: Some( - 176272420896316891, - ), - }, - HighlightedRange { - range: [49; 52), - tag: "text", - id: None, - }, - HighlightedRange { - range: [58; 61), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [62; 63), - tag: "variable", - id: Some( - 15061637676198917049, - ), - }, - HighlightedRange { - range: [65; 68), - tag: "text", - id: None, - }, - HighlightedRange { - range: [73; 75), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [76; 79), - tag: "variable", - id: Some( - 14077410872302487760, - ), - }, - HighlightedRange { - range: [80; 81), - tag: "type", - id: None, - }, - HighlightedRange { - range: [80; 81), - tag: "variable", - id: Some( - 8379786015941272633, - ), - }, - HighlightedRange { - range: [88; 89), - tag: "type", - id: None, - }, - HighlightedRange { - range: [96; 110), - tag: "macro", - id: None, - }, - HighlightedRange { - range: [117; 127), - tag: "comment", - id: None, - }, - HighlightedRange { - range: [128; 130), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [131; 135), - tag: "variable", - id: Some( - 5766414492220109266, - ), - }, - HighlightedRange { - range: [145; 153), - tag: "macro", - id: None, - }, - HighlightedRange { - range: [154; 166), - tag: "string", - id: None, - }, - HighlightedRange { - range: [168; 170), - tag: "literal", - id: None, - }, - HighlightedRange { - range: [178; 181), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [182; 185), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [186; 189), - tag: "macro", - id: None, - }, - HighlightedRange { - range: [197; 200), - tag: "macro", - id: None, - }, - HighlightedRange { - range: [192; 195), - tag: "text", - id: None, - }, - HighlightedRange { - range: [208; 211), - tag: "macro", - id: None, - }, - HighlightedRange { - range: [212; 216), - tag: "macro", - id: None, - }, - HighlightedRange { - range: [226; 227), - tag: "literal", - id: None, - }, - HighlightedRange { - range: [232; 233), - tag: "literal", - id: None, - }, - HighlightedRange { - range: [242; 248), - tag: "keyword.unsafe", - id: None, - }, - HighlightedRange { - range: [251; 254), - tag: "text", - id: None, - }, - HighlightedRange { - range: [255; 262), - tag: "text", - id: None, - }, - HighlightedRange { - range: [263; 264), - tag: "literal", - id: None, - }, - ], -) diff --git a/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap deleted file mode 100644 index 84cd521a2..000000000 --- a/crates/ra_ide_api/src/snapshots/tests__rainbow_highlighting.snap +++ /dev/null @@ -1,128 +0,0 @@ ---- -created: "2019-05-25T11:21:56.117898Z" -creator: insta@0.8.1 -source: crates/ra_ide_api/src/syntax_highlighting.rs -expression: result ---- -Ok( - [ - HighlightedRange { - range: [1; 3), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [4; 8), - tag: "variable", - id: Some( - 5766414492220109266, - ), - }, - HighlightedRange { - range: [17; 20), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [21; 26), - tag: "variable", - id: Some( - 15975256018338854530, - ), - }, - HighlightedRange { - range: [29; 36), - tag: "string", - id: None, - }, - HighlightedRange { - range: [42; 45), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [46; 47), - tag: "variable", - id: Some( - 176272420896316891, - ), - }, - HighlightedRange { - range: [50; 55), - tag: "variable", - id: Some( - 15975256018338854530, - ), - }, - HighlightedRange { - range: [56; 65), - tag: "text", - id: None, - }, - HighlightedRange { - range: [73; 76), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [77; 78), - tag: "variable", - id: Some( - 15061637676198917049, - ), - }, - HighlightedRange { - range: [81; 86), - tag: "variable", - id: Some( - 15975256018338854530, - ), - }, - HighlightedRange { - range: [87; 96), - tag: "text", - id: None, - }, - HighlightedRange { - range: [105; 108), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [109; 110), - tag: "variable", - id: Some( - 1714508680417729339, - ), - }, - HighlightedRange { - range: [113; 134), - tag: "string", - id: None, - }, - HighlightedRange { - range: [140; 143), - tag: "keyword", - id: None, - }, - HighlightedRange { - range: [144; 145), - tag: "variable", - id: Some( - 15953336624848413466, - ), - }, - HighlightedRange { - range: [148; 149), - tag: "variable", - id: Some( - 1714508680417729339, - ), - }, - HighlightedRange { - range: [150; 159), - tag: "text", - id: None, - }, - ], -) diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 407fcda4a..8981c85e6 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs @@ -10,7 +10,7 @@ use crate::{FileId, db::RootDatabase}; pub struct HighlightedRange { pub range: TextRange, pub tag: &'static str, - pub id: Option, + pub binding_hash: Option, } fn is_control_keyword(kind: SyntaxKind) -> bool { @@ -30,15 +30,18 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec { let _p = profile("highlight"); - let source_file = db.parse(file_id); - fn hash(x: T) -> u64 { - use std::{collections::hash_map::DefaultHasher, hash::Hasher}; + fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 { + fn hash(x: T) -> u64 { + use std::{collections::hash_map::DefaultHasher, hash::Hasher}; + + let mut hasher = DefaultHasher::new(); + x.hash(&mut hasher); + hasher.finish() + } - let mut hasher = DefaultHasher::new(); - x.hash(&mut hasher); - hasher.finish() + hash((file_id, text, shadow_count)) } // Visited nodes to handle highlighting priorities @@ -50,66 +53,92 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec ("comment", None), - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => ("string", None), - ATTR => ("attribute", None), + let mut binding_hash = None; + let tag = match node.kind() { + COMMENT => "comment", + STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", + ATTR => "attribute", NAME_REF => { - if let Some(name_ref) = node.as_ast_node::() { + if let Some(name_ref) = node.as_node().and_then(ast::NameRef::cast) { use crate::name_ref_kind::{classify_name_ref, NameRefKind::*}; use hir::{ModuleDef, ImplItem}; // FIXME: try to reuse the SourceAnalyzers let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); match classify_name_ref(db, &analyzer, name_ref) { - Some(Method(_)) => ("function", None), - Some(Macro(_)) => ("macro", None), - Some(FieldAccess(_)) => ("field", None), - Some(AssocItem(ImplItem::Method(_))) => ("function", None), - Some(AssocItem(ImplItem::Const(_))) => ("constant", None), - Some(AssocItem(ImplItem::TypeAlias(_))) => ("type", None), - Some(Def(ModuleDef::Module(_))) => ("module", None), - Some(Def(ModuleDef::Function(_))) => ("function", None), - Some(Def(ModuleDef::Struct(_))) => ("type", None), - Some(Def(ModuleDef::Union(_))) => ("type", None), - Some(Def(ModuleDef::Enum(_))) => ("type", None), - Some(Def(ModuleDef::EnumVariant(_))) => ("constant", None), - Some(Def(ModuleDef::Const(_))) => ("constant", None), - Some(Def(ModuleDef::Static(_))) => ("constant", None), - Some(Def(ModuleDef::Trait(_))) => ("type", None), - Some(Def(ModuleDef::TypeAlias(_))) => ("type", None), - Some(SelfType(_)) => ("type", None), - Some(Pat(ptr)) => ("variable", Some(hash({ - let text = ptr.syntax_node_ptr().to_node(&source_file.syntax()).text().to_smol_string(); - let shadow_count = bindings_shadow_count.entry(text.clone()).or_default(); - (text, shadow_count) - }))), - Some(SelfParam(_)) => ("type", None), - Some(GenericParam(_)) => ("type", None), - None => ("text", None), + Some(Method(_)) => "function", + Some(Macro(_)) => "macro", + Some(FieldAccess(field)) => { + let (hir_file_id, src) = field.source(db); + if let hir::FieldSource::Named(name) = src { + let text = name.syntax().text().to_smol_string(); + let shadow_count = 0; // potentially even from different file + binding_hash = Some(calc_binding_hash(hir_file_id.original_file(db), &text, shadow_count)); + } + + "field" + }, + Some(AssocItem(ImplItem::Method(_))) => "function", + Some(AssocItem(ImplItem::Const(_))) => "constant", + Some(AssocItem(ImplItem::TypeAlias(_))) => "type", + Some(Def(ModuleDef::Module(_))) => "module", + Some(Def(ModuleDef::Function(_))) => "function", + Some(Def(ModuleDef::Struct(_))) => "type", + Some(Def(ModuleDef::Union(_))) => "type", + Some(Def(ModuleDef::Enum(_))) => "type", + Some(Def(ModuleDef::EnumVariant(_))) => "constant", + Some(Def(ModuleDef::Const(_))) => "constant", + Some(Def(ModuleDef::Static(_))) => "constant", + Some(Def(ModuleDef::Trait(_))) => "type", + Some(Def(ModuleDef::TypeAlias(_))) => "type", + Some(SelfType(_)) => "type", + Some(Pat(ptr)) => { + binding_hash = Some({ + let text = ptr.syntax_node_ptr().to_node(&source_file.syntax()).text().to_smol_string(); + let shadow_count = bindings_shadow_count.entry(text.clone()).or_default(); + calc_binding_hash(file_id, &text, *shadow_count) + }); + + "variable" + }, + Some(SelfParam(_)) => "type", + Some(GenericParam(_)) => "type", + None => "text", } } else { - ("text", None) + "text" } } NAME => { - if let Some(name) = node.as_ast_node::() { - ("variable", Some(hash({ - let text = name.syntax().text().to_smol_string(); - let shadow_count = bindings_shadow_count.entry(text.clone()).or_insert(1); - *shadow_count += 1; - (text, shadow_count) - }))) + if let Some(name) = node.as_node().and_then(ast::Name::cast) { + if name.syntax().ancestors().any(|x| ast::BindPat::cast(x).is_some()) { + binding_hash = Some({ + let text = name.syntax().text().to_smol_string(); + let shadow_count = bindings_shadow_count.entry(text.clone()).or_insert(0); + *shadow_count += 1; + calc_binding_hash(file_id, &text, *shadow_count) + }); + "variable" + } else if name.syntax().ancestors().any(|x| ast::NamedFieldDef::cast(x).is_some()) { + binding_hash = Some({ + let text = name.syntax().text().to_smol_string(); + let shadow_count = 0; + calc_binding_hash(file_id, &text, shadow_count) + }); + "variable" + } else { + "function" + } } else { - ("text", None) + "text" } } - TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => ("type", None), - INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => ("literal", None), - LIFETIME => ("parameter", None), - T![unsafe] => ("keyword.unsafe", None), - k if is_control_keyword(k) => ("keyword.control", None), - k if k.is_keyword() => ("keyword", None), + TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type", + INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", + LIFETIME => "parameter", + T![unsafe] => "keyword.unsafe", + k if is_control_keyword(k) => "keyword.control", + k if k.is_keyword() => "keyword", _ => { // let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) { @@ -128,7 +157,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec Vec String { +pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { let source_file = db.parse(file_id); + fn rainbowify(seed: u64) -> String { + use rand::prelude::*; + let mut rng = SmallRng::seed_from_u64(seed); + format!("hsl({h},{s}%,{l}%)", + h = rng.gen_range::(0, 361), + s = rng.gen_range::(42, 99), + l = rng.gen_range::(40, 91), + ) + } + let mut ranges = highlight(db, file_id); ranges.sort_by_key(|it| it.range.start()); // quick non-optimal heuristic to intersect token ranges and highlighted ranges @@ -166,16 +205,20 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId) -> String { } } let text = html_escape(&token.text()); - let classes = could_intersect + let ranges = could_intersect .iter() .filter(|it| token.range().is_subrange(&it.range)) - .map(|it| it.tag) .collect::>(); - if classes.is_empty() { + if ranges.is_empty() { buf.push_str(&text); } else { - let classes = classes.join(" "); - buf.push_str(&format!("{}", classes, text)); + let classes = ranges.iter().map(|x| x.tag).collect::>().join(" "); + let binding_hash = ranges.first().and_then(|x| x.binding_hash); + let color = match (rainbow, binding_hash) { + (true, Some(hash)) => format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)), + _ => "".into() + }; + buf.push_str(&format!("{}", classes, color, text)); } } buf.push_str(""); @@ -189,11 +232,8 @@ fn html_escape(text: &str) -> String { const STYLE: &str = " "; @@ -241,12 +280,12 @@ fn main() { } unsafe { vec.set_len(0); } } -"#, +"#.trim(), ); let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html"); let actual_html = &analysis.highlight_as_html(file_id).unwrap(); let expected_html = &read_text(&dst_file); - // std::fs::write(dst_file, &actual_html).unwrap(); + std::fs::write(dst_file, &actual_html).unwrap(); assert_eq_text!(expected_html, actual_html); } @@ -261,9 +300,13 @@ fn main() { let x = "other color please!"; let y = x.to_string(); -}"#, +} +"#.trim(), ); - let result = analysis.highlight(file_id); - assert_debug_snapshot_matches!("rainbow_highlighting", result); + let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/rainbow_highlighting.html"); + let actual_html = &analysis.highlight_as_html(file_id).unwrap(); + let expected_html = &read_text(&dst_file); + std::fs::write(dst_file, &actual_html).unwrap(); + assert_eq_text!(expected_html, actual_html); } } -- cgit v1.2.3 From 2b200f6e1a600e263f314efba5d8c3743095071b Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Sat, 25 May 2019 16:29:39 +0200 Subject: Disable broken struct field rainbowing --- crates/ra_ide_api/src/lib.rs | 6 +++--- crates/ra_ide_api/src/snapshots/highlighting.html | 6 +++--- crates/ra_ide_api/src/syntax_highlighting.rs | 18 +----------------- 3 files changed, 7 insertions(+), 23 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 65a3b591a..452407e8e 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -463,8 +463,8 @@ impl Analysis { } /// Computes syntax highlighting for the given file. - pub fn highlight_as_html(&self, file_id: FileId) -> Cancelable { - self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, true)) + pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancelable { + self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) } /// Computes completions at the given position. @@ -472,7 +472,7 @@ impl Analysis { self.with_db(|db| completion::completions(db, position).map(Into::into)) } - /// Computes assists (aks code actons aka intentions) for the given + /// Computes assists (aka code actions aka intentions) for the given /// position. pub fn assists(&self, frange: FileRange) -> Cancelable> { self.with_db(|db| assists::assists(db, frange)) diff --git a/crates/ra_ide_api/src/snapshots/highlighting.html b/crates/ra_ide_api/src/snapshots/highlighting.html index 4f4ed62a1..ebd187a35 100644 --- a/crates/ra_ide_api/src/snapshots/highlighting.html +++ b/crates/ra_ide_api/src/snapshots/highlighting.html @@ -19,8 +19,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4e
#[derive(Clone, Debug)]
 struct Foo {
-    pub x: i32,
-    pub y: i32,
+    pub x: i32,
+    pub y: i32,
 }
 
 fn foo<T>() -> T {
@@ -33,7 +33,7 @@ pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4e
 
     let mut vec = Vec::new();
     if true {
-        vec.push(Foo { x: 0, y: 1 });
+        vec.push(Foo { x: 0, y: 1 });
     }
     unsafe { vec.set_len(0); }
 }
\ No newline at end of file diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 8981c85e6..e46686ab9 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs @@ -68,16 +68,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec "function", Some(Macro(_)) => "macro", - Some(FieldAccess(field)) => { - let (hir_file_id, src) = field.source(db); - if let hir::FieldSource::Named(name) = src { - let text = name.syntax().text().to_smol_string(); - let shadow_count = 0; // potentially even from different file - binding_hash = Some(calc_binding_hash(hir_file_id.original_file(db), &text, shadow_count)); - } - - "field" - }, + Some(FieldAccess(_)) => "field", Some(AssocItem(ImplItem::Method(_))) => "function", Some(AssocItem(ImplItem::Const(_))) => "constant", Some(AssocItem(ImplItem::TypeAlias(_))) => "type", @@ -119,13 +110,6 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec Date: Sun, 26 May 2019 11:56:31 +0200 Subject: make it build again --- crates/ra_ide_api/src/syntax_highlighting.rs | 38 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index e46686ab9..dcefb0513 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs @@ -85,13 +85,18 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec "type", Some(Pat(ptr)) => { binding_hash = Some({ - let text = ptr.syntax_node_ptr().to_node(&source_file.syntax()).text().to_smol_string(); - let shadow_count = bindings_shadow_count.entry(text.clone()).or_default(); + let text = ptr + .syntax_node_ptr() + .to_node(&source_file.syntax()) + .text() + .to_smol_string(); + let shadow_count = + bindings_shadow_count.entry(text.clone()).or_default(); calc_binding_hash(file_id, &text, *shadow_count) }); "variable" - }, + } Some(SelfParam(_)) => "type", Some(GenericParam(_)) => "type", None => "text", @@ -105,7 +110,8 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec String { use rand::prelude::*; let mut rng = SmallRng::seed_from_u64(seed); - format!("hsl({h},{s}%,{l}%)", + format!( + "hsl({h},{s}%,{l}%)", h = rng.gen_range::(0, 361), s = rng.gen_range::(42, 99), l = rng.gen_range::(40, 91), @@ -199,8 +206,12 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo let classes = ranges.iter().map(|x| x.tag).collect::>().join(" "); let binding_hash = ranges.first().and_then(|x| x.binding_hash); let color = match (rainbow, binding_hash) { - (true, Some(hash)) => format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)), - _ => "".into() + (true, Some(hash)) => format!( + " data-binding-hash=\"{}\" style=\"color: {};\"", + hash, + rainbowify(hash) + ), + _ => "".into(), }; buf.push_str(&format!("{}", classes, color, text)); } @@ -264,10 +275,11 @@ fn main() { } unsafe { vec.set_len(0); } } -"#.trim(), +"# + .trim(), ); let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html"); - let actual_html = &analysis.highlight_as_html(file_id).unwrap(); + let actual_html = &analysis.highlight_as_html(file_id, true).unwrap(); let expected_html = &read_text(&dst_file); std::fs::write(dst_file, &actual_html).unwrap(); assert_eq_text!(expected_html, actual_html); @@ -285,10 +297,12 @@ fn main() { let x = "other color please!"; let y = x.to_string(); } -"#.trim(), +"# + .trim(), ); - let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/rainbow_highlighting.html"); - let actual_html = &analysis.highlight_as_html(file_id).unwrap(); + let dst_file = + project_dir().join("crates/ra_ide_api/src/snapshots/rainbow_highlighting.html"); + let actual_html = &analysis.highlight_as_html(file_id, true).unwrap(); let expected_html = &read_text(&dst_file); std::fs::write(dst_file, &actual_html).unwrap(); assert_eq_text!(expected_html, actual_html); -- cgit v1.2.3