Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Pressing enter only results in inserting a newline after more than half a minute
After installing the latest version of rust analyzer, I noticed that pressing Enter (either the default or numpad Enter) doesn't result in a newline. After a seemingly arbitrary delay of over 30 seconds, a new line is inserted after all.

I also tried copying and pasting a newline, which works without any trouble.

It seems like it is a regression from my previous install (last week). Currently I am on commit ` 91bd783273477d`.

Pull request description:
check for cancellation during macro expansion
closes #1331

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 68c5f78747..79e6329076 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1155,7 +1155,6 @@ dependencies = [
  "ra_prof 0.1.0",
  "ra_syntax 0.1.0",
  "ra_text_edit 0.1.0",
- "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 84a1564ceb..93aba4c704 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -16,10 +16,7 @@ fn main() -> Result<()> {
         .setting(clap::AppSettings::SubcommandRequiredElseHelp)
         .subcommand(SubCommand::with_name("parse").arg(Arg::with_name("no-dump").long("--no-dump")))
         .subcommand(SubCommand::with_name("symbols"))
-        .subcommand(
-            SubCommand::with_name("highlight")
-                .arg(Arg::with_name("rainbow").short("r").long("rainbow")),
-        )
+        .subcommand(SubCommand::with_name("highlight"))
         .subcommand(
             SubCommand::with_name("analysis-stats")
                 .arg(Arg::with_name("verbose").short("v").long("verbose"))
@@ -42,9 +39,9 @@ fn main() -> Result<()> {
                 println!("{:?}", s);
             }
         }
-        ("highlight", Some(matches)) => {
+        ("highlight", _) => {
             let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
-            let html = analysis.highlight_as_html(file_id, matches.is_present("rainbow")).unwrap();
+            let html = analysis.highlight_as_html(file_id).unwrap();
             println!("{}", html);
         }
         ("analysis-stats", Some(matches)) => {
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 2eb7f0da01..5c3799e95b 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -61,6 +61,7 @@ impl HirFileId {
         db: &impl DefDatabase,
         file_id: HirFileId,
     ) -> Option<TreeArc<SyntaxNode>> {
+        db.check_canceled();
         let _p = profile("parse_or_expand_query");
         match file_id.0 {
             HirFileIdRepr::File(file_id) => Some(db.parse(file_id).syntax().to_owned()),
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index 8939e9d79d..d399d5e2e0 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -15,7 +15,6 @@ rustc-hash = "1.0"
 parking_lot = "0.7.0"
 unicase = "2.2.0"
 superslice = "1.0.0"
-rand = "0.6.5"
 
 jemallocator = { version = "0.1.9", optional = true }
 jemalloc-ctl = { version = "0.2.0", optional = true }
diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs
index 2434f428f6..0e64abdbd4 100644
--- a/crates/ra_ide_api/src/change.rs
+++ b/crates/ra_ide_api/src/change.rs
@@ -156,6 +156,10 @@ impl RootDatabase {
     pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
         let _p = profile("RootDatabase::apply_change");
         log::info!("apply_change {:?}", change);
+        {
+            let _p = profile("RootDatabase::apply_change/cancellation");
+            self.salsa_runtime().next_revision();
+        }
         if !change.new_roots.is_empty() {
             let mut local_roots = Vec::clone(&self.local_roots());
             for (root_id, is_local) in change.new_roots {
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 452407e8e3..d3456d5b25 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, rainbow: bool) -> Cancelable<String> {
-        self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
+    pub fn highlight_as_html(&self, file_id: FileId) -> Cancelable<String> {
+        self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id))
     }
 
     /// 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 (aka code actions aka intentions) for the given
+    /// Computes assists (aks code actons aka intentions) for the given
     /// position.
     pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<Assist>> {
         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 ebd187a35d..bfc0a67b11 100644
--- a/crates/ra_ide_api/src/snapshots/highlighting.html
+++ b/crates/ra_ide_api/src/snapshots/highlighting.html
@@ -1,7 +1,10 @@
 
 <style>
-body       { margin: 0; }
-pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+pre {
+    color: #DCDCCC;
+    background-color: #3F3F3F;
+    font-size: 22px;
+}
 
 .comment   { color: #7F9F7F; }
 .string    { color: #CC9393; }
@@ -16,8 +19,10 @@ pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4e
 .keyword           { color: #F0DFAF; }
 .keyword\.unsafe  { color: #F0DFAF; font-weight: bold; }
 .keyword\.control { color: #DC8CC3; }
+
 </style>
-<pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span>
+<pre><code>
+<span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span>
 <span class="keyword">struct</span> <span class="function">Foo</span> {
     <span class="keyword">pub</span> <span class="function">x</span>: <span class="text">i32</span>,
     <span class="keyword">pub</span> <span class="function">y</span>: <span class="text">i32</span>,
@@ -31,9 +36,10 @@ pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4e
 <span class="keyword">fn</span> <span class="function">main</span>() {
     <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal">92</span>);
 
-    <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable" data-binding-hash="9636295041291189729" style="color: hsl(51,57%,74%);">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>();
+    <span class="keyword">let</span> <span class="keyword">mut</span> <span class="function">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>();
     <span class="keyword.control">if</span> <span class="keyword">true</span> {
-        <span class="variable" data-binding-hash="8496027264380925433" style="color: hsl(18,48%,55%);">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>: <span class="literal">0</span>, <span class="field">y</span>: <span class="literal">1</span> });
+        <span class="text">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>: <span class="literal">0</span>, <span class="field">y</span>: <span class="literal">1</span> });
     }
-    <span class="keyword.unsafe">unsafe</span> { <span class="variable" data-binding-hash="8496027264380925433" style="color: hsl(18,48%,55%);">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); }
-}</code></pre>
\ No newline at end of file
+    <span class="keyword.unsafe">unsafe</span> { <span class="text">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); }
+}
+</code></pre>
\ 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
deleted file mode 100644
index 729d129d07..0000000000
--- a/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-<style>
-body       { margin: 0; }
-pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
-
-.comment   { color: #7F9F7F; }
-.string    { color: #CC9393; }
-.function  { color: #93E0E3; }
-.parameter { color: #94BFF3; }
-.builtin   { color: #DD6718; }
-.text      { color: #DCDCCC; }
-.attribute { color: #BFEBBF; }
-.literal   { color: #DFAF8F; }
-.macro     { color: #DFAF8F; }
-
-.keyword           { color: #F0DFAF; }
-.keyword\.unsafe  { color: #F0DFAF; font-weight: bold; }
-.keyword\.control { color: #DC8CC3; }
-</style>
-<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span> = <span class="string">"hello"</span>;
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="5695551762718493399" style="color: hsl(272,48%,45%);">x</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>();
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="5435401749617022797" style="color: hsl(353,77%,74%);">y</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>();
-
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span> = <span class="string">"other color please!"</span>;
-    <span class="keyword">let</span> <span class="variable" data-binding-hash="14878783531007968800" style="color: hsl(265,73%,83%);">y</span> = <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span>.<span class="text">to_string</span>();
-}</code></pre>
\ 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 dcefb05134..87e053364c 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, FxHashMap};
+use rustc_hash::FxHashSet;
 
-use ra_syntax::{ast, AstNode, TextRange, Direction, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxElement, T};
+use ra_syntax::{ast, AstNode, TextRange, Direction, SyntaxKind, SyntaxKind::*, SyntaxElement, T};
 use ra_db::SourceDatabase;
 use ra_prof::profile;
 
@@ -10,7 +10,6 @@ use crate::{FileId, db::RootDatabase};
 pub struct HighlightedRange {
     pub range: TextRange,
     pub tag: &'static str,
-    pub binding_hash: Option<u64>,
 }
 
 fn is_control_keyword(kind: SyntaxKind) -> bool {
@@ -30,36 +29,22 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
 
 pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
     let _p = profile("highlight");
-    let source_file = db.parse(file_id);
-
-    fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
-        fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
-            use std::{collections::hash_map::DefaultHasher, hash::Hasher};
-
-            let mut hasher = DefaultHasher::new();
-            x.hash(&mut hasher);
-            hasher.finish()
-        }
 
-        hash((file_id, text, shadow_count))
-    }
+    let source_file = db.parse(file_id);
 
     // Visited nodes to handle highlighting priorities
     let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
-    let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default();
-
     let mut res = Vec::new();
     for node in source_file.syntax().descendants_with_tokens() {
         if highlighted.contains(&node) {
             continue;
         }
-        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_node().and_then(ast::NameRef::cast) {
+                if let Some(name_ref) = node.as_node().and_then(|n| ast::NameRef::cast(n)) {
                     use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
                     use hir::{ModuleDef, ImplItem};
 
@@ -83,20 +68,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                         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(Pat(_)) => "text",
                         Some(SelfParam(_)) => "type",
                         Some(GenericParam(_)) => "type",
                         None => "text",
@@ -105,24 +77,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                     "text"
                 }
             }
-            NAME => {
-                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 {
-                        "function"
-                    }
-                } else {
-                    "text"
-                }
-            }
+            NAME => "function",
             TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type",
             INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
             LIFETIME => "parameter",
@@ -130,7 +85,6 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
             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) {
                     if let Some(path) = macro_call.path() {
                         if let Some(segment) = path.segment() {
@@ -147,7 +101,6 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                                 res.push(HighlightedRange {
                                     range: TextRange::from_to(range_start, range_end),
                                     tag: "macro",
-                                    binding_hash: None,
                                 })
                             }
                         }
@@ -156,25 +109,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                 continue;
             }
         };
-        res.push(HighlightedRange { range: node.range(), tag, binding_hash })
+        res.push(HighlightedRange { range: node.range(), tag })
     }
     res
 }
 
-pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
+pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId) -> 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::<u16, _, _>(0, 361),
-            s = rng.gen_range::<u16, _, _>(42, 99),
-            l = rng.gen_range::<u16, _, _>(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
@@ -196,24 +138,16 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
             }
         }
         let text = html_escape(&token.text());
-        let ranges = could_intersect
+        let classes = could_intersect
             .iter()
             .filter(|it| token.range().is_subrange(&it.range))
+            .map(|it| it.tag)
             .collect::<Vec<_>>();
-        if ranges.is_empty() {
+        if classes.is_empty() {
             buf.push_str(&text);
         } else {
-            let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().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!("<span class=\"{}\"{}>{}</span>", classes, color, text));
+            let classes = classes.join(" ");
+            buf.push_str(&format!("<span class=\"{}\">{}</span>", classes, text));
         }
     }
     buf.push_str("</code></pre>");
@@ -227,8 +161,11 @@ fn html_escape(text: &str) -> String {
 
 const STYLE: &str = "
 <style>
-body       { margin: 0; }
-pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+pre {
+    color: #DCDCCC;
+    background-color: #3F3F3F;
+    font-size: 22px;
+}
 
 .comment   { color: #7F9F7F; }
 .string    { color: #CC9393; }
@@ -243,6 +180,7 @@ pre        { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4e
 .keyword           { color: #F0DFAF; }
 .keyword\\.unsafe  { color: #F0DFAF; font-weight: bold; }
 .keyword\\.control { color: #DC8CC3; }
+
 </style>
 ";
 
@@ -275,36 +213,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, true).unwrap();
-        let expected_html = &read_text(&dst_file);
-        std::fs::write(dst_file, &actual_html).unwrap();
-        assert_eq_text!(expected_html, actual_html);
-    }
-
-    #[test]
-    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();
-}
-"#
-            .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, true).unwrap();
+        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);
     }
 }
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index e36db12b32..a82ae696b6 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -872,11 +872,7 @@ fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> {
         .analysis()
         .highlight(file_id)?
         .into_iter()
-        .map(|h| Decoration {
-            range: h.range.conv_with(&line_index),
-            tag: h.tag,
-            binding_hash: h.binding_hash.map(|x| x.to_string()),
-        })
+        .map(|h| Decoration { range: h.range.conv_with(&line_index), tag: h.tag })
         .collect();
     Ok(res)
 }
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 992c24eac3..6090eb7b92 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -129,7 +129,6 @@ pub struct PublishDecorationsParams {
 pub struct Decoration {
     pub range: Range,
     pub tag: &'static str,
-    pub binding_hash: Option<String>,
 }
 
 pub enum ParentModule {}
diff --git a/crates/ra_syntax/src/syntax_text.rs b/crates/ra_syntax/src/syntax_text.rs
index c9038cd5c0..b013164c49 100644
--- a/crates/ra_syntax/src/syntax_text.rs
+++ b/crates/ra_syntax/src/syntax_text.rs
@@ -1,6 +1,6 @@
 use std::{fmt, ops::{self, Bound}};
 
-use crate::{SmolStr, SyntaxNode, TextRange, TextUnit, SyntaxElement};
+use crate::{SyntaxNode, TextRange, TextUnit, SyntaxElement};
 
 #[derive(Clone)]
 pub struct SyntaxText<'a> {
@@ -34,12 +34,6 @@ impl<'a> SyntaxText<'a> {
         self.chunks().collect()
     }
 
-    pub fn to_smol_string(&self) -> SmolStr {
-        // FIXME: use `self.chunks().collect()` here too once
-        // https://github.com/matklad/smol_str/pull/12 is merged and published
-        self.to_string().into()
-    }
-
     pub fn contains(&self, c: char) -> bool {
         self.chunks().any(|it| it.contains(c))
     }
diff --git a/docs/user/features.md b/docs/user/features.md
index b6e6008c42..22470bc563 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -470,12 +470,3 @@ There also snippet completions:
 
 - `tfn` -> `#[test] fn f(){}`
 
-### Code highlighting
-
-Experimental feature to let rust-analyzer highlight Rust code instead of using the
-default highlighter.
-
-#### Rainbow highlighting
-
-Experimental feature that, given code highlighting using rust-analyzer is
-active, will pick unique colors for identifiers.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 6b3a12f91a..29cd260a4c 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -36,11 +36,6 @@
             "integrity": "sha512-Ja7d4s0qyGFxjGeDq5S7Si25OFibSAHUi6i17UWnwNnpitADN7hah9q0Tl25gxuV5R1u2Bx+np6w4LHXfHyj/g==",
             "dev": true
         },
-        "@types/seedrandom": {
-            "version": "2.4.28",
-            "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.28.tgz",
-            "integrity": "sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA=="
-        },
         "agent-base": {
             "version": "4.2.1",
             "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
@@ -989,11 +984,6 @@
             "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
             "dev": true
         },
-        "seedrandom": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.1.tgz",
-            "integrity": "sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg=="
-        },
         "semver": {
             "version": "5.7.0",
             "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index 05c8083949..cde5fbcb8f 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -31,13 +31,11 @@
         "singleQuote": true
     },
     "dependencies": {
-        "seedrandom": "^3.0.1",
         "vscode-languageclient": "^5.3.0-next.4"
     },
     "devDependencies": {
         "@types/mocha": "^5.2.6",
         "@types/node": "^10.14.5",
-        "@types/seedrandom": "^2.4.28",
         "prettier": "^1.17.0",
         "shx": "^0.3.1",
         "tslint": "^5.16.0",
@@ -164,11 +162,6 @@
                     "default": false,
                     "description": "Highlight Rust code (overrides built-in syntax highlighting)"
                 },
-                "rust-analyzer.rainbowHighlightingOn": {
-                    "type": "boolean",
-                    "default": false,
-                    "description": "When highlighting Rust code, use a unique color per identifier"
-                },
                 "rust-analyzer.showWorkspaceLoadedNotification": {
                     "type": "boolean",
                     "default": true,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 8d73a6b340..481a5e5f18 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -15,7 +15,6 @@ export interface CargoWatchOptions {
 
 export class Config {
     public highlightingOn = true;
-    public rainbowHighlightingOn = false;
     public enableEnhancedTyping = true;
     public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server';
     public showWorkspaceLoadedNotification = true;
@@ -40,12 +39,6 @@ export class Config {
             this.highlightingOn = config.get('highlightingOn') as boolean;
         }
 
-        if (config.has('rainbowHighlightingOn')) {
-            this.rainbowHighlightingOn = config.get(
-                'rainbowHighlightingOn'
-            ) as boolean;
-        }
-
         if (config.has('showWorkspaceLoadedNotification')) {
             this.showWorkspaceLoadedNotification = config.get(
                 'showWorkspaceLoadedNotification'
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index 52a0bd4bb1..8389d94b8c 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,4 +1,3 @@
-import seedrandom = require('seedrandom');
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
 
@@ -7,20 +6,6 @@ import { Server } from './server';
 export interface Decoration {
     range: lc.Range;
     tag: string;
-    bindingHash?: string;
-}
-
-// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
-function fancify(seed: string, shade: 'light' | 'dark') {
-    const random = seedrandom(seed);
-    const randomInt = (min: number, max: number) => {
-        return Math.floor(random() * (max - min + 1)) + min;
-    };
-
-    const h = randomInt(0, 360);
-    const s = randomInt(42, 98);
-    const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
-    return `hsl(${h},${s}%,${l}%)`;
 }
 
 export class Highlighter {
@@ -91,9 +76,6 @@ export class Highlighter {
         }
 
         const byTag: Map<string, vscode.Range[]> = new Map();
-        const colorfulIdents: Map<string, vscode.Range[]> = new Map();
-        const rainbowTime = Server.config.rainbowHighlightingOn;
-
         for (const tag of this.decorations.keys()) {
             byTag.set(tag, []);
         }
@@ -102,23 +84,9 @@ export class Highlighter {
             if (!byTag.get(d.tag)) {
                 continue;
             }
-
-            if (rainbowTime && d.bindingHash) {
-                if (!colorfulIdents.has(d.bindingHash)) {
-                    colorfulIdents.set(d.bindingHash, []);
-                }
-                colorfulIdents
-                    .get(d.bindingHash)!
-                    .push(
-                        Server.client.protocol2CodeConverter.asRange(d.range)
-                    );
-            } else {
-                byTag
-                    .get(d.tag)!
-                    .push(
-                        Server.client.protocol2CodeConverter.asRange(d.range)
-                    );
-            }
+            byTag
+                .get(d.tag)!
+                .push(Server.client.protocol2CodeConverter.asRange(d.range));
         }
 
         for (const tag of byTag.keys()) {
@@ -128,13 +96,5 @@ export class Highlighter {
             const ranges = byTag.get(tag)!;
             editor.setDecorations(dec, ranges);
         }
-
-        for (const [hash, ranges] of colorfulIdents.entries()) {
-            const dec = vscode.window.createTextEditorDecorationType({
-                light: { color: fancify(hash, 'light') },
-                dark: { color: fancify(hash, 'dark') }
-            });
-            editor.setDecorations(dec, ranges);
-        }
     }
 }


Test patch:
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs
index 6f37a980db..f61048aaf0 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/main.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs
@@ -6,10 +6,11 @@ use std::{
 };
 
 use lsp_types::{
-    CodeActionContext, DocumentFormattingParams, FormattingOptions, Position, Range,
+    CodeActionContext, DocumentFormattingParams, FormattingOptions, Position, Range, DidOpenTextDocumentParams, TextDocumentItem, TextDocumentPositionParams
 };
 use ra_lsp_server::req::{
     CodeActionParams, CodeActionRequest, Formatting, Runnables, RunnablesParams, CompletionParams, Completion,
+    DidOpenTextDocument, OnEnter,
 };
 use serde_json::json;
 use tempfile::TempDir;
@@ -17,6 +18,7 @@ use tempfile::TempDir;
 use crate::support::{project, Project};
 
 const LOG: &'static str = "";
+const PROFILE: &'static str = "*@3>100";
 
 #[test]
 fn completes_items_from_standard_library() {
@@ -341,3 +343,68 @@ fn main() {{}}
         json!([]),
     );
 }
+
+#[test]
+fn diagnostics_dont_block_typing() {
+    let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
+    let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
+    let server = project(&format!(
+        r#"
+//- Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- src/lib.rs
+{}
+
+{}
+
+fn main() {{}}
+"#,
+        librs, libs
+    ));
+    server.wait_until_workspace_is_loaded();
+    for i in 0..10 {
+        server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
+            text_document: TextDocumentItem {
+                uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
+                language_id: "rust".to_string(),
+                version: 0,
+                text: "/// Docs\nfn foo() {}".to_string(),
+            },
+        });
+    }
+    let start = std::time::Instant::now();
+    server.request::<OnEnter>(
+        TextDocumentPositionParams {
+            text_document: server.doc_id("src/m0.rs"),
+            position: Position { line: 0, character: 5 },
+        },
+        json!({
+          "cursorPosition": {
+            "position": { "character": 4, "line": 1 },
+            "textDocument": { "uri": "file:///[..]src/m0.rs" }
+          },
+          "label": "on enter",
+          "workspaceEdit": {
+            "documentChanges": [
+              {
+                "edits": [
+                  {
+                    "newText": "\n/// ",
+                    "range": {
+                      "end": { "character": 5, "line": 0 },
+                      "start": { "character": 5, "line": 0 }
+                    }
+                  }
+                ],
+                "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
+              }
+            ]
+          }
+        }),
+    );
+    let elapsed = start.elapsed();
+    assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
+}
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs
index d68182174b..729067395f 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/support.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs
@@ -52,6 +52,11 @@ impl<'a> Project<'a> {
         static INIT: Once = Once::new();
         INIT.call_once(|| {
             let _ = Logger::with_env_or_str(crate::LOG).start().unwrap();
+            ra_prof::set_filter(if crate::PROFILE.is_empty() {
+                ra_prof::Filter::disabled()
+            } else {
+                ra_prof::Filter::from_spec(&crate::PROFILE)
+            });
         });
 
         let mut paths = vec![];
@@ -121,6 +126,15 @@ impl Server {
         TextDocumentIdentifier { uri: Url::from_file_path(path).unwrap() }
     }
 
+    pub fn notification<N>(&self, params: N::Params)
+    where
+        N: Notification,
+        N::Params: Serialize,
+    {
+        let r = RawNotification::new::<N>(&params);
+        self.send_notification(r)
+    }
+
     pub fn request<R>(&self, params: R::Params, expected_resp: Value)
     where
         R: Request,