Repository: rust-analyzer/rust-analyzer
Pull request #3765 (2020-03-29 13:43:52)

Adds sort for RecordLit comparison in SSR
an item from #3186 

edwin0cheng: LGTM with a nit, but personally I don't like multiple long inline functions in rust but we could refactor it later on.

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index fb7afe9fdc..5050c558cb 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -11,7 +11,6 @@ env:
   CARGO_INCREMENTAL: 0
   CARGO_NET_RETRY: 10
   RUN_SLOW_TESTS: 1
-  RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   RUSTUP_MAX_RETRIES: 10
 
diff --git a/Cargo.lock b/Cargo.lock
index 9a1a056830..8d81c48398 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,9 +11,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.28"
+version = "1.0.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
+checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"
 
 [[package]]
 name = "anymap"
@@ -435,9 +435,9 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.10"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
+checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
 dependencies = [
  "libc",
 ]
@@ -563,9 +563,9 @@ dependencies = [
 
 [[package]]
 name = "jod-thread"
-version = "0.1.1"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4022656272c3e564a7cdebcaaba6518d844b0d0c1836597196efb5bfeb98bb49"
+checksum = "2f52a11f73b88fab829a0e4d9e13ea5982c7ac457c72eb3541d82a4afdfce4ff"
 
 [[package]]
 name = "kernel32-sys"
@@ -792,9 +792,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "0.1.9"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "092d791bf7847f70bbd49085489fba25fc2c193571752bff9e36e74e72403932"
+checksum = "8292c1e1e81ddb552c4c90c36af201a0ce7e34995f55f0480f01052f242811c9"
 dependencies = [
  "paste-impl",
  "proc-macro-hack",
@@ -802,9 +802,9 @@ dependencies = [
 
 [[package]]
 name = "paste-impl"
-version = "0.1.9"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "406c23fb4c45cc6f68a9bbabb8ec7bd6f8cfcbd17e9e8f72c2460282f8325729"
+checksum = "5e9c43f2645f06ee452544ad032886a75f3d1797b9487dcadcae9100ba58a51c"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -848,9 +848,9 @@ checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.10"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
+checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
 dependencies = [
  "unicode-xid",
 ]
@@ -886,6 +886,18 @@ dependencies = [
  "test_utils",
 ]
 
+[[package]]
+name = "ra_cargo_watch"
+version = "0.1.0"
+dependencies = [
+ "cargo_metadata",
+ "crossbeam-channel",
+ "insta",
+ "log",
+ "lsp-types",
+ "serde_json",
+]
+
 [[package]]
 name = "ra_cfg"
 version = "0.1.0"
@@ -910,19 +922,6 @@ dependencies = [
  "test_utils",
 ]
 
-[[package]]
-name = "ra_flycheck"
-version = "0.1.0"
-dependencies = [
- "cargo_metadata",
- "crossbeam-channel",
- "insta",
- "jod-thread",
- "log",
- "lsp-types",
- "serde_json",
-]
-
 [[package]]
 name = "ra_fmt"
 version = "0.1.0"
@@ -1075,12 +1074,7 @@ dependencies = [
 name = "ra_proc_macro"
 version = "0.1.0"
 dependencies = [
- "crossbeam-channel",
- "jod-thread",
- "log",
  "ra_tt",
- "serde",
- "serde_json",
 ]
 
 [[package]]
@@ -1101,6 +1095,7 @@ dependencies = [
  "cargo_metadata",
  "log",
  "ra_arena",
+ "ra_cargo_watch",
  "ra_cfg",
  "ra_db",
  "ra_proc_macro",
@@ -1299,8 +1294,8 @@ dependencies = [
  "lsp-types",
  "parking_lot",
  "pico-args",
+ "ra_cargo_watch",
  "ra_db",
- "ra_flycheck",
  "ra_hir",
  "ra_hir_def",
  "ra_hir_ty",
@@ -1430,9 +1425,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.50"
+version = "1.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867"
+checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
 dependencies = [
  "itoa",
  "ryu",
@@ -1655,9 +1650,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.4"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e"
+checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
 dependencies = [
  "winapi 0.3.8",
 ]
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index 0f33907c0a..5e764113d2 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -14,8 +14,14 @@ pub struct ArenaMap<ID, V> {
 impl<T, V> ArenaMap<Idx<T>, V> {
     pub fn insert(&mut self, id: Idx<T>, t: V) {
         let idx = Self::to_idx(id);
-
-        self.v.resize_with((idx + 1).max(self.v.len()), || None);
+        if self.v.capacity() <= idx {
+            self.v.reserve(idx + 1 - self.v.capacity());
+        }
+        if self.v.len() <= idx {
+            while self.v.len() <= idx {
+                self.v.push(None);
+            }
+        }
         self.v[idx] = Some(t);
     }
 
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 8d1af99336..add82e5b1b 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -1,11 +1,15 @@
+//! FIXME: write short doc here
+
 use std::iter;
 
 use hir::{Adt, HasSource, ModuleDef, Semantics};
 use itertools::Itertools;
 use ra_ide_db::RootDatabase;
-use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
 
 use crate::{Assist, AssistCtx, AssistId};
+use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
+
+use ast::{MatchArm, Pat};
 
 // Assist: fill_match_arms
 //
@@ -93,7 +97,10 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
     }
 
     ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
-        let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
+        arms.extend(missing_arms);
+
+        let indent_level = IndentLevel::from_node(match_arm_list.syntax());
+        let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));
 
         edit.target(match_expr.syntax().text_range());
         edit.set_cursor(expr.syntax().text_range().start());
@@ -648,93 +655,4 @@ mod tests {
             "#,
         );
     }
-
-    #[test]
-    fn fill_match_arms_preserves_comments() {
-        check_assist(
-            fill_match_arms,
-            r#"
-            enum A {
-                One,
-                Two,
-            }
-            fn foo(a: A) {
-                match a {
-                    // foo bar baz<|>
-                    A::One => {}
-                    // This is where the rest should be
-                }
-            }
-            "#,
-            r#"
-            enum A {
-                One,
-                Two,
-            }
-            fn foo(a: A) {
-                match <|>a {
-                    // foo bar baz
-                    A::One => {}
-                    // This is where the rest should be
-                    A::Two => {}
-                }
-            }
-            "#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_preserves_comments_empty() {
-        check_assist(
-            fill_match_arms,
-            r#"
-            enum A {
-                One,
-                Two,
-            }
-            fn foo(a: A) {
-                match a {
-                    // foo bar baz<|>
-                }
-            }
-            "#,
-            r#"
-            enum A {
-                One,
-                Two,
-            }
-            fn foo(a: A) {
-                match <|>a {
-                    // foo bar baz
-                    A::One => {}
-                    A::Two => {}
-                }
-            }
-            "#,
-        );
-    }
-
-    #[test]
-    fn fill_match_arms_placeholder() {
-        check_assist(
-            fill_match_arms,
-            r#"
-            enum A { One, Two, }
-            fn foo(a: A) {
-                match a<|> {
-                    _ => (),
-                }
-            }
-            "#,
-            r#"
-            enum A { One, Two, }
-            fn foo(a: A) {
-                match <|>a {
-                    A::One => {}
-                    A::Two => {}
-                }
-            }
-            "#,
-        );
-    }
 }
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml
similarity index 87%
rename from crates/ra_flycheck/Cargo.toml
rename to crates/ra_cargo_watch/Cargo.toml
index c9a9ddc122..741345a215 100644
--- a/crates/ra_flycheck/Cargo.toml
+++ b/crates/ra_cargo_watch/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 edition = "2018"
-name = "ra_flycheck"
+name = "ra_cargo_watch"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 
@@ -10,7 +10,6 @@ lsp-types = { version = "0.73.0", features = ["proposed"] }
 log = "0.4.8"
 cargo_metadata = "0.9.1"
 serde_json = "1.0.48"
-jod-thread = "0.1.1"
 
 [dev-dependencies]
 insta = "0.15.0"
diff --git a/crates/ra_flycheck/src/conv/test.rs b/crates/ra_cargo_watch/src/conv/test.rs
similarity index 100%
rename from crates/ra_flycheck/src/conv/test.rs
rename to crates/ra_cargo_watch/src/conv/test.rs
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
similarity index 63%
rename from crates/ra_flycheck/src/lib.rs
rename to crates/ra_cargo_watch/src/lib.rs
index f6f9171add..7c525c4300 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -1,29 +1,29 @@
 //! cargo_check provides the functionality needed to run `cargo check` or
 //! another compatible command (f.x. clippy) in a background thread and provide
 //! LSP diagnostics based on the output of the command.
-mod conv;
-
+use cargo_metadata::Message;
+use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
+use lsp_types::{
+    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
+    WorkDoneProgressEnd, WorkDoneProgressReport,
+};
 use std::{
     error, fmt,
     io::{BufRead, BufReader},
     path::{Path, PathBuf},
     process::{Command, Stdio},
+    thread::JoinHandle,
     time::Instant,
 };
 
-use cargo_metadata::Message;
-use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
-use lsp_types::{
-    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
-    WorkDoneProgressEnd, WorkDoneProgressReport,
-};
+mod conv;
 
 use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
 
 pub use crate::conv::url_from_path_with_drive_lowercasing;
 
 #[derive(Clone, Debug)]
-pub struct CheckConfig {
+pub struct CheckOptions {
     pub enable: bool,
     pub args: Vec<String>,
     pub command: String,
@@ -36,26 +36,50 @@ pub struct CheckConfig {
 /// The spawned thread is shut down when this struct is dropped.
 #[derive(Debug)]
 pub struct CheckWatcher {
-    // XXX: drop order is significant
-    cmd_send: Sender<CheckCommand>,
-    handle: Option<jod_thread::JoinHandle<()>>,
     pub task_recv: Receiver<CheckTask>,
+    cmd_send: Option<Sender<CheckCommand>>,
+    handle: Option<JoinHandle<()>>,
 }
 
 impl CheckWatcher {
-    pub fn new(config: CheckConfig, workspace_root: PathBuf) -> CheckWatcher {
+    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
+        let options = options.clone();
+
         let (task_send, task_recv) = unbounded::<CheckTask>();
         let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
-        let handle = jod_thread::spawn(move || {
-            let mut check = CheckWatcherThread::new(config, workspace_root);
+        let handle = std::thread::spawn(move || {
+            let mut check = CheckWatcherThread::new(options, workspace_root);
             check.run(&task_send, &cmd_recv);
         });
-        CheckWatcher { task_recv, cmd_send, handle: Some(handle) }
+        CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle) }
+    }
+
+    /// Returns a CheckWatcher that doesn't actually do anything
+    pub fn dummy() -> CheckWatcher {
+        CheckWatcher { task_recv: never(), cmd_send: None, handle: None }
     }
 
     /// Schedule a re-start of the cargo check worker.
     pub fn update(&self) {
-        self.cmd_send.send(CheckCommand::Update).unwrap();
+        if let Some(cmd_send) = &self.cmd_send {
+            cmd_send.send(CheckCommand::Update).unwrap();
+        }
+    }
+}
+
+impl std::ops::Drop for CheckWatcher {
+    fn drop(&mut self) {
+        if let Some(handle) = self.handle.take() {
+            // Take the sender out of the option
+            let cmd_send = self.cmd_send.take();
+
+            // Dropping the sender finishes the thread loop
+            drop(cmd_send);
+
+            // Join the thread, it should finish shortly. We don't really care
+            // whether it panicked, so it is safe to ignore the result
+            let _ = handle.join();
+        }
     }
 }
 
@@ -77,34 +101,23 @@ pub enum CheckCommand {
 }
 
 struct CheckWatcherThread {
-    options: CheckConfig,
+    options: CheckOptions,
     workspace_root: PathBuf,
+    watcher: WatchThread,
     last_update_req: Option<Instant>,
-    // XXX: drop order is significant
-    message_recv: Receiver<CheckEvent>,
-    /// WatchThread exists to wrap around the communication needed to be able to
-    /// run `cargo check` without blocking. Currently the Rust standard library
-    /// doesn't provide a way to read sub-process output without blocking, so we
-    /// have to wrap sub-processes output handling in a thread and pass messages
-    /// back over a channel.
-    check_process: Option<jod_thread::JoinHandle<()>>,
 }
 
 impl CheckWatcherThread {
-    fn new(options: CheckConfig, workspace_root: PathBuf) -> CheckWatcherThread {
+    fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread {
         CheckWatcherThread {
             options,
             workspace_root,
+            watcher: WatchThread::dummy(),
             last_update_req: None,
-            message_recv: never(),
-            check_process: None,
         }
     }
 
     fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
-        // If we rerun the thread, we need to discard the previous check results first
-        self.clean_previous_results(task_send);
-
         loop {
             select! {
                 recv(&cmd_recv) -> cmd => match cmd {
@@ -114,32 +127,29 @@ impl CheckWatcherThread {
                         break;
                     },
                 },
-                recv(self.message_recv) -> msg => match msg {
+                recv(self.watcher.message_recv) -> msg => match msg {
                     Ok(msg) => self.handle_message(msg, task_send),
                     Err(RecvError) => {
                         // Watcher finished, replace it with a never channel to
                         // avoid busy-waiting.
-                        self.message_recv = never();
-                        self.check_process = None;
+                        std::mem::replace(&mut self.watcher.message_recv, never());
                     },
                 }
             };
 
             if self.should_recheck() {
-                self.last_update_req = None;
+                self.last_update_req.take();
                 task_send.send(CheckTask::ClearDiagnostics).unwrap();
-                self.restart_check_process();
+
+                // Replace with a dummy watcher first so we drop the original and wait for completion
+                std::mem::replace(&mut self.watcher, WatchThread::dummy());
+
+                // Then create the actual new watcher
+                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
             }
         }
     }
 
-    fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
-        task_send.send(CheckTask::ClearDiagnostics).unwrap();
-        task_send
-            .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
-            .unwrap();
-    }
-
     fn should_recheck(&mut self) -> bool {
         if let Some(_last_update_req) = &self.last_update_req {
             // We currently only request an update on save, as we need up to
@@ -211,59 +221,6 @@ impl CheckWatcherThread {
             CheckEvent::Msg(Message::Unknown) => {}
         }
     }
-
-    fn restart_check_process(&mut self) {
-        // First, clear and cancel the old thread
-        self.message_recv = never();
-        self.check_process = None;
-        if !self.options.enable {
-            return;
-        }
-
-        let mut args: Vec<String> = vec![
-            self.options.command.clone(),
-            "--workspace".to_string(),
-            "--message-format=json".to_string(),
-            "--manifest-path".to_string(),
-            format!("{}/Cargo.toml", self.workspace_root.display()),
-        ];
-        if self.options.all_targets {
-            args.push("--all-targets".to_string());
-        }
-        args.extend(self.options.args.iter().cloned());
-
-        let (message_send, message_recv) = unbounded();
-        let workspace_root = self.workspace_root.to_owned();
-        self.message_recv = message_recv;
-        self.check_process = Some(jod_thread::spawn(move || {
-            // If we trigger an error here, we will do so in the loop instead,
-            // which will break out of the loop, and continue the shutdown
-            let _ = message_send.send(CheckEvent::Begin);
-
-            let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
-                // Skip certain kinds of messages to only spend time on what's useful
-                match &message {
-                    Message::CompilerArtifact(artifact) if artifact.fresh => return true,
-                    Message::BuildScriptExecuted(_) => return true,
-                    Message::Unknown => return true,
-                    _ => {}
-                }
-
-                // if the send channel was closed, we want to shutdown
-                message_send.send(CheckEvent::Msg(message)).is_ok()
-            });
-
-            if let Err(err) = res {
-                // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
-                // to display user-caused misconfiguration errors instead of just logging them here
-                log::error!("Cargo watcher failed {:?}", err);
-            }
-
-            // We can ignore any error here, as we are already in the progress
-            // of shutting down.
-            let _ = message_send.send(CheckEvent::End);
-        }))
-    }
 }
 
 #[derive(Debug)]
@@ -272,6 +229,18 @@ pub struct DiagnosticWithFixes {
     fixes: Vec<CodeAction>,
 }
 
+/// WatchThread exists to wrap around the communication needed to be able to
+/// run `cargo check` without blocking. Currently the Rust standard library
+/// doesn't provide a way to read sub-process output without blocking, so we
+/// have to wrap sub-processes output handling in a thread and pass messages
+/// back over a channel.
+/// The correct way to dispose of the thread is to drop it, on which the
+/// sub-process will be killed, and the thread will be joined.
+struct WatchThread {
+    handle: Option<JoinHandle<()>>,
+    message_recv: Receiver<CheckEvent>,
+}
+
 enum CheckEvent {
     Begin,
     Msg(cargo_metadata::Message),
@@ -288,7 +257,7 @@ impl fmt::Display for CargoError {
 }
 impl error::Error for CargoError {}
 
-fn run_cargo(
+pub fn run_cargo(
     args: &[String],
     current_dir: Option<&Path>,
     on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
@@ -361,3 +330,76 @@ fn run_cargo(
 
     Err(CargoError(err_msg))
 }
+
+impl WatchThread {
+    fn dummy() -> WatchThread {
+        WatchThread { handle: None, message_recv: never() }
+    }
+
+    fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
+        let mut args: Vec<String> = vec![
+            options.command.clone(),
+            "--workspace".to_string(),
+            "--message-format=json".to_string(),
+            "--manifest-path".to_string(),
+            format!("{}/Cargo.toml", workspace_root.display()),
+        ];
+        if options.all_targets {
+            args.push("--all-targets".to_string());
+        }
+        args.extend(options.args.iter().cloned());
+
+        let (message_send, message_recv) = unbounded();
+        let workspace_root = workspace_root.to_owned();
+        let handle = if options.enable {
+            Some(std::thread::spawn(move || {
+                // If we trigger an error here, we will do so in the loop instead,
+                // which will break out of the loop, and continue the shutdown
+                let _ = message_send.send(CheckEvent::Begin);
+
+                let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
+                    // Skip certain kinds of messages to only spend time on what's useful
+                    match &message {
+                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
+                        Message::BuildScriptExecuted(_) => return true,
+                        Message::Unknown => return true,
+                        _ => {}
+                    }
+
+                    // if the send channel was closed, we want to shutdown
+                    message_send.send(CheckEvent::Msg(message)).is_ok()
+                });
+
+                if let Err(err) = res {
+                    // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
+                    // to display user-caused misconfiguration errors instead of just logging them here
+                    log::error!("Cargo watcher failed {:?}", err);
+                }
+
+                // We can ignore any error here, as we are already in the progress
+                // of shutting down.
+                let _ = message_send.send(CheckEvent::End);
+            }))
+        } else {
+            None
+        };
+        WatchThread { handle, message_recv }
+    }
+}
+
+impl std::ops::Drop for WatchThread {
+    fn drop(&mut self) {
+        if let Some(handle) = self.handle.take() {
+            // Replace our reciever with dummy one, so we can drop and close the
+            // one actually communicating with the thread
+            let recv = std::mem::replace(&mut self.message_recv, never());
+
+            // Dropping the original reciever initiates thread sub-process shutdown
+            drop(recv);
+
+            // Join the thread, it should finish shortly. We don't really care
+            // whether it panicked, so it is safe to ignore the result
+            let _ = handle.join();
+        }
+    }
+}
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 97d1208ec6..4d270e0def 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -9,15 +9,6 @@ pub struct ProcMacroExpander {
     proc_macro_id: ProcMacroId,
 }
 
-macro_rules! err {
-    ($fmt:literal, $($tt:tt),*) => {
-        mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*)))
-    };
-    ($fmt:literal) => {
-        mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string()))
-    }
-}
-
 impl ProcMacroExpander {
     pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
         ProcMacroExpander { krate, proc_macro_id }
@@ -34,24 +25,8 @@ impl ProcMacroExpander {
             .proc_macro
             .get(self.proc_macro_id.0 as usize)
             .clone()
-            .ok_or_else(|| err!("No derive macro found."))?;
-
-        let tt = remove_derive_atr(tt, &proc_macro.name)
-            .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
+            .ok_or_else(|| mbe::ExpandError::ConversionError)?;
 
         proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
     }
 }
-
-fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> {
-    // FIXME: proper handle the remove derive
-    // We assume the first 2 tokens are #[derive(name)]
-    if tt.token_trees.len() > 2 {
-        let mut tt = tt.clone();
-        tt.token_trees.remove(0);
-        tt.token_trees.remove(0);
-        return Some(tt);
-    }
-
-    None
-}
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index 13ecd537ad..a6ef44a31c 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -159,13 +159,20 @@ impl HirDisplay for ApplicationTy {
             }
             TypeCtor::FnDef(def) => {
                 let sig = f.db.callable_item_signature(def).subst(&self.parameters);
-                match def {
-                    CallableDef::FunctionId(ff) => write!(f, "fn {}", f.db.function_data(ff).name)?,
-                    CallableDef::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
+                let name = match def {
+                    CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(),
+                    CallableDef::StructId(s) => f.db.struct_data(s).name.clone(),
                     CallableDef::EnumVariantId(e) => {
-                        write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
+                        let enum_data = f.db.enum_data(e.parent);
+                        enum_data.variants[e.local_id].name.clone()
                     }
                 };
+                match def {
+                    CallableDef::FunctionId(_) => write!(f, "fn {}", name)?,
+                    CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
+                        write!(f, "{}", name)?
+                    }
+                }
                 if self.parameters.len() > 0 {
                     let generics = generics(f.db.upcast(), def.into());
                     let (parent_params, self_param, type_params, _impl_trait_params) =
@@ -190,6 +197,8 @@ impl HirDisplay for ApplicationTy {
                 };
                 write!(f, "{}", name)?;
                 if self.parameters.len() > 0 {
+                    write!(f, "<")?;
+
                     let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
                     let parameters_to_write = if f.omit_verbose_types() {
                         match self
@@ -198,8 +207,8 @@ impl HirDisplay for ApplicationTy {
                             .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
                             .filter(|defaults| !defaults.is_empty())
                         {
-                            None => self.parameters.0.as_ref(),
-                            Some(default_parameters) => {
+                            Option::None => self.parameters.0.as_ref(),
+                            Option::Some(default_parameters) => {
                                 for (i, parameter) in self.parameters.iter().enumerate() {
                                     match (parameter, default_parameters.get(i)) {
                                         (&Ty::Unknown, _) | (_, None) => {
@@ -219,7 +228,7 @@ impl HirDisplay for ApplicationTy {
                     } else {
                         self.parameters.0.as_ref()
                     };
-                    write!(f, "<")?;
+
                     f.write_joined(parameters_to_write, ", ")?;
                     write!(f, ">")?;
                 }
@@ -229,9 +238,9 @@ impl HirDisplay for ApplicationTy {
                     AssocContainerId::TraitId(it) => it,
                     _ => panic!("not an associated type"),
                 };
-                let trait_ = f.db.trait_data(trait_);
-                let type_alias = f.db.type_alias_data(type_alias);
-                write!(f, "{}::{}", trait_.name, type_alias.name)?;
+                let trait_name = f.db.trait_data(trait_).name.clone();
+                let name = f.db.type_alias_data(type_alias).name.clone();
+                write!(f, "{}::{}", trait_name, name)?;
                 if self.parameters.len() > 0 {
                     write!(f, "<")?;
                     f.write_joined(&*self.parameters.0, ", ")?;
@@ -264,8 +273,8 @@ impl HirDisplay for ProjectionTy {
             return write!(f, "{}", TYPE_HINT_TRUNCATION);
         }
 
-        let trait_ = f.db.trait_data(self.trait_(f.db));
-        write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_.name)?;
+        let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone();
+        write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?;
         if self.parameters.len() > 1 {
             write!(f, "<")?;
             f.write_joined(&self.parameters[1..], ", ")?;
@@ -310,7 +319,7 @@ impl HirDisplay for Ty {
                     Ty::Opaque(_) => write!(f, "impl ")?,
                     _ => unreachable!(),
                 };
-                write_bounds_like_dyn_trait(predicates, f)?;
+                write_bounds_like_dyn_trait(&predicates, f)?;
             }
             Ty::Unknown => write!(f, "{{unknown}}")?,
             Ty::Infer(..) => write!(f, "_")?,
@@ -343,7 +352,7 @@ fn write_bounds_like_dyn_trait(
                 // We assume that the self type is $0 (i.e. the
                 // existential) here, which is the only thing that's
                 // possible in actual Rust, and hence don't print it
-                write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?;
+                write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?;
                 if trait_ref.substs.len() > 1 {
                     write!(f, "<")?;
                     f.write_joined(&trait_ref.substs[1..], ", ")?;
@@ -360,8 +369,9 @@ fn write_bounds_like_dyn_trait(
                     write!(f, "<")?;
                     angle_open = true;
                 }
-                let type_alias = f.db.type_alias_data(projection_pred.projection_ty.associated_ty);
-                write!(f, "{} = ", type_alias.name)?;
+                let name =
+                    f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone();
+                write!(f, "{} = ", name)?;
                 projection_pred.ty.hir_fmt(f)?;
             }
             GenericPredicate::Error => {
@@ -395,7 +405,7 @@ impl TraitRef {
         } else {
             write!(f, ": ")?;
         }
-        write!(f, "{}", f.db.trait_data(self.trait_).name)?;
+        write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?;
         if self.substs.len() > 1 {
             write!(f, "<")?;
             f.write_joined(&self.substs[1..], ", ")?;
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index b683572fbf..cd0757be5f 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -34,15 +34,15 @@ pub use crate::completion::completion_item::{
 };
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct CompletionConfig {
+pub struct CompletionOptions {
     pub enable_postfix_completions: bool,
     pub add_call_parenthesis: bool,
     pub add_call_argument_snippets: bool,
 }
 
-impl Default for CompletionConfig {
+impl Default for CompletionOptions {
     fn default() -> Self {
-        CompletionConfig {
+        CompletionOptions {
             enable_postfix_completions: true,
             add_call_parenthesis: true,
             add_call_argument_snippets: true,
@@ -75,9 +75,9 @@ impl Default for CompletionConfig {
 pub(crate) fn completions(
     db: &RootDatabase,
     position: FilePosition,
-    config: &CompletionConfig,
+    options: &CompletionOptions,
 ) -> Option<Completions> {
-    let ctx = CompletionContext::new(db, position, config)?;
+    let ctx = CompletionContext::new(db, position, options)?;
 
     let mut acc = Completions::default();
 
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 29c2881c62..0a00054b23 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -15,7 +15,7 @@ use crate::{
 };
 
 pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
-    if !ctx.config.enable_postfix_completions {
+    if !ctx.options.enable_postfix_completions {
         return;
     }
 
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index fdc0da2c5e..319e33b615 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
 };
 use ra_text_edit::AtomTextEdit;
 
-use crate::{completion::CompletionConfig, FilePosition};
+use crate::{completion::CompletionOptions, FilePosition};
 
 /// `CompletionContext` is created early during completion to figure out, where
 /// exactly is the cursor, syntax-wise.
@@ -19,7 +19,7 @@ use crate::{completion::CompletionConfig, FilePosition};
 pub(crate) struct CompletionContext<'a> {
     pub(super) sema: Semantics<'a, RootDatabase>,
     pub(super) db: &'a RootDatabase,
-    pub(super) config: &'a CompletionConfig,
+    pub(super) options: &'a CompletionOptions,
     pub(super) offset: TextUnit,
     /// The token before the cursor, in the original file.
     pub(super) original_token: SyntaxToken,
@@ -61,7 +61,7 @@ impl<'a> CompletionContext<'a> {
     pub(super) fn new(
         db: &'a RootDatabase,
         position: FilePosition,
-        config: &'a CompletionConfig,
+        options: &'a CompletionOptions,
     ) -> Option<CompletionContext<'a>> {
         let sema = Semantics::new(db);
 
@@ -85,7 +85,7 @@ impl<'a> CompletionContext<'a> {
         let mut ctx = CompletionContext {
             sema,
             db,
-            config,
+            options,
             original_token,
             token,
             offset: position.offset,
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 1c7c0924db..60f1b83f33 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -106,7 +106,7 @@ impl Completions {
         };
 
         // Add `<>` for generic types
-        if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
+        if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis {
             let has_non_default_type_params = match resolution {
                 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
                 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -211,14 +211,14 @@ impl Completions {
                 .detail(function_signature.to_string());
 
         // If not an import, add parenthesis automatically.
-        if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis {
+        if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis {
             tested_by!(inserts_parens_for_function_calls);
 
             let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
                 (format!("{}()$0", name), format!("{}()", name))
             } else {
                 builder = builder.trigger_call_info();
-                let snippet = if ctx.config.add_call_argument_snippets {
+                let snippet = if ctx.options.add_call_argument_snippets {
                     let to_skip = if has_self_param { 1 } else { 0 };
                     let function_params_snippet = function_signature
                         .parameter_names
@@ -311,7 +311,7 @@ mod tests {
 
     use crate::completion::{
         test_utils::{do_completion, do_completion_with_options},
-        CompletionConfig, CompletionItem, CompletionKind,
+        CompletionItem, CompletionKind, CompletionOptions,
     };
 
     fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
@@ -320,7 +320,7 @@ mod tests {
 
     fn do_reference_completion_with_options(
         ra_fixture: &str,
-        options: CompletionConfig,
+        options: CompletionOptions,
     ) -> Vec<CompletionItem> {
         do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
     }
@@ -589,7 +589,7 @@ mod tests {
                     s.f<|>
                 }
                 ",
-                CompletionConfig {
+                CompletionOptions {
                     add_call_argument_snippets: false,
                     .. Default::default()
                 }
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index d06fc03d3e..f4f0751c05 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -11,14 +11,14 @@ use ra_syntax::{
 use crate::{FileId, FunctionSignature};
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct InlayHintsConfig {
+pub struct InlayHintsOptions {
     pub type_hints: bool,
     pub parameter_hints: bool,
     pub chaining_hints: bool,
     pub max_length: Option<usize>,
 }
 
-impl Default for InlayHintsConfig {
+impl Default for InlayHintsOptions {
     fn default() -> Self {
         Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
     }
@@ -41,7 +41,7 @@ pub struct InlayHint {
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
     file_id: FileId,
-    config: &InlayHintsConfig,
+    options: &InlayHintsOptions,
 ) -> Vec<InlayHint> {
     let _p = profile("inlay_hints");
     let sema = Semantics::new(db);
@@ -50,14 +50,14 @@ pub(crate) fn inlay_hints(
     let mut res = Vec::new();
     for node in file.syntax().descendants() {
         if let Some(expr) = ast::Expr::cast(node.clone()) {
-            get_chaining_hints(&mut res, &sema, config, expr);
+            get_chaining_hints(&mut res, &sema, options, expr);
         }
 
         match_ast! {
             match node {
-                ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
-                ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
-                ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
+                ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
+                ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
+                ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, options, it); },
                 _ => (),
             }
         }
@@ -68,10 +68,10 @@ pub(crate) fn inlay_hints(
 fn get_chaining_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
-    config: &InlayHintsConfig,
+    options: &InlayHintsOptions,
     expr: ast::Expr,
 ) -> Option<()> {
-    if !config.chaining_hints {
+    if !options.chaining_hints {
         return None;
     }
 
@@ -95,7 +95,7 @@ fn get_chaining_hints(
     let next = tokens.next()?.kind();
     let next_next = tokens.next()?.kind();
     if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
-        let label = ty.display_truncated(sema.db, config.max_length).to_string();
+        let label = ty.display_truncated(sema.db, options.max_length).to_string();
         acc.push(InlayHint {
             range: expr.syntax().text_range(),
             kind: InlayKind::ChainingHint,
@@ -108,10 +108,10 @@ fn get_chaining_hints(
 fn get_param_name_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
-    config: &InlayHintsConfig,
+    options: &InlayHintsOptions,
     expr: ast::Expr,
 ) -> Option<()> {
-    if !config.parameter_hints {
+    if !options.parameter_hints {
         return None;
     }
 
@@ -148,10 +148,10 @@ fn get_param_name_hints(
 fn get_bind_pat_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
-    config: &InlayHintsConfig,
+    options: &InlayHintsOptions,
     pat: ast::BindPat,
 ) -> Option<()> {
-    if !config.type_hints {
+    if !options.type_hints {
         return None;
     }
 
@@ -164,7 +164,7 @@ fn get_bind_pat_hints(
     acc.push(InlayHint {
         range: pat.syntax().text_range(),
         kind: InlayKind::TypeHint,
-        label: ty.display_truncated(sema.db, config.max_length).to_string().into(),
+        label: ty.display_truncated(sema.db, options.max_length).to_string().into(),
     });
     Some(())
 }
@@ -270,7 +270,7 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
 
 #[cfg(test)]
 mod tests {
-    use crate::inlay_hints::InlayHintsConfig;
+    use crate::inlay_hints::InlayHintsOptions;
     use insta::assert_debug_snapshot;
 
     use crate::mock_analysis::single_file;
@@ -284,7 +284,7 @@ mod tests {
                 let _x = foo(4, 4);
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
         [
             InlayHint {
                 range: [106; 107),
@@ -308,7 +308,7 @@ mod tests {
                 let _x = foo(4, 4);
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
     }
 
     #[test]
@@ -320,7 +320,7 @@ mod tests {
                 let _x = foo(4, 4);
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
         [
             InlayHint {
                 range: [97; 99),
@@ -344,7 +344,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [69; 71),
@@ -401,7 +401,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [193; 197),
@@ -481,7 +481,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [21; 30),
@@ -545,7 +545,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [21; 30),
@@ -595,7 +595,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [188; 192),
@@ -690,7 +690,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [188; 192),
@@ -785,7 +785,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [252; 256),
@@ -857,7 +857,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
         [
             InlayHint {
                 range: [74; 75),
@@ -945,7 +945,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###"
         [
             InlayHint {
                 range: [798; 809),
@@ -1067,7 +1067,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
         []
         "###
         );
@@ -1093,7 +1093,7 @@ fn main() {
 }"#,
         );
 
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
         []
         "###
         );
@@ -1115,7 +1115,7 @@ fn main() {
                     .into_c();
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
         [
             InlayHint {
                 range: [232; 269),
@@ -1144,7 +1144,7 @@ fn main() {
                 let c = A(B(C)).into_b().into_c();
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
     }
 
     #[test]
@@ -1162,7 +1162,7 @@ fn main() {
                     .0;
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
         [
             InlayHint {
                 range: [150; 221),
@@ -1204,7 +1204,7 @@ fn main() {
                     .into_c();
             }"#,
         );
-        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
+        assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
         [
             InlayHint {
                 range: [403; 452),
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 2853810865..e43414985f 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -62,13 +62,13 @@ use crate::display::ToNav;
 pub use crate::{
     assists::{Assist, AssistId},
     call_hierarchy::CallItem,
-    completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat},
+    completion::{CompletionItem, CompletionItemKind, CompletionOptions, InsertTextFormat},
     diagnostics::Severity,
     display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
     expand_macro::ExpandedMacro,
     folding_ranges::{Fold, FoldKind},
     hover::HoverResult,
-    inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
+    inlay_hints::{InlayHint, InlayHintsOptions, InlayKind},
     references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
     runnables::{Runnable, RunnableKind, TestId},
     source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -138,11 +138,6 @@ impl AnalysisHost {
     pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
         AnalysisHost { db: RootDatabase::new(lru_capacity) }
     }
-
-    pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
-        self.db.update_lru_capacity(lru_capacity);
-    }
-
     /// Returns a snapshot of the current state, which you can query for
     /// semantic information.
     pub fn analysis(&self) -> Analysis {
@@ -325,9 +320,9 @@ impl Analysis {
     pub fn inlay_hints(
         &self,
         file_id: FileId,
-        config: &InlayHintsConfig,
+        inlay_hint_opts: &InlayHintsOptions,
     ) -> Cancelable<Vec<InlayHint>> {
-        self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
+        self.with_db(|db| inlay_hints::inlay_hints(db, file_id, inlay_hint_opts))
     }
 
     /// Returns the set of folding ranges.
@@ -450,9 +445,9 @@ impl Analysis {
     pub fn completions(
         &self,
         position: FilePosition,
-        config: &CompletionConfig,
+        options: &CompletionOptions,
     ) -> Cancelable<Option<Vec<CompletionItem>>> {
-        self.with_db(|db| completion::completions(db, position, config).map(Into::into))
+        self.with_db(|db| completion::completions(db, position, options).map(Into::into))
     }
 
     /// Computes assists (aka code actions aka intentions) for the given
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1c9710a5d5..1abb891c17 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
 use ra_ide_db::symbol_index::SymbolsDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::ast::make::try_expr_from_text;
-use ra_syntax::ast::{AstToken, Comment};
+use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit};
 use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
 use ra_text_edit::{TextEdit, TextEditBuilder};
 use rustc_hash::FxHashMap;
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr
 }
 
 fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
+    fn check_record_lit(
+        pattern: RecordLit,
+        code: RecordLit,
+        placeholders: &[Var],
+        match_: Match,
+    ) -> Option<Match> {
+        let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
+
+        let mut pattern_fields =
+            pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
+        let mut code_fields =
+            code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
+
+        if pattern_fields.len() != code_fields.len() {
+            return None;
+        }
+
+        let by_name = |a: &RecordField, b: &RecordField| {
+            a.name_ref()
+                .map(|x| x.syntax().text().to_string())
+                .cmp(&b.name_ref().map(|x| x.syntax().text().to_string()))
+        };
+        pattern_fields.sort_by(by_name);
+        code_fields.sort_by(by_name);
+
+        pattern_fields.into_iter().zip(code_fields.into_iter()).fold(
+            Some(match_),
+            |accum, (a, b)| {
+                accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_))
+            },
+        )
+    }
+
+    fn check_opt_nodes(
+        pattern: Option<impl AstNode>,
+        code: Option<impl AstNode>,
+        placeholders: &[Var],
+        match_: Match,
+    ) -> Option<Match> {
+        match (pattern, code) {
+            (Some(pattern), Some(code)) => check(
+                &SyntaxElement::from(pattern.syntax().clone()),
+                &SyntaxElement::from(code.syntax().clone()),
+                placeholders,
+                match_,
+            ),
+            (None, None) => Some(match_),
+            _ => None,
+        }
+    }
+
     fn check(
         pattern: &SyntaxElement,
         code: &SyntaxElement,
         placeholders: &[Var],
         mut match_: Match,
     ) -> Option<Match> {
-        match (pattern, code) {
-            (SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => {
+        match (&pattern, &code) {
+            (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
                 if pattern.text() == code.text() {
                     Some(match_)
                 } else {
                     None
                 }
             }
-            (SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => {
+            (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
                 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
                     match_.binding.insert(Var(pattern.text().to_string()), code.clone());
                     Some(match_)
                 } else {
-                    let mut pattern_children = pattern
-                        .children_with_tokens()
-                        .filter(|element| !element.kind().is_trivia());
-                    let mut code_children =
-                        code.children_with_tokens().filter(|element| !element.kind().is_trivia());
-                    let new_ignored_comments = code.children_with_tokens().filter_map(|element| {
-                        element.as_token().and_then(|token| Comment::cast(token.clone()))
-                    });
-                    match_.ignored_comments.extend(new_ignored_comments);
-                    let match_from_children = pattern_children
-                        .by_ref()
-                        .zip(code_children.by_ref())
-                        .fold(Some(match_), |accum, (a, b)| {
-                            accum.and_then(|match_| check(&a, &b, placeholders, match_))
-                        });
-                    match_from_children.and_then(|match_| {
-                        if pattern_children.count() == 0 && code_children.count() == 0 {
-                            Some(match_)
-                        } else {
-                            None
-                        }
-                    })
+                    if let (Some(pattern), Some(code)) =
+                        (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
+                    {
+                        check_record_lit(pattern, code, placeholders, match_)
+                    } else {
+                        let mut pattern_children = pattern
+                            .children_with_tokens()
+                            .filter(|element| !element.kind().is_trivia());
+                        let mut code_children = code
+                            .children_with_tokens()
+                            .filter(|element| !element.kind().is_trivia());
+                        let new_ignored_comments =
+                            code.children_with_tokens().filter_map(|element| {
+                                element.as_token().and_then(|token| Comment::cast(token.clone()))
+                            });
+                        match_.ignored_comments.extend(new_ignored_comments);
+                        pattern_children
+                            .by_ref()
+                            .zip(code_children.by_ref())
+                            .fold(Some(match_), |accum, (a, b)| {
+                                accum.and_then(|match_| check(&a, &b, placeholders, match_))
+                            })
+                            .filter(|_| {
+                                pattern_children.next().is_none() && code_children.next().is_none()
+                            })
+                    }
                 }
             }
             _ => None,
@@ -434,4 +489,13 @@ mod tests {
             "fn main() { bar(5)/* using 5 */ }",
         )
     }
+
+    #[test]
+    fn ssr_struct_lit() {
+        assert_ssr_transform(
+            "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)",
+            "fn main() { foo{b:2, a:1} }",
+            "fn main() { foo::new(1, 2) }",
+        )
+    }
 }
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index e6f2d36e94..4faeefa8db 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -115,15 +115,11 @@ impl RootDatabase {
         db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
         db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
         db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
-        db.update_lru_capacity(lru_capacity);
-        db
-    }
-
-    pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
         let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
-        self.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
-        self.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
-        self.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
+        db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
+        db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
+        db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
+        db
     }
 }
 
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml
index d009ceb82e..bc2c37296d 100644
--- a/crates/ra_proc_macro/Cargo.toml
+++ b/crates/ra_proc_macro/Cargo.toml
@@ -10,8 +10,3 @@ doctest = false
 
 [dependencies]
 ra_tt = { path = "../ra_tt" }
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-log = "0.4.8"
-crossbeam-channel = "0.4.0"
-jod-thread = "0.1.1"
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
index 51fbb046a9..5e21dd4872 100644
--- a/crates/ra_proc_macro/src/lib.rs
+++ b/crates/ra_proc_macro/src/lib.rs
@@ -5,104 +5,55 @@
 //! is used to provide basic infrastructure  for communication between two
 //! processes: Client (RA itself), Server (the external program)
 
-mod rpc;
-mod process;
-pub mod msg;
-
-use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
 use ra_tt::{SmolStr, Subtree};
-use rpc::ProcMacroKind;
 use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
 
-pub use rpc::{ExpansionResult, ExpansionTask};
-
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ProcMacroProcessExpander {
     process: Arc<ProcMacroProcessSrv>,
-    dylib_path: PathBuf,
     name: SmolStr,
 }
 
-impl Eq for ProcMacroProcessExpander {}
-impl PartialEq for ProcMacroProcessExpander {
-    fn eq(&self, other: &Self) -> bool {
-        self.name == other.name
-            && self.dylib_path == other.dylib_path
-            && Arc::ptr_eq(&self.process, &other.process)
-    }
-}
-
 impl ra_tt::TokenExpander for ProcMacroProcessExpander {
     fn expand(
         &self,
-        subtree: &Subtree,
+        _subtree: &Subtree,
         _attr: Option<&Subtree>,
     ) -> Result<Subtree, ra_tt::ExpansionError> {
-        self.process.custom_derive(&self.dylib_path, subtree, &self.name)
+        // FIXME: do nothing for now
+        Ok(Subtree::default())
     }
 }
 
-#[derive(Debug)]
-enum ProcMacroClientKind {
-    Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread },
-    Dummy,
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ProcMacroProcessSrv {
+    path: PathBuf,
 }
 
-#[derive(Debug)]
-pub struct ProcMacroClient {
-    kind: ProcMacroClientKind,
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ProcMacroClient {
+    Process { process: Arc<ProcMacroProcessSrv> },
+    Dummy,
 }
 
 impl ProcMacroClient {
-    pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> {
-        let (thread, process) = ProcMacroProcessSrv::run(process_path)?;
-        Ok(ProcMacroClient {
-            kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
-        })
+    pub fn extern_process(process_path: &Path) -> ProcMacroClient {
+        let process = ProcMacroProcessSrv { path: process_path.into() };
+        ProcMacroClient::Process { process: Arc::new(process) }
     }
 
     pub fn dummy() -> ProcMacroClient {
-        ProcMacroClient { kind: ProcMacroClientKind::Dummy }
+        ProcMacroClient::Dummy
     }
 
     pub fn by_dylib_path(
         &self,
-        dylib_path: &Path,
+        _dylib_path: &Path,
     ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
-        match &self.kind {
-            ProcMacroClientKind::Dummy => vec![],
-            ProcMacroClientKind::Process { process, .. } => {
-                let macros = match process.find_proc_macros(dylib_path) {
-                    Err(err) => {
-                        eprintln!("Fail to find proc macro. Error: {:#?}", err);
-                        return vec![];
-                    }
-                    Ok(macros) => macros,
-                };
-
-                macros
-                    .into_iter()
-                    .filter_map(|(name, kind)| {
-                        // FIXME: Support custom derive only for now.
-                        match kind {
-                            ProcMacroKind::CustomDerive => {
-                                let name = SmolStr::new(&name);
-                                let expander: Arc<dyn ra_tt::TokenExpander> =
-                                    Arc::new(ProcMacroProcessExpander {
-                                        process: process.clone(),
-                                        name: name.clone(),
-                                        dylib_path: dylib_path.into(),
-                                    });
-                                Some((name, expander))
-                            }
-                            _ => None,
-                        }
-                    })
-                    .collect()
-            }
-        }
+        // FIXME: return empty for now
+        vec![]
     }
 }
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs
deleted file mode 100644
index aa95bcc8f7..0000000000
--- a/crates/ra_proc_macro/src/msg.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-//! Defines messages for cross-process message based on `ndjson` wire protocol
-
-use std::{
-    convert::TryFrom,
-    io::{self, BufRead, Write},
-};
-
-use crate::{
-    rpc::{ListMacrosResult, ListMacrosTask},
-    ExpansionResult, ExpansionTask,
-};
-use serde::{de::DeserializeOwned, Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub enum Request {
-    ListMacro(ListMacrosTask),
-    ExpansionMacro(ExpansionTask),
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub enum Response {
-    Error(ResponseError),
-    ListMacro(ListMacrosResult),
-    ExpansionMacro(ExpansionResult),
-}
-
-macro_rules! impl_try_from_response {
-    ($ty:ty, $tag:ident) => {
-        impl TryFrom<Response> for $ty {
-            type Error = &'static str;
-            fn try_from(value: Response) -> Result<Self, Self::Error> {
-                match value {
-                    Response::$tag(res) => Ok(res),
-                    _ => Err("Fail to convert from response"),
-                }
-            }
-        }
-    };
-}
-
-impl_try_from_response!(ListMacrosResult, ListMacro);
-impl_try_from_response!(ExpansionResult, ExpansionMacro);
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct ResponseError {
-    pub code: ErrorCode,
-    pub message: String,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub enum ErrorCode {
-    ServerErrorEnd,
-    ExpansionError,
-}
-
-pub trait Message: Sized + Serialize + DeserializeOwned {
-    fn read(r: &mut impl BufRead) -> io::Result<Option<Self>> {
-        let text = match read_json(r)? {
-            None => return Ok(None),
-            Some(text) => text,
-        };
-        let msg = serde_json::from_str(&text)?;
-        Ok(Some(msg))
-    }
-    fn write(self, w: &mut impl Write) -> io::Result<()> {
-        let text = serde_json::to_string(&self)?;
-        write_json(w, &text)
-    }
-}
-
-impl Message for Request {}
-impl Message for Response {}
-
-fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
-    let mut buf = String::new();
-    if inp.read_line(&mut buf)? == 0 {
-        return Ok(None);
-    }
-    // Remove ending '\n'
-    let buf = &buf[..buf.len() - 1];
-    if buf.is_empty() {
-        return Ok(None);
-    }
-    Ok(Some(buf.to_string()))
-}
-
-fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
-    log::debug!("> {}", msg);
-    out.write_all(msg.as_bytes())?;
-    out.write_all(b"\n")?;
-    out.flush()?;
-    Ok(())
-}
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
deleted file mode 100644
index e8c85be38a..0000000000
--- a/crates/ra_proc_macro/src/process.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-//! Handle process life-time and message passing for proc-macro client
-
-use crossbeam_channel::{bounded, Receiver, Sender};
-use ra_tt::Subtree;
-
-use crate::msg::{ErrorCode, Message, Request, Response, ResponseError};
-use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
-
-use io::{BufRead, BufReader};
-use std::{
-    convert::{TryFrom, TryInto},
-    io::{self, Write},
-    path::{Path, PathBuf},
-    process::{Child, Command, Stdio},
-    sync::{Arc, Weak},
-};
-
-#[derive(Debug, Default)]
-pub(crate) struct ProcMacroProcessSrv {
-    inner: Option<Weak<Sender<Task>>>,
-}
-
-#[derive(Debug)]
-pub(crate) struct ProcMacroProcessThread {
-    // XXX: drop order is significant
-    sender: Arc<Sender<Task>>,
-    handle: jod_thread::JoinHandle<()>,
-}
-
-struct Task {
-    req: Request,
-    result_tx: Sender<Option<Response>>,
-}
-
-struct Process {
-    path: PathBuf,
-    child: Child,
-}
-
-impl Drop for Process {
-    fn drop(&mut self) {
-        let _ = self.child.kill();
-    }
-}
-
-impl Process {
-    fn run(process_path: &Path) -> Result<Process, io::Error> {
-        let child = Command::new(process_path.clone())
-            .stdin(Stdio::piped())
-            .stdout(Stdio::piped())
-            .stderr(Stdio::null())
-            .spawn()?;
-
-        Ok(Process { path: process_path.into(), child })
-    }
-
-    fn restart(&mut self) -> Result<(), io::Error> {
-        let _ = self.child.kill();
-        self.child = Command::new(self.path.clone())
-            .stdin(Stdio::piped())
-            .stdout(Stdio::piped())
-            .stderr(Stdio::null())
-            .spawn()?;
-        Ok(())
-    }
-
-    fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
-        let stdin = self.child.stdin.take()?;
-        let stdout = self.child.stdout.take()?;
-        let read = BufReader::new(stdout);
-
-        Some((stdin, read))
-    }
-}
-
-impl ProcMacroProcessSrv {
-    pub fn run(
-        process_path: &Path,
-    ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> {
-        let process = Process::run(process_path)?;
-
-        let (task_tx, task_rx) = bounded(0);
-        let handle = jod_thread::spawn(move || {
-            client_loop(task_rx, process);
-        });
-
-        let task_tx = Arc::new(task_tx);
-        let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
-        let thread = ProcMacroProcessThread { handle, sender: task_tx };
-
-        Ok((thread, srv))
-    }
-
-    pub fn find_proc_macros(
-        &self,
-        dylib_path: &Path,
-    ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
-        let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
-
-        let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
-        Ok(result.macros)
-    }
-
-    pub fn custom_derive(
-        &self,
-        dylib_path: &Path,
-        subtree: &Subtree,
-        derive_name: &str,
-    ) -> Result<Subtree, ra_tt::ExpansionError> {
-        let task = ExpansionTask {
-            macro_body: subtree.clone(),
-            macro_name: derive_name.to_string(),
-            attributes: None,
-            lib: dylib_path.to_path_buf(),
-        };
-
-        let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
-        Ok(result.expansion)
-    }
-
-    pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError>
-    where
-        R: TryFrom<Response, Error = &'static str>,
-    {
-        let sender = match &self.inner {
-            None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())),
-            Some(it) => it,
-        };
-
-        let (result_tx, result_rx) = bounded(0);
-        let sender = match sender.upgrade() {
-            None => {
-                return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
-            }
-            Some(it) => it,
-        };
-        sender.send(Task { req: req.into(), result_tx }).unwrap();
-        let res = result_rx
-            .recv()
-            .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
-
-        match res {
-            Some(Response::Error(err)) => {
-                return Err(ra_tt::ExpansionError::ExpansionError(err.message));
-            }
-            Some(res) => Ok(res.try_into().map_err(|err| {
-                ra_tt::ExpansionError::Unknown(format!(
-                    "Fail to get response, reason : {:#?} ",
-                    err
-                ))
-            })?),
-            None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())),
-        }
-    }
-}
-
-fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
-    let (mut stdin, mut stdout) = match process.stdio() {
-        None => return,
-        Some(it) => it,
-    };
-
-    for task in task_rx {
-        let Task { req, result_tx } = task;
-
-        match send_request(&mut stdin, &mut stdout, req) {
-            Ok(res) => result_tx.send(res).unwrap(),
-            Err(_err) => {
-                let res = Response::Error(ResponseError {
-                    code: ErrorCode::ServerErrorEnd,
-                    message: "Server closed".into(),
-                });
-                result_tx.send(res.into()).unwrap();
-                // Restart the process
-                if process.restart().is_err() {
-                    break;
-                }
-                let stdio = match process.stdio() {
-                    None => break,
-                    Some(it) => it,
-                };
-                stdin = stdio.0;
-                stdout = stdio.1;
-            }
-        }
-    }
-}
-
-fn send_request(
-    mut writer: &mut impl Write,
-    mut reader: &mut impl BufRead,
-    req: Request,
-) -> Result<Option<Response>, io::Error> {
-    req.write(&mut writer)?;
-    Ok(Response::read(&mut reader)?)
-}
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
deleted file mode 100644
index 66b3f55db3..0000000000
--- a/crates/ra_proc_macro/src/rpc.rs
+++ /dev/null
@@ -1,266 +0,0 @@
-//! Data struture serialization related stuffs for RPC
-//!
-//! Define all necessary rpc serialization data structure,
-//! which include ra_tt related data and some task messages.
-//! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier,
-//! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")]
-//! for separation of code responsibility.
-
-use ra_tt::{
-    Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId,
-    TokenTree,
-};
-use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
-
-#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
-pub struct ListMacrosTask {
-    pub lib: PathBuf,
-}
-
-#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
-pub enum ProcMacroKind {
-    CustomDerive,
-    FuncLike,
-    Attr,
-}
-
-#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
-pub struct ListMacrosResult {
-    pub macros: Vec<(String, ProcMacroKind)>,
-}
-
-#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
-pub struct ExpansionTask {
-    /// Argument of macro call.
-    ///
-    /// In custom derive that would be a struct or enum; in attribute-like macro - underlying
-    /// item; in function-like macro - the macro body.
-    #[serde(with = "SubtreeDef")]
-    pub macro_body: Subtree,
-
-    /// Names of macros to expand.
-    ///
-    /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In
-    /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`).
-    pub macro_name: String,
-
-    /// Possible attributes for the attribute-like macros.
-    #[serde(with = "opt_subtree_def")]
-    pub attributes: Option<Subtree>,
-
-    pub lib: PathBuf,
-}
-
-#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
-pub struct ExpansionResult {
-    #[serde(with = "SubtreeDef")]
-    pub expansion: Subtree,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "DelimiterKind")]
-enum DelimiterKindDef {
-    Parenthesis,
-    Brace,
-    Bracket,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "TokenId")]
-struct TokenIdDef(u32);
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Delimiter")]
-struct DelimiterDef {
-    #[serde(with = "TokenIdDef")]
-    pub id: TokenId,
-    #[serde(with = "DelimiterKindDef")]
-    pub kind: DelimiterKind,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Subtree")]
-struct SubtreeDef {
-    #[serde(default, with = "opt_delimiter_def")]
-    pub delimiter: Option<Delimiter>,
-    #[serde(with = "vec_token_tree")]
-    pub token_trees: Vec<TokenTree>,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "TokenTree")]
-enum TokenTreeDef {
-    #[serde(with = "LeafDef")]
-    Leaf(Leaf),
-    #[serde(with = "SubtreeDef")]
-    Subtree(Subtree),
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Leaf")]
-enum LeafDef {
-    #[serde(with = "LiteralDef")]
-    Literal(Literal),
-    #[serde(with = "PunctDef")]
-    Punct(Punct),
-    #[serde(with = "IdentDef")]
-    Ident(Ident),
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Literal")]
-struct LiteralDef {
-    pub text: SmolStr,
-    #[serde(with = "TokenIdDef")]
-    pub id: TokenId,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Punct")]
-struct PunctDef {
-    pub char: char,
-    #[serde(with = "SpacingDef")]
-    pub spacing: Spacing,
-    #[serde(with = "TokenIdDef")]
-    pub id: TokenId,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Spacing")]
-enum SpacingDef {
-    Alone,
-    Joint,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(remote = "Ident")]
-struct IdentDef {
-    pub text: SmolStr,
-    #[serde(with = "TokenIdDef")]
-    pub id: TokenId,
-}
-
-mod opt_delimiter_def {
-    use super::{Delimiter, DelimiterDef};
-    use serde::{Deserialize, Deserializer, Serialize, Serializer};
-
-    pub fn serialize<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        #[derive(Serialize)]
-        struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter);
-        value.as_ref().map(Helper).serialize(serializer)
-    }
-
-    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Delimiter>, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        struct Helper(#[serde(with = "DelimiterDef")] Delimiter);
-        let helper = Option::deserialize(deserializer)?;
-        Ok(helper.map(|Helper(external)| external))
-    }
-}
-
-mod opt_subtree_def {
-    use super::{Subtree, SubtreeDef};
-    use serde::{Deserialize, Deserializer, Serialize, Serializer};
-
-    pub fn serialize<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        #[derive(Serialize)]
-        struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree);
-        value.as_ref().map(Helper).serialize(serializer)
-    }
-
-    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Subtree>, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        struct Helper(#[serde(with = "SubtreeDef")] Subtree);
-        let helper = Option::deserialize(deserializer)?;
-        Ok(helper.map(|Helper(external)| external))
-    }
-}
-
-mod vec_token_tree {
-    use super::{TokenTree, TokenTreeDef};
-    use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
-
-    pub fn serialize<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        #[derive(Serialize)]
-        struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree);
-
-        let items: Vec<_> = value.iter().map(Helper).collect();
-        let mut seq = serializer.serialize_seq(Some(items.len()))?;
-        for element in items {
-            seq.serialize_element(&element)?;
-        }
-        seq.end()
-    }
-
-    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<TokenTree>, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        struct Helper(#[serde(with = "TokenTreeDef")] TokenTree);
-
-        let helper = Vec::deserialize(deserializer)?;
-        Ok(helper.into_iter().map(|Helper(external)| external).collect())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn fixture_token_tree() -> Subtree {
-        let mut subtree = Subtree::default();
-        subtree
-            .token_trees
-            .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into()));
-        subtree
-            .token_trees
-            .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into()));
-        subtree.token_trees.push(TokenTree::Subtree(
-            Subtree {
-                delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }),
-                token_trees: vec![],
-            }
-            .into(),
-        ));
-        subtree
-    }
-
-    #[test]
-    fn test_proc_macro_rpc_works() {
-        let tt = fixture_token_tree();
-        let task = ExpansionTask {
-            macro_body: tt.clone(),
-            macro_name: Default::default(),
-            attributes: None,
-            lib: Default::default(),
-        };
-
-        let json = serde_json::to_string(&task).unwrap();
-        let back: ExpansionTask = serde_json::from_str(&json).unwrap();
-
-        assert_eq!(task.macro_body, back.macro_body);
-
-        let result = ExpansionResult { expansion: tt.clone() };
-        let json = serde_json::to_string(&result).unwrap();
-        let back: ExpansionResult = serde_json::from_str(&json).unwrap();
-
-        assert_eq!(result, back);
-    }
-}
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 00ea3a9b06..9e167db964 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -339,14 +339,6 @@ pub fn print_backtrace() {
     let bt = backtrace::Backtrace::new();
     eprintln!("{:?}", bt);
 }
-#[cfg(not(feature = "backtrace"))]
-pub fn print_backtrace() {
-    eprintln!(
-        r#"enable the backtrace feature:
-    ra_prof = {{ path = "../ra_prof", features = [ "backtrace"] }}
-"#
-    );
-}
 
 thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false));
 
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index b10644b4b0..cdcdd63c91 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -16,6 +16,7 @@ cargo_metadata = "0.9.1"
 ra_arena = { path = "../ra_arena" }
 ra_db = { path = "../ra_db" }
 ra_cfg = { path = "../ra_cfg" }
+ra_cargo_watch = { path = "../ra_cargo_watch" }
 ra_proc_macro =  { path = "../ra_proc_macro" }
 
 serde = { version = "1.0.104", features = ["derive"] }
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index f4fd6ad28c..291594e2a2 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,16 +1,14 @@
 //! FIXME: write short doc here
 
 use std::{
-    env,
-    ffi::OsStr,
     ops,
     path::{Path, PathBuf},
-    process::Command,
 };
 
 use anyhow::{Context, Result};
 use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
 use ra_arena::{Arena, Idx};
+use ra_cargo_watch::run_cargo;
 use ra_db::Edition;
 use rustc_hash::FxHashMap;
 use serde::Deserialize;
@@ -77,7 +75,6 @@ pub type Target = Idx<TargetData>;
 
 #[derive(Debug, Clone)]
 pub struct PackageData {
-    pub id: String,
     pub name: String,
     pub manifest: PathBuf,
     pub targets: Vec<Target>,
@@ -164,7 +161,7 @@ impl CargoWorkspace {
         let mut out_dir_by_id = FxHashMap::default();
         let mut proc_macro_dylib_paths = FxHashMap::default();
         if cargo_features.load_out_dirs_from_check {
-            let resources = load_extern_resources(cargo_toml, cargo_features)?;
+            let resources = load_extern_resources(cargo_toml, cargo_features);
             out_dir_by_id = resources.out_dirs;
             proc_macro_dylib_paths = resources.proc_dylib_paths;
         }
@@ -183,7 +180,6 @@ impl CargoWorkspace {
                 .with_context(|| format!("Failed to parse edition {}", edition))?;
             let pkg = packages.alloc(PackageData {
                 name,
-                id: id.to_string(),
                 manifest: manifest_path,
                 targets: Vec::new(),
                 is_member,
@@ -253,18 +249,6 @@ impl CargoWorkspace {
     pub fn workspace_root(&self) -> &Path {
         &self.workspace_root
     }
-
-    pub fn package_flag(&self, package: &PackageData) -> String {
-        if self.is_unique(&*package.name) {
-            package.name.clone()
-        } else {
-            package.id.clone()
-        }
-    }
-
-    fn is_unique(&self, name: &str) -> bool {
-        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
-    }
 }
 
 #[derive(Debug, Clone, Default)]
@@ -273,61 +257,48 @@ pub struct ExternResources {
     proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
 }
 
-pub fn load_extern_resources(
-    cargo_toml: &Path,
-    cargo_features: &CargoFeatures,
-) -> Result<ExternResources> {
-    let mut cmd = Command::new(cargo_binary());
-    cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
+pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources {
+    let mut args: Vec<String> = vec![
+        "check".to_string(),
+        "--message-format=json".to_string(),
+        "--manifest-path".to_string(),
+        cargo_toml.display().to_string(),
+    ];
+
     if cargo_features.all_features {
-        cmd.arg("--all-features");
+        args.push("--all-features".to_string());
     } else if cargo_features.no_default_features {
         // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
         // https://github.com/oli-obk/cargo_metadata/issues/79
-        cmd.arg("--no-default-features");
+        args.push("--no-default-features".to_string());
     } else {
-        cmd.args(&cargo_features.features);
+        args.extend(cargo_features.features.iter().cloned());
     }
 
-    let output = cmd.output()?;
-
-    let mut res = ExternResources::default();
-
-    let stdout = String::from_utf8(output.stdout)?;
-    for line in stdout.lines() {
-        if let Ok(message) = serde_json::from_str::<cargo_metadata::Message>(&line) {
-            match message {
-                Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
-                    res.out_dirs.insert(package_id, out_dir);
-                }
+    let mut acc = ExternResources::default();
+    let res = run_cargo(&args, cargo_toml.parent(), &mut |message| {
+        match message {
+            Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
+                acc.out_dirs.insert(package_id, out_dir);
+            }
 
-                Message::CompilerArtifact(message) => {
-                    if message.target.kind.contains(&"proc-macro".to_string()) {
-                        let package_id = message.package_id;
-                        // Skip rmeta file
-                        if let Some(filename) =
-                            message.filenames.iter().filter(|name| is_dylib(name)).next()
-                        {
-                            res.proc_dylib_paths.insert(package_id, filename.clone());
-                        }
+            Message::CompilerArtifact(message) => {
+                if message.target.kind.contains(&"proc-macro".to_string()) {
+                    let package_id = message.package_id;
+                    if let Some(filename) = message.filenames.get(0) {
+                        acc.proc_dylib_paths.insert(package_id, filename.clone());
                     }
                 }
-                Message::CompilerMessage(_) => (),
-                Message::Unknown => (),
             }
+            Message::CompilerMessage(_) => (),
+            Message::Unknown => (),
         }
-    }
-    Ok(res)
-}
+        true
+    });
 
-// FIXME: File a better way to know if it is a dylib
-fn is_dylib(path: &Path) -> bool {
-    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
-        None => false,
-        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
+    if let Err(err) = res {
+        log::error!("Failed to load outdirs: {:?}", err);
     }
-}
 
-fn cargo_binary() -> String {
-    env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
+    acc
 }
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index b69cae234c..2304e00cf9 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -46,46 +46,14 @@ impl ast::FnDef {
     }
 }
 
-fn make_multiline<N>(node: N) -> N
-where
-    N: AstNode + Clone,
-{
-    let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
-        Some(it) => it,
-        None => return node,
-    };
-    let sibling = match l_curly.next_sibling_or_token() {
-        Some(it) => it,
-        None => return node,
-    };
-    let existing_ws = match sibling.as_token() {
-        None => None,
-        Some(tok) if tok.kind() != WHITESPACE => None,
-        Some(ws) => {
-            if ws.text().contains('\n') {
-                return node;
-            }
-            Some(ws.clone())
-        }
-    };
-
-    let indent = leading_indent(node.syntax()).unwrap_or_default();
-    let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
-    let to_insert = iter::once(ws.ws().into());
-    match existing_ws {
-        None => node.insert_children(InsertPosition::After(l_curly), to_insert),
-        Some(ws) => node.replace_children(single_node(ws), to_insert),
-    }
-}
-
 impl ast::ItemList {
     #[must_use]
-    pub fn append_items(&self, items: impl IntoIterator<Item = ast::ImplItem>) -> ast::ItemList {
+    pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList {
         let mut res = self.clone();
         if !self.syntax().text().contains_char('\n') {
-            res = make_multiline(res);
+            res = res.make_multiline();
         }
-        items.into_iter().for_each(|it| res = res.append_item(it));
+        items.for_each(|it| res = res.append_item(it));
         res
     }
 
@@ -113,6 +81,35 @@ impl ast::ItemList {
     fn l_curly(&self) -> Option<SyntaxElement> {
         self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
     }
+
+    fn make_multiline(&self) -> ast::ItemList {
+        let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
+            Some(it) => it,
+            None => return self.clone(),
+        };
+        let sibling = match l_curly.next_sibling_or_token() {
+            Some(it) => it,
+            None => return self.clone(),
+        };
+        let existing_ws = match sibling.as_token() {
+            None => None,
+            Some(tok) if tok.kind() != WHITESPACE => None,
+            Some(ws) => {
+                if ws.text().contains('\n') {
+                    return self.clone();
+                }
+                Some(ws.clone())
+            }
+        };
+
+        let indent = leading_indent(self.syntax()).unwrap_or_default();
+        let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
+        let to_insert = iter::once(ws.ws().into());
+        match existing_ws {
+            None => self.insert_children(InsertPosition::After(l_curly), to_insert),
+            Some(ws) => self.replace_children(single_node(ws), to_insert),
+        }
+    }
 }
 
 impl ast::RecordFieldList {
@@ -337,80 +334,6 @@ impl ast::UseTree {
     }
 }
 
-impl ast::MatchArmList {
-    #[must_use]
-    pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
-        let mut res = self.clone();
-        res = res.strip_if_only_whitespace();
-        if !res.syntax().text().contains_char('\n') {
-            res = make_multiline(res);
-        }
-        items.into_iter().for_each(|it| res = res.append_arm(it));
-        res
-    }
-
-    fn strip_if_only_whitespace(&self) -> ast::MatchArmList {
-        let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']);
-        iter.next(); // Eat the curly
-        let mut inner = iter.take_while(|it| it.kind() != T!['}']);
-        if !inner.clone().all(|it| it.kind() == WHITESPACE) {
-            return self.clone();
-        }
-        let start = match inner.next() {
-            Some(s) => s,
-            None => return self.clone(),
-        };
-        let end = match inner.last() {
-            Some(s) => s,
-            None => start.clone(),
-        };
-        self.replace_children(start..=end, &mut iter::empty())
-    }
-
-    #[must_use]
-    pub fn remove_placeholder(&self) -> ast::MatchArmList {
-        let placeholder =
-            self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::PlaceholderPat(_))));
-        if let Some(placeholder) = placeholder {
-            self.remove_arm(&placeholder)
-        } else {
-            self.clone()
-        }
-    }
-
-    #[must_use]
-    fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
-        let start = arm.syntax().clone();
-        let end = if let Some(comma) = start
-            .siblings_with_tokens(Direction::Next)
-            .skip(1)
-            .skip_while(|it| it.kind().is_trivia())
-            .next()
-            .filter(|it| it.kind() == T![,])
-        {
-            comma
-        } else {
-            start.clone().into()
-        };
-        self.replace_children(start.into()..=end, None)
-    }
-
-    #[must_use]
-    pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
-        let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
-            Some(t) => t,
-            None => return self.clone(),
-        };
-        let position = InsertPosition::Before(r_curly.into());
-        let arm_ws = tokens::WsBuilder::new("    ");
-        let match_indent = &leading_indent(self.syntax()).unwrap_or_default();
-        let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent));
-        let to_insert: ArrayVec<[SyntaxElement; 3]> =
-            [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into();
-        self.insert_children(position, to_insert)
-    }
-}
-
 #[must_use]
 pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
     N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs
index 87bb21cd9a..dd202601d9 100644
--- a/crates/ra_syntax/src/parsing/text_tree_sink.rs
+++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs
@@ -149,21 +149,10 @@ fn n_attached_trivias<'a>(
         MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
         | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => {
             let mut res = 0;
-            let mut trivias = trivias.enumerate().peekable();
-
-            while let Some((i, (kind, text))) = trivias.next() {
+            for (i, (kind, text)) in trivias.enumerate() {
                 match kind {
                     WHITESPACE => {
                         if text.contains("\n\n") {
-                            // we check whether the next token is a doc-comment
-                            // and skip the whitespace in this case
-                            if let Some((peek_kind, peek_text)) =
-                                trivias.peek().map(|(_, pair)| pair)
-                            {
-                                if *peek_kind == COMMENT && peek_text.starts_with("///") {
-                                    continue;
-                                }
-                            }
                             break;
                         }
                     }
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index bd484aa30a..1015ce0a60 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -189,12 +189,7 @@ impl Subtree {
 pub mod buffer;
 
 #[derive(Debug, PartialEq, Eq)]
-pub enum ExpansionError {
-    IOError(String),
-    JsonError(String),
-    Unknown(String),
-    ExpansionError(String),
-}
+pub enum ExpansionError {}
 
 pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe {
     fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index f5f7734325..8fe6799d23 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -33,7 +33,7 @@ threadpool = "1.7.1"
 stdx = { path = "../stdx" }
 
 lsp-server = "0.3.1"
-ra_flycheck = { path = "../ra_flycheck" }
+ra_cargo_watch = { path = "../ra_cargo_watch" }
 ra_ide = { path = "../ra_ide" }
 ra_prof = { path = "../ra_prof" }
 ra_project_model = { path = "../ra_project_model" }
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 942c303287..f87bdcec5c 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -77,7 +77,7 @@ impl CargoTargetSpec {
             ProjectWorkspace::Cargo { cargo, .. } => {
                 let tgt = cargo.target_by_root(&path)?;
                 Some(CargoTargetSpec {
-                    package: cargo.package_flag(&cargo[cargo[tgt].package]),
+                    package: cargo[cargo[tgt].package].name.clone(),
                     target: cargo[tgt].name.clone(),
                     target_kind: cargo[tgt].kind,
                 })
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 7667873d5f..7164b0adeb 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -12,7 +12,7 @@ use ra_db::{
     salsa::{Database, Durability},
     FileId, SourceDatabaseExt,
 };
-use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol};
+use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionOptions, FilePosition, LineCol};
 
 use crate::cli::{load_cargo::load_cargo, Verbosity};
 
@@ -102,7 +102,7 @@ pub fn analysis_bench(
             let file_position = FilePosition { file_id, offset };
 
             if is_completion {
-                let options = CompletionConfig::default();
+                let options = CompletionOptions::default();
                 let res = do_work(&mut host, file_id, |analysis| {
                     analysis.completions(file_position, &options)
                 });
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index e8dc953c38..6edc03fe0f 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -579,7 +579,7 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>)
             .into_iter()
             .map(|nav| (file_id, RangeInfo::new(range, nav)))
             .try_conv_with_to_vec(world)?;
-        if world.config.supports_location_link {
+        if world.options.supports_location_link {
             Ok(links.into())
         } else {
             let locations: Vec<Location> = links
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index bb33fb27d5..7825b00779 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -14,15 +14,14 @@ use std::{
     time::{Duration, Instant},
 };
 
-use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
+use crossbeam_channel::{select, unbounded, RecvError, Sender};
 use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
 use lsp_types::{
-    ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress,
-    WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
-    WorkDoneProgressReport,
+    ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin,
+    WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport,
 };
-use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask};
-use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId};
+use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask};
+use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId};
 use ra_prof::profile;
 use ra_vfs::{VfsFile, VfsTask, Watch};
 use relative_path::RelativePathBuf;
@@ -38,10 +37,9 @@ use crate::{
         subscriptions::Subscriptions,
     },
     req,
-    world::{Config, WorldSnapshot, WorldState},
+    world::{Options, WorldSnapshot, WorldState},
     Result, ServerConfig,
 };
-use req::ConfigurationParams;
 
 #[derive(Debug)]
 pub struct LspError {
@@ -65,54 +63,6 @@ impl fmt::Display for LspError {
 
 impl Error for LspError {}
 
-fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureFlags {
-    let mut ff = FeatureFlags::default();
-    for (flag, &value) in &config.feature_flags {
-        if ff.set(flag.as_str(), value).is_err() {
-            log::error!("unknown feature flag: {:?}", flag);
-            show_message(
-                req::MessageType::Error,
-                format!("unknown feature flag: {:?}", flag),
-                &connection.sender,
-            );
-        }
-    }
-    log::info!("feature_flags: {:#?}", ff);
-    ff
-}
-
-fn get_config(
-    config: &ServerConfig,
-    text_document_caps: Option<&TextDocumentClientCapabilities>,
-) -> Config {
-    Config {
-        publish_decorations: config.publish_decorations,
-        supports_location_link: text_document_caps
-            .and_then(|it| it.definition)
-            .and_then(|it| it.link_support)
-            .unwrap_or(false),
-        line_folding_only: text_document_caps
-            .and_then(|it| it.folding_range.as_ref())
-            .and_then(|it| it.line_folding_only)
-            .unwrap_or(false),
-        inlay_hints: InlayHintsConfig {
-            type_hints: config.inlay_hints_type,
-            parameter_hints: config.inlay_hints_parameter,
-            chaining_hints: config.inlay_hints_chaining,
-            max_length: config.inlay_hints_max_length,
-        },
-        check: CheckConfig {
-            enable: config.cargo_watch_enable,
-            args: config.cargo_watch_args.clone(),
-            command: config.cargo_watch_command.clone(),
-            all_targets: config.cargo_watch_all_targets,
-        },
-        rustfmt_args: config.rustfmt_args.clone(),
-        vscode_lldb: config.vscode_lldb,
-        proc_macro_srv: None, // FIXME: get this from config
-    }
-}
-
 pub fn main_loop(
     ws_roots: Vec<PathBuf>,
     client_caps: ClientCapabilities,
@@ -140,10 +90,23 @@ pub fn main_loop(
         SetThreadPriority(thread, thread_priority_above_normal);
     }
 
-    let text_document_caps = client_caps.text_document.as_ref();
     let mut loop_state = LoopState::default();
     let mut world_state = {
-        let feature_flags = get_feature_flags(&config, &connection);
+        let feature_flags = {
+            let mut ff = FeatureFlags::default();
+            for (flag, value) in config.feature_flags {
+                if ff.set(flag.as_str(), value).is_err() {
+                    log::error!("unknown feature flag: {:?}", flag);
+                    show_message(
+                        req::MessageType::Error,
+                        format!("unknown feature flag: {:?}", flag),
+                        &connection.sender,
+                    );
+                }
+            }
+            ff
+        };
+        log::info!("feature_flags: {:#?}", feature_flags);
 
         // FIXME: support dynamic workspace loading.
         let workspaces = {
@@ -205,20 +168,46 @@ pub fn main_loop(
             connection.sender.send(request.into()).unwrap();
         }
 
+        let options = {
+            let text_document_caps = client_caps.text_document.as_ref();
+            Options {
+                publish_decorations: config.publish_decorations,
+                supports_location_link: text_document_caps
+                    .and_then(|it| it.definition)
+                    .and_then(|it| it.link_support)
+                    .unwrap_or(false),
+                line_folding_only: text_document_caps
+                    .and_then(|it| it.folding_range.as_ref())
+                    .and_then(|it| it.line_folding_only)
+                    .unwrap_or(false),
+                inlay_hints: InlayHintsOptions {
+                    type_hints: config.inlay_hints_type,
+                    parameter_hints: config.inlay_hints_parameter,
+                    chaining_hints: config.inlay_hints_chaining,
+                    max_length: config.inlay_hints_max_length,
+                },
+                cargo_watch: CheckOptions {
+                    enable: config.cargo_watch_enable,
+                    args: config.cargo_watch_args,
+                    command: config.cargo_watch_command,
+                    all_targets: config.cargo_watch_all_targets,
+                },
+                rustfmt_args: config.rustfmt_args,
+                vscode_lldb: config.vscode_lldb,
+            }
+        };
+
         WorldState::new(
             ws_roots,
             workspaces,
             config.lru_capacity,
             &globs,
             Watch(!config.use_client_watching),
-            get_config(&config, text_document_caps),
+            options,
             feature_flags,
         )
     };
 
-    loop_state.roots_total = world_state.vfs.read().n_roots();
-    loop_state.roots_scanned = 0;
-
     let pool = ThreadPool::default();
     let (task_sender, task_receiver) = unbounded::<Task>();
     let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>();
@@ -240,7 +229,7 @@ pub fn main_loop(
                     Err(RecvError) => return Err("vfs died".into()),
                 },
                 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
-                recv(world_state.check_watcher.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
+                recv(world_state.check_watcher.task_recv) -> task => match task {
                     Ok(task) => Event::CheckWatcher(task),
                     Err(RecvError) => return Err("check watcher died".into()),
                 }
@@ -255,7 +244,6 @@ pub fn main_loop(
                 &task_sender,
                 &libdata_sender,
                 &connection,
-                text_document_caps,
                 &mut world_state,
                 &mut loop_state,
                 event,
@@ -345,10 +333,7 @@ struct LoopState {
     in_flight_libraries: usize,
     pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
     workspace_loaded: bool,
-    roots_progress_reported: Option<usize>,
-    roots_scanned: usize,
-    roots_total: usize,
-    configuration_request_id: Option<RequestId>,
+    roots_scanned_progress: Option<usize>,
 }
 
 impl LoopState {
@@ -366,7 +351,6 @@ fn loop_turn(
     task_sender: &Sender<Task>,
     libdata_sender: &Sender<LibraryData>,
     connection: &Connection,
-    text_document_caps: Option<&TextDocumentClientCapabilities>,
     world_state: &mut WorldState,
     loop_state: &mut LoopState,
     event: Event,
@@ -393,7 +377,6 @@ fn loop_turn(
             world_state.add_lib(lib);
             world_state.maybe_collect_garbage();
             loop_state.in_flight_libraries -= 1;
-            loop_state.roots_scanned += 1;
         }
         Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?,
         Event::Msg(msg) => match msg {
@@ -407,53 +390,25 @@ fn loop_turn(
                 req,
             )?,
             Message::Notification(not) => {
-                on_notification(&connection.sender, world_state, loop_state, not)?;
+                on_notification(
+                    &connection.sender,
+                    world_state,
+                    &mut loop_state.pending_requests,
+                    &mut loop_state.subscriptions,
+                    not,
+                )?;
             }
             Message::Response(resp) => {
                 let removed = loop_state.pending_responses.remove(&resp.id);
                 if !removed {
                     log::error!("unexpected response: {:?}", resp)
                 }
-
-                if Some(&resp.id) == loop_state.configuration_request_id.as_ref() {
-                    loop_state.configuration_request_id = None;
-                    log::debug!("config update response: '{:?}", resp);
-                    let Response { error, result, .. } = resp;
-
-                    match (
-                        error,
-                        result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)),
-                    ) {
-                        (Some(err), _) => {
-                            log::error!("failed to fetch the server settings: {:?}", err)
-                        }
-                        (None, Some(Ok(new_config))) => {
-                            let new_config = new_config
-                                .first()
-                                .expect(
-                                    "the client is expected to always send a non-empty config data",
-                                )
-                                .to_owned();
-                            world_state.update_configuration(
-                                new_config.lru_capacity,
-                                get_config(&new_config, text_document_caps),
-                                get_feature_flags(&new_config, connection),
-                            );
-                        }
-                        (None, Some(Err(e))) => {
-                            log::error!("failed to parse client config response: {}", e)
-                        }
-                        (None, None) => {
-                            log::error!("received empty server settings response from the client")
-                        }
-                    }
-                }
             }
         },
     };
 
     let mut state_changed = false;
-    if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) {
+    if let Some(changes) = world_state.process_changes() {
         state_changed = true;
         loop_state.pending_libraries.extend(changes);
     }
@@ -472,34 +427,28 @@ fn loop_turn(
         });
     }
 
-    let show_progress = !loop_state.workspace_loaded
-        && world_state.feature_flags.get("notifications.workspace-loaded");
-
     if !loop_state.workspace_loaded
-        && loop_state.roots_scanned == loop_state.roots_total
+        && world_state.roots_to_scan == 0
         && loop_state.pending_libraries.is_empty()
         && loop_state.in_flight_libraries == 0
     {
         loop_state.workspace_loaded = true;
-        if let Some(check_watcher) = &world_state.check_watcher {
-            check_watcher.update();
-        }
+        world_state.check_watcher.update();
         pool.execute({
             let subs = loop_state.subscriptions.subscriptions();
             let snap = world_state.snapshot();
             move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
         });
-    }
-
-    if show_progress {
-        send_startup_progress(&connection.sender, loop_state);
+        send_startup_progress(&connection.sender, loop_state, world_state);
+    } else if !loop_state.workspace_loaded {
+        send_startup_progress(&connection.sender, loop_state, world_state);
     }
 
     if state_changed {
         update_file_notifications_on_threadpool(
             pool,
             world_state.snapshot(),
-            world_state.config.publish_decorations,
+            world_state.options.publish_decorations,
             task_sender.clone(),
             loop_state.subscriptions.subscriptions(),
         )
@@ -607,7 +556,8 @@ fn on_request(
 fn on_notification(
     msg_sender: &Sender<Message>,
     state: &mut WorldState,
-    loop_state: &mut LoopState,
+    pending_requests: &mut PendingRequests,
+    subs: &mut Subscriptions,
     not: Notification,
 ) -> Result<()> {
     let not = match notification_cast::<req::Cancel>(not) {
@@ -616,7 +566,7 @@ fn on_notification(
                 NumberOrString::Number(id) => id.into(),
                 NumberOrString::String(id) => id.into(),
             };
-            if loop_state.pending_requests.cancel(&id) {
+            if pending_requests.cancel(&id) {
                 let response = Response::new_err(
                     id,
                     ErrorCode::RequestCanceled as i32,
@@ -635,7 +585,7 @@ fn on_notification(
             if let Some(file_id) =
                 state.vfs.write().add_file_overlay(&path, params.text_document.text)
             {
-                loop_state.subscriptions.add_sub(FileId(file_id.0));
+                subs.add_sub(FileId(file_id.0));
             }
             return Ok(());
         }
@@ -654,9 +604,7 @@ fn on_notification(
     };
     let not = match notification_cast::<req::DidSaveTextDocument>(not) {
         Ok(_params) => {
-            if let Some(check_watcher) = &state.check_watcher {
-                check_watcher.update();
-            }
+            state.check_watcher.update();
             return Ok(());
         }
         Err(not) => not,
@@ -666,7 +614,7 @@ fn on_notification(
             let uri = params.text_document.uri;
             let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
             if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) {
-                loop_state.subscriptions.remove_sub(FileId(file_id.0));
+                subs.remove_sub(FileId(file_id.0));
             }
             let params =
                 req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None };
@@ -677,17 +625,7 @@ fn on_notification(
         Err(not) => not,
     };
     let not = match notification_cast::<req::DidChangeConfiguration>(not) {
-        Ok(_) => {
-            // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
-            // this notification's parameters should be ignored and the actual config queried separately.
-            let request_id = loop_state.next_request_id();
-            let request = request_new::<req::WorkspaceConfiguration>(
-                request_id.clone(),
-                ConfigurationParams::default(),
-            );
-            msg_sender.send(request.into())?;
-            loop_state.configuration_request_id = Some(request_id);
-
+        Ok(_params) => {
             return Ok(());
         }
         Err(not) => not,
@@ -768,13 +706,21 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
     }
 }
 
-fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
-    let total: usize = loop_state.roots_total;
-    let prev = loop_state.roots_progress_reported;
-    let progress = loop_state.roots_scanned;
-    loop_state.roots_progress_reported = Some(progress);
+fn send_startup_progress(
+    sender: &Sender<Message>,
+    loop_state: &mut LoopState,
+    world_state: &WorldState,
+) {
+    if !world_state.feature_flags.get("notifications.workspace-loaded") {
+        return;
+    }
 
-    match (prev, loop_state.workspace_loaded) {
+    let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
+    let prev_progress = loop_state.roots_scanned_progress;
+    let progress = total - world_state.roots_to_scan;
+    loop_state.roots_scanned_progress = Some(progress);
+
+    match (prev_progress, loop_state.workspace_loaded) {
         (None, false) => {
             let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>(
                 loop_state.next_request_id(),
@@ -809,14 +755,14 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
         ),
         _ => {}
     }
+}
 
-    fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
-        let notif = notification_new::<req::Progress>(req::ProgressParams {
-            token: req::ProgressToken::String("rustAnalyzer/startup".into()),
-            value: req::ProgressParamsValue::WorkDone(work_done_progress),
-        });
-        sender.send(notif.into()).unwrap();
-    }
+fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
+    let notif = notification_new::<req::Progress>(req::ProgressParams {
+        token: req::ProgressToken::String("rustAnalyzer/startup".into()),
+        value: req::ProgressParamsValue::WorkDone(work_done_progress),
+    });
+    sender.send(notif.into()).unwrap();
 }
 
 struct PoolDispatcher<'a> {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index d5cb5d1372..12f8ca2976 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -14,12 +14,12 @@ use lsp_types::{
     CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
     DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
     Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
-    Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
+    Range, RenameParams, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams,
     SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
     TextEdit, WorkspaceEdit,
 };
 use ra_ide::{
-    Assist, AssistId, CompletionConfig, FileId, FilePosition, FileRange, Query, RangeInfo,
+    Assist, AssistId, CompletionOptions, FileId, FilePosition, FileRange, Query, RangeInfo,
     Runnable, RunnableKind, SearchScope,
 };
 use ra_prof::profile;
@@ -425,7 +425,7 @@ pub fn handle_completion(
         return Ok(None);
     }
 
-    let config = CompletionConfig {
+    let options = CompletionOptions {
         enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"),
         add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"),
         add_call_argument_snippets: world
@@ -433,7 +433,7 @@ pub fn handle_completion(
             .get("completion.insertion.add-argument-snippets"),
     };
 
-    let items = match world.analysis().completions(position, &config)? {
+    let items = match world.analysis().completions(position, &options)? {
         None => return Ok(None),
         Some(items) => items,
     };
@@ -457,7 +457,7 @@ pub fn handle_folding_range(
     let ctx = FoldConvCtx {
         text: &text,
         line_index: &line_index,
-        line_folding_only: world.config.line_folding_only,
+        line_folding_only: world.options.line_folding_only,
     };
     let res = Some(folds.into_iter().map_conv_with(&ctx).collect());
     Ok(res)
@@ -611,7 +611,7 @@ pub fn handle_formatting(
     let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
 
     let mut rustfmt = process::Command::new("rustfmt");
-    rustfmt.args(&world.config.rustfmt_args);
+    rustfmt.args(&world.options.rustfmt_args);
     if let Some(&crate_id) = crate_ids.first() {
         // Assume all crates are in the same edition
         let edition = world.analysis().crate_edition(crate_id)?;
@@ -815,7 +815,7 @@ pub fn handle_code_lens(
         };
         lenses.push(lens);
 
-        if world.config.vscode_lldb {
+        if world.options.vscode_lldb {
             if r.args[0] == "run" {
                 r.args[0] = "build".into();
             } else {
@@ -1028,7 +1028,7 @@ pub fn handle_inlay_hints(
     let analysis = world.analysis();
     let line_index = analysis.file_line_index(file_id)?;
     Ok(analysis
-        .inlay_hints(file_id, &world.config.inlay_hints)?
+        .inlay_hints(file_id, &world.options.inlay_hints)?
         .into_iter()
         .map_conv_with(&line_index)
         .collect())
@@ -1145,7 +1145,7 @@ pub fn handle_semantic_tokens(
         }
     }
 
-    let tokens = builder.build();
+    let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
 
     Ok(Some(tokens.into()))
 }
@@ -1166,7 +1166,7 @@ pub fn handle_semantic_tokens_range(
         builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers);
     }
 
-    let tokens = builder.build();
+    let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
 
     Ok(Some(tokens.into()))
 }
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 994f0ed614..8557294f62 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
 
 pub use lsp_types::{
     notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
-    CodeLensParams, CompletionParams, CompletionResponse, ConfigurationParams, DiagnosticTag,
+    CodeLensParams, CompletionParams, CompletionResponse, DiagnosticTag,
     DidChangeConfigurationParams, DidChangeWatchedFilesParams,
     DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
     DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2a66bbfd85..d9ba770509 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -2,7 +2,7 @@
 
 use std::ops;
 
-use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens};
+use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType};
 
 pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute");
 pub(crate) const BUILTIN_TYPE: SemanticTokenType = SemanticTokenType::new("builtinType");
@@ -109,8 +109,8 @@ impl SemanticTokensBuilder {
         self.prev_char = range.start.character as u32;
     }
 
-    pub fn build(self) -> SemanticTokens {
-        SemanticTokens { result_id: None, data: self.data }
+    pub fn build(self) -> Vec<SemanticToken> {
+        self.data
     }
 }
 
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index acb729bae7..64a7b907ed 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -11,9 +11,9 @@ use std::{
 use crossbeam_channel::{unbounded, Receiver};
 use lsp_types::Url;
 use parking_lot::RwLock;
-use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckWatcher};
+use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher};
 use ra_ide::{
-    Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData,
+    Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData,
     SourceRootId,
 };
 use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
@@ -31,34 +31,15 @@ use crate::{
 use ra_db::ExternSourceId;
 use rustc_hash::{FxHashMap, FxHashSet};
 
-fn create_watcher(workspaces: &[ProjectWorkspace], config: &Config) -> Option<CheckWatcher> {
-    // FIXME: Figure out the multi-workspace situation
-    workspaces
-        .iter()
-        .find_map(|w| match w {
-            ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
-            ProjectWorkspace::Json { .. } => None,
-        })
-        .map(|cargo| {
-            let cargo_project_root = cargo.workspace_root().to_path_buf();
-            Some(CheckWatcher::new(config.check.clone(), cargo_project_root))
-        })
-        .unwrap_or_else(|| {
-            log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
-            None
-        })
-}
-
 #[derive(Debug, Clone)]
-pub struct Config {
+pub struct Options {
     pub publish_decorations: bool,
     pub supports_location_link: bool,
     pub line_folding_only: bool,
-    pub inlay_hints: InlayHintsConfig,
+    pub inlay_hints: InlayHintsOptions,
     pub rustfmt_args: Vec<String>,
-    pub check: CheckConfig,
+    pub cargo_watch: CheckOptions,
     pub vscode_lldb: bool,
-    pub proc_macro_srv: Option<String>,
 }
 
 /// `WorldState` is the primary mutable state of the language server
@@ -68,21 +49,23 @@ pub struct Config {
 /// incremental salsa database.
 #[derive(Debug)]
 pub struct WorldState {
-    pub config: Config,
+    pub options: Options,
     pub feature_flags: Arc<FeatureFlags>,
+    //FIXME: this belongs to `LoopState` rather than to `WorldState`
+    pub roots_to_scan: usize,
     pub roots: Vec<PathBuf>,
     pub workspaces: Arc<Vec<ProjectWorkspace>>,
     pub analysis_host: AnalysisHost,
     pub vfs: Arc<RwLock<Vfs>>,
     pub task_receiver: Receiver<VfsTask>,
     pub latest_requests: Arc<RwLock<LatestRequests>>,
-    pub check_watcher: Option<CheckWatcher>,
+    pub check_watcher: CheckWatcher,
     pub diagnostics: DiagnosticCollection,
 }
 
 /// An immutable snapshot of the world's state at a point in time.
 pub struct WorldSnapshot {
-    pub config: Config,
+    pub options: Options,
     pub feature_flags: Arc<FeatureFlags>,
     pub workspaces: Arc<Vec<ProjectWorkspace>>,
     pub analysis: Analysis,
@@ -98,7 +81,7 @@ impl WorldState {
         lru_capacity: Option<usize>,
         exclude_globs: &[Glob],
         watch: Watch,
-        config: Config,
+        options: Options,
         feature_flags: FeatureFlags,
     ) -> WorldState {
         let mut change = AnalysisChange::new();
@@ -140,7 +123,7 @@ impl WorldState {
         let (task_sender, task_receiver) = unbounded();
         let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
         let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
-
+        let roots_to_scan = vfs_roots.len();
         for r in vfs_roots {
             let vfs_root_path = vfs.root2path(r);
             let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
@@ -168,23 +151,8 @@ impl WorldState {
             vfs_file.map(|f| FileId(f.0))
         };
 
-        let proc_macro_client = match &config.proc_macro_srv {
-            None => ProcMacroClient::dummy(),
-            Some(srv) => {
-                let path = Path::new(&srv);
-                match ProcMacroClient::extern_process(path) {
-                    Ok(it) => it,
-                    Err(err) => {
-                        log::error!(
-                            "Fail to run ra_proc_macro_srv from path {}, error : {}",
-                            path.to_string_lossy(),
-                            err
-                        );
-                        ProcMacroClient::dummy()
-                    }
-                }
-            }
-        };
+        let proc_macro_client =
+            ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv"));
 
         workspaces
             .iter()
@@ -201,13 +169,28 @@ impl WorldState {
             });
         change.set_crate_graph(crate_graph);
 
-        let check_watcher = create_watcher(&workspaces, &config);
+        // FIXME: Figure out the multi-workspace situation
+        let check_watcher = workspaces
+            .iter()
+            .find_map(|w| match w {
+                ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
+                ProjectWorkspace::Json { .. } => None,
+            })
+            .map(|cargo| {
+                let cargo_project_root = cargo.workspace_root().to_path_buf();
+                CheckWatcher::new(&options.cargo_watch, cargo_project_root)
+            })
+            .unwrap_or_else(|| {
+                log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
+                CheckWatcher::dummy()
+            });
 
         let mut analysis_host = AnalysisHost::new(lru_capacity);
         analysis_host.apply_change(change);
         WorldState {
-            config: config,
+            options,
             feature_flags: Arc::new(feature_flags),
+            roots_to_scan,
             roots: folder_roots,
             workspaces: Arc::new(workspaces),
             analysis_host,
@@ -219,23 +202,10 @@ impl WorldState {
         }
     }
 
-    pub fn update_configuration(
-        &mut self,
-        lru_capacity: Option<usize>,
-        config: Config,
-        feature_flags: FeatureFlags,
-    ) {
-        self.feature_flags = Arc::new(feature_flags);
-        self.analysis_host.update_lru_capacity(lru_capacity);
-        self.check_watcher = create_watcher(&self.workspaces, &config);
-        self.config = config;
-    }
-
     /// Returns a vec of libraries
     /// FIXME: better API here
     pub fn process_changes(
         &mut self,
-        roots_scanned: &mut usize,
     ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
         let changes = self.vfs.write().commit_changes();
         if changes.is_empty() {
@@ -249,7 +219,7 @@ impl WorldState {
                     let root_path = self.vfs.read().root2path(root);
                     let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
                     if is_local {
-                        *roots_scanned += 1;
+                        self.roots_to_scan -= 1;
                         for (file, path, text) in files {
                             change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                         }
@@ -277,6 +247,7 @@ impl WorldState {
     }
 
     pub fn add_lib(&mut self, data: LibraryData) {
+        self.roots_to_scan -= 1;
         let mut change = AnalysisChange::new();
         change.add_library(data);
         self.analysis_host.apply_change(change);
@@ -284,7 +255,7 @@ impl WorldState {
 
     pub fn snapshot(&self) -> WorldSnapshot {
         WorldSnapshot {
-            config: self.config.clone(),
+            options: self.options.clone(),
             feature_flags: Arc::clone(&self.feature_flags),
             workspaces: Arc::clone(&self.workspaces),
             analysis: self.analysis_host.analysis(),
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index d2efa22363..8492c17af2 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -5,7 +5,7 @@ use std::{cell::Cell, fmt};
 /// Appends formatted string to a `String`.
 #[macro_export]
 macro_rules! format_to {
-    ($buf:expr) => ();
+    (&buf:expr) => ();
     ($buf:expr, $lit:literal $($arg:tt)*) => {
         { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
     };
diff --git a/docs/user/features.md b/docs/user/features.md
index 8aeec2e81e..56d2969fd4 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -81,12 +81,6 @@ Join selected lines into one, smartly fixing up whitespace and trailing commas.
 Shows the parse tree of the current file. It exists mostly for debugging
 rust-analyzer itself.
 
-You can hover over syntax nodes in the opened text file to see the appropriate
-rust code that it refers to and the rust editor will also highlight the proper
-text range.
-
-<img src="https://user-images.githubusercontent.com/36276403/78043783-7425e180-737c-11ea-8653-b02b773c5aa1.png" alt="demo" height="200px" >
-
 #### Expand Macro Recursively
 
 Shows the full macro expansion of the macro at current cursor.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 955a050666..b7220d8e20 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -84,9 +84,9 @@
             "dev": true
         },
         "@types/node": {
-            "version": "12.12.34",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz",
-            "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==",
+            "version": "12.12.30",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.30.tgz",
+            "integrity": "sha512-sz9MF/zk6qVr3pAnM0BSQvYIBK44tS75QC5N+VbWSE4DjCV/pJ+UzCW/F+vVnl7TkOPcuwQureKNtSSwjBTaMg==",
             "dev": true
         },
         "@types/node-fetch": {
@@ -115,56 +115,45 @@
             "dev": true
         },
         "@typescript-eslint/eslint-plugin": {
-            "version": "2.26.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz",
-            "integrity": "sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw==",
+            "version": "2.24.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz",
+            "integrity": "sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/experimental-utils": "2.26.0",
+                "@typescript-eslint/experimental-utils": "2.24.0",
+                "eslint-utils": "^1.4.3",
                 "functional-red-black-tree": "^1.0.1",
                 "regexpp": "^3.0.0",
                 "tsutils": "^3.17.1"
             }
         },
         "@typescript-eslint/experimental-utils": {
-            "version": "2.26.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz",
-            "integrity": "sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ==",
+            "version": "2.24.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz",
+            "integrity": "sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw==",
             "dev": true,
             "requires": {
                 "@types/json-schema": "^7.0.3",
-                "@typescript-eslint/typescript-estree": "2.26.0",
-                "eslint-scope": "^5.0.0",
-                "eslint-utils": "^2.0.0"
-            },
-            "dependencies": {
-                "eslint-utils": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
-                    "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
-                    "dev": true,
-                    "requires": {
-                        "eslint-visitor-keys": "^1.1.0"
-                    }
-                }
+                "@typescript-eslint/typescript-estree": "2.24.0",
+                "eslint-scope": "^5.0.0"
             }
         },
         "@typescript-eslint/parser": {
-            "version": "2.26.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.26.0.tgz",
-            "integrity": "sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ==",
+            "version": "2.24.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.24.0.tgz",
+            "integrity": "sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw==",
             "dev": true,
             "requires": {
                 "@types/eslint-visitor-keys": "^1.0.0",
-                "@typescript-eslint/experimental-utils": "2.26.0",
-                "@typescript-eslint/typescript-estree": "2.26.0",
+                "@typescript-eslint/experimental-utils": "2.24.0",
+                "@typescript-eslint/typescript-estree": "2.24.0",
                 "eslint-visitor-keys": "^1.1.0"
             }
         },
         "@typescript-eslint/typescript-estree": {
-            "version": "2.26.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz",
-            "integrity": "sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg==",
+            "version": "2.24.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz",
+            "integrity": "sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA==",
             "dev": true,
             "requires": {
                 "debug": "^4.1.1",
@@ -1399,9 +1388,9 @@
             }
         },
         "rollup": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.1.tgz",
-            "integrity": "sha512-BRjzOauORe+R0U0I6SkMTSG22nYmtztR/TaBl0SvbXgc3VAxBDrZoB6HROiK0S5px1pUBnLnjBkbzmVuwC9Q1Q==",
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz",
+            "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==",
             "dev": true,
             "requires": {
                 "fsevents": "~2.1.2"
diff --git a/editors/code/package.json b/editors/code/package.json
index ba31c4e63d..37e0832208 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -39,13 +39,13 @@
     "devDependencies": {
         "@rollup/plugin-commonjs": "^11.0.2",
         "@rollup/plugin-node-resolve": "^7.1.1",
-        "@types/node": "^12.12.34",
+        "@types/node": "^12.12.30",
         "@types/node-fetch": "^2.5.5",
         "@types/vscode": "^1.43.0",
-        "@typescript-eslint/eslint-plugin": "^2.26.0",
-        "@typescript-eslint/parser": "^2.26.0",
+        "@typescript-eslint/eslint-plugin": "^2.24.0",
+        "@typescript-eslint/parser": "^2.24.0",
         "eslint": "^6.8.0",
-        "rollup": "^2.3.1",
+        "rollup": "^2.1.0",
         "tslib": "^1.11.1",
         "typescript": "^3.8.3",
         "typescript-formatter": "^7.2.2",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index f909f8db26..98f2f232f4 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -5,39 +5,15 @@ import { Config } from './config';
 import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
 import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
 
-export function configToServerOptions(config: Config) {
-    return {
-        publishDecorations: !config.highlightingSemanticTokens,
-        lruCapacity: config.lruCapacity,
-
-        inlayHintsType: config.inlayHints.typeHints,
-        inlayHintsParameter: config.inlayHints.parameterHints,
-        inlayHintsChaining: config.inlayHints.chainingHints,
-        inlayHintsMaxLength: config.inlayHints.maxLength,
-
-        cargoWatchEnable: config.cargoWatchOptions.enable,
-        cargoWatchArgs: config.cargoWatchOptions.arguments,
-        cargoWatchCommand: config.cargoWatchOptions.command,
-        cargoWatchAllTargets: config.cargoWatchOptions.allTargets,
-
-        excludeGlobs: config.excludeGlobs,
-        useClientWatching: config.useClientWatching,
-        featureFlags: config.featureFlags,
-        withSysroot: config.withSysroot,
-        cargoFeatures: config.cargoFeatures,
-        rustfmtArgs: config.rustfmtArgs,
-        vscodeLldb: vscode.extensions.getExtension("vadimcn.vscode-lldb") != null,
-    };
-}
-
-export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> {
+export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> {
     // '.' Is the fallback if no folder is open
     // TODO?: Workspace folders support Uri's (eg: file://test.txt).
     // It might be a good idea to test if the uri points to a file.
+    const workspaceFolderPath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '.';
 
     const run: lc.Executable = {
         command: serverPath,
-        options: { cwd },
+        options: { cwd: workspaceFolderPath },
     };
     const serverOptions: lc.ServerOptions = {
         run,
@@ -46,10 +22,32 @@ export async function createClient(config: Config, serverPath: string, cwd: stri
     const traceOutputChannel = vscode.window.createOutputChannel(
         'Rust Analyzer Language Server Trace',
     );
+    const cargoWatchOpts = config.cargoWatchOptions;
 
     const clientOptions: lc.LanguageClientOptions = {
         documentSelector: [{ scheme: 'file', language: 'rust' }],
-        initializationOptions: configToServerOptions(config),
+        initializationOptions: {
+            publishDecorations: !config.highlightingSemanticTokens,
+            lruCapacity: config.lruCapacity,
+
+            inlayHintsType: config.inlayHints.typeHints,
+            inlayHintsParameter: config.inlayHints.parameterHints,
+            inlayHintsChaining: config.inlayHints.chainingHints,
+            inlayHintsMaxLength: config.inlayHints.maxLength,
+
+            cargoWatchEnable: cargoWatchOpts.enable,
+            cargoWatchArgs: cargoWatchOpts.arguments,
+            cargoWatchCommand: cargoWatchOpts.command,
+            cargoWatchAllTargets: cargoWatchOpts.allTargets,
+
+            excludeGlobs: config.excludeGlobs,
+            useClientWatching: config.useClientWatching,
+            featureFlags: config.featureFlags,
+            withSysroot: config.withSysroot,
+            cargoFeatures: config.cargoFeatures,
+            rustfmtArgs: config.rustfmtArgs,
+            vscodeLldb: vscode.extensions.getExtension("vadimcn.vscode-lldb") != null,
+        },
         traceOutputChannel,
         middleware: {
             // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 996c7a7169..2e08e8f115 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -1,10 +1,8 @@
 import * as vscode from 'vscode';
 import * as ra from '../rust-analyzer-api';
 
-import { Ctx, Cmd, Disposable } from '../ctx';
-import { isRustDocument, RustEditor, isRustEditor, sleep } from '../util';
-
-const AST_FILE_SCHEME = "rust-analyzer";
+import { Ctx, Cmd } from '../ctx';
+import { isRustDocument } from '../util';
 
 // Opens the virtual file that will show the syntax tree
 //
@@ -12,13 +10,35 @@ const AST_FILE_SCHEME = "rust-analyzer";
 export function syntaxTree(ctx: Ctx): Cmd {
     const tdcp = new TextDocumentContentProvider(ctx);
 
-    void new AstInspector(ctx);
-
-    ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
+    ctx.pushCleanup(
+        vscode.workspace.registerTextDocumentContentProvider(
+            'rust-analyzer',
+            tdcp,
+        ),
+    );
+
+    vscode.workspace.onDidChangeTextDocument(
+        (event: vscode.TextDocumentChangeEvent) => {
+            const doc = event.document;
+            if (!isRustDocument(doc)) return;
+            afterLs(() => tdcp.eventEmitter.fire(tdcp.uri));
+        },
+        null,
+        ctx.subscriptions,
+    );
+
+    vscode.window.onDidChangeActiveTextEditor(
+        (editor: vscode.TextEditor | undefined) => {
+            if (!editor || !isRustDocument(editor.document)) return;
+            tdcp.eventEmitter.fire(tdcp.uri);
+        },
+        null,
+        ctx.subscriptions,
+    );
 
     return async () => {
         const editor = vscode.window.activeTextEditor;
-        const rangeEnabled = !!editor && !editor.selection.isEmpty;
+        const rangeEnabled = !!(editor && !editor.selection.isEmpty);
 
         const uri = rangeEnabled
             ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
@@ -28,126 +48,45 @@ export function syntaxTree(ctx: Ctx): Cmd {
 
         tdcp.eventEmitter.fire(uri);
 
-        void await vscode.window.showTextDocument(document, {
-            viewColumn: vscode.ViewColumn.Two,
-            preserveFocus: true
-        });
+        return vscode.window.showTextDocument(
+            document,
+            vscode.ViewColumn.Two,
+            true,
+        );
     };
 }
 
-class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
-    readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
-    readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
+// We need to order this after LS updates, but there's no API for that.
+// Hence, good old setTimeout.
+function afterLs(f: () => void) {
+    setTimeout(f, 10);
+}
 
 
-    constructor(private readonly ctx: Ctx) {
-        vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
-        vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
-    }
+class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
+    uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
+    eventEmitter = new vscode.EventEmitter<vscode.Uri>();
 
-    private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
-        if (isRustDocument(event.document)) {
-            // We need to order this after language server updates, but there's no API for that.
-            // Hence, good old sleep().
-            void sleep(10).then(() => this.eventEmitter.fire(this.uri));
-        }
-    }
-    private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
-        if (editor && isRustEditor(editor)) {
-            this.eventEmitter.fire(this.uri);
-        }
+    constructor(private readonly ctx: Ctx) {
     }
 
-    provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
-        const rustEditor = this.ctx.activeRustEditor;
-        if (!rustEditor) return '';
+    provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
+        const editor = vscode.window.activeTextEditor;
+        const client = this.ctx.client;
+        if (!editor || !client) return '';
 
         // When the range based query is enabled we take the range of the selection
-        const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
-            ? this.ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
+        const range = uri.query === 'range=true' && !editor.selection.isEmpty
+            ? client.code2ProtocolConverter.asRange(editor.selection)
             : null;
 
-        const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
-        return this.ctx.client.sendRequest(ra.syntaxTree, params, ct);
+        return client.sendRequest(ra.syntaxTree, {
+            textDocument: { uri: editor.document.uri.toString() },
+            range,
+        });
     }
 
     get onDidChange(): vscode.Event<vscode.Uri> {
         return this.eventEmitter.event;
     }
 }
-
-
-// FIXME: consider implementing this via the Tree View API?
-// https://code.visualstudio.com/api/extension-guides/tree-view
-class AstInspector implements vscode.HoverProvider, Disposable {
-    private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({
-        fontStyle: "normal",
-        border: "#ffffff 1px solid",
-    });
-    private rustEditor: undefined | RustEditor;
-
-    constructor(ctx: Ctx) {
-        ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
-        vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
-        vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
-
-        ctx.pushCleanup(this);
-    }
-    dispose() {
-        this.setRustEditor(undefined);
-    }
-
-    private onDidCloseTextDocument(doc: vscode.TextDocument) {
-        if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
-            this.setRustEditor(undefined);
-        }
-    }
-
-    private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
-        if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) {
-            this.setRustEditor(undefined);
-            return;
-        }
-        this.setRustEditor(editors.find(isRustEditor));
-    }
-
-    private setRustEditor(newRustEditor: undefined | RustEditor) {
-        if (newRustEditor !== this.rustEditor) {
-            this.rustEditor?.setDecorations(AstInspector.astDecorationType, []);
-        }
-        this.rustEditor = newRustEditor;
-    }
-
-    provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
-        if (!this.rustEditor) return;
-
-        const astTextLine = doc.lineAt(hoverPosition.line);
-
-        const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text);
-        if (!rustTextRange) return;
-
-        this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]);
-        this.rustEditor.revealRange(rustTextRange);
-
-        const rustSourceCode = this.rustEditor.document.getText(rustTextRange);
-        const astTextRange = this.findAstRange(astTextLine);
-
-        return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange);
-    }
-
-    private findAstRange(astLine: vscode.TextLine) {
-        const lineOffset = astLine.range.start;
-        const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
-        const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
-        return new vscode.Range(begin, end);
-    }
-
-    private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range {
-        const parsedRange = /\[(\d+); (\d+)\)/.exec(astLine);
-        if (!parsedRange) return;
-
-        const [begin, end] = parsedRange.slice(1).map(off => doc.positionAt(+off));
-
-        return new vscode.Range(begin, end);
-    }
-}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 501997fef7..e77462c1b0 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -12,9 +12,9 @@ export class Config {
     private readonly requiresReloadOpts = [
         "serverPath",
         "cargoFeatures",
-        "excludeGlobs",
-        "useClientWatching",
-        "highlighting",
+        "cargo-watch",
+        "highlighting.semanticTokens",
+        "inlayHints",
         "updates.channel",
     ]
         .map(opt => `${this.rootSection}.${opt}`);
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 86b5f36295..84c170ea8b 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
 
 import { Config } from './config';
-import { createClient, configToServerOptions } from './client';
+import { createClient } from './client';
 import { isRustEditor, RustEditor } from './util';
 
 export class Ctx {
@@ -15,17 +15,11 @@ export class Ctx {
 
     }
 
-    static async create(
-        config: Config,
-        extCtx: vscode.ExtensionContext,
-        serverPath: string,
-        cwd: string,
-    ): Promise<Ctx> {
-        const client = await createClient(config, serverPath, cwd);
+    static async create(config: Config, extCtx: vscode.ExtensionContext, serverPath: string): Promise<Ctx> {
+        const client = await createClient(config, serverPath);
         const res = new Ctx(config, extCtx, client, serverPath);
         res.pushCleanup(client.start());
         await client.onReady();
-        client.onRequest('workspace/configuration', _ => [configToServerOptions(config)]);
         return res;
     }
 
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 6a8bd942e1..542d1f3672 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -17,11 +17,7 @@ export function activateInlayHints(ctx: Ctx) {
             ) {
                 return this.dispose();
             }
-            if (this.updater) {
-                this.updater.syncCacheAndRenderHints();
-            } else {
-                this.updater = new HintsUpdater(ctx);
-            }
+            if (!this.updater) this.updater = new HintsUpdater(ctx);
         },
         dispose() {
             this.updater?.dispose();
@@ -128,7 +124,7 @@ class HintsUpdater implements Disposable {
         this.syncCacheAndRenderHints();
     }
 
-    syncCacheAndRenderHints() {
+    private syncCacheAndRenderHints() {
         // FIXME: make inlayHints request pass an array of files?
         this.sourceFiles.forEach((file, uri) => this.fetchHints(file).then(hints => {
             if (!hints) return;
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7ba16120c3..980ed925b4 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -13,7 +13,6 @@ import { log, assert } from './util';
 import { PersistentState } from './persistent_state';
 import { fetchRelease, download } from './net';
 import { spawnSync } from 'child_process';
-import { activateTaskProvider } from './tasks';
 
 let ctx: Ctx | undefined;
 
@@ -42,18 +41,11 @@ export async function activate(context: vscode.ExtensionContext) {
     const state = new PersistentState(context.globalState);
     const serverPath = await bootstrap(config, state);
 
-    const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
-    if (workspaceFolder === undefined) {
-        const err = "Cannot activate rust-analyzer when no folder is opened";
-        void vscode.window.showErrorMessage(err);
-        throw new Error(err);
-    }
-
     // Note: we try to start the server before we activate type hints so that it
     // registers its `onDidChangeDocument` handler before us.
     //
     // This a horribly, horribly wrong way to deal with this problem.
-    ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath);
+    ctx = await Ctx.create(config, context, serverPath);
 
     // Commands which invokes manually via command palette, shortcut, etc.
 
@@ -93,20 +85,12 @@ export async function activate(context: vscode.ExtensionContext) {
     ctx.registerCommand('applySourceChange', commands.applySourceChange);
     ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
 
-    ctx.pushCleanup(activateTaskProvider(workspaceFolder));
-
     activateStatusDisplay(ctx);
 
     if (!ctx.config.highlightingSemanticTokens) {
         activateHighlighting(ctx);
     }
     activateInlayHints(ctx);
-
-    vscode.workspace.onDidChangeConfiguration(
-        _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
-        null,
-        ctx.subscriptions,
-    );
 }
 
 export async function deactivate() {
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
deleted file mode 100644
index fa1c4a951d..0000000000
--- a/editors/code/src/tasks.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import * as vscode from 'vscode';
-
-// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
-// our configuration should be compatible with it so use the same key.
-const TASK_TYPE = 'cargo';
-
-export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable {
-    const provider: vscode.TaskProvider = {
-        // Detect Rust tasks. Currently we do not do any actual detection
-        // of tasks (e.g. aliases in .cargo/config) and just return a fixed
-        // set of tasks that always exist. These tasks cannot be removed in
-        // tasks.json - only tweaked.
-        provideTasks: () => getStandardCargoTasks(target),
-
-        // We don't need to implement this.
-        resolveTask: () => undefined,
-    };
-
-    return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
-}
-
-function getStandardCargoTasks(target: vscode.WorkspaceFolder): vscode.Task[] {
-    return [
-        { command: 'build', group: vscode.TaskGroup.Build },
-        { command: 'check', group: vscode.TaskGroup.Build },
-        { command: 'test', group: vscode.TaskGroup.Test },
-        { command: 'clean', group: vscode.TaskGroup.Clean },
-        { command: 'run', group: undefined },
-    ]
-        .map(({ command, group }) => {
-            const vscodeTask = new vscode.Task(
-                // The contents of this object end up in the tasks.json entries.
-                {
-                    type: TASK_TYPE,
-                    command,
-                },
-                // The scope of the task - workspace or specific folder (global
-                // is not supported).
-                target,
-                // The task name, and task source. These are shown in the UI as
-                // `${source}: ${name}`, e.g. `rust: cargo build`.
-                `cargo ${command}`,
-                'rust',
-                // What to do when this command is executed.
-                new vscode.ShellExecution('cargo', [command]),
-                // Problem matchers.
-                ['$rustc'],
-            );
-            vscodeTask.group = group;
-            return vscodeTask;
-        });
-}
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 6f91f81d63..978a31751e 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -65,12 +65,12 @@ export async function sendRequestWithRetry<TParam, TRet>(
     throw 'unreachable';
 }
 
-export function sleep(ms: number) {
+function sleep(ms: number) {
     return new Promise(resolve => setTimeout(resolve, ms));
 }
 
 export type RustDocument = vscode.TextDocument & { languageId: "rust" };
-export type RustEditor = vscode.TextEditor & { document: RustDocument };
+export type RustEditor = vscode.TextEditor & { document: RustDocument; id: string };
 
 export function isRustDocument(document: vscode.TextDocument): document is RustDocument {
     return document.languageId === 'rust'


Test patch:
diff --git a/crates/ra_flycheck/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs
similarity index 100%
rename from crates/ra_flycheck/src/conv.rs
rename to crates/ra_cargo_watch/src/conv.rs
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
similarity index 98%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
index 4c9db0385b..a59fa84faa 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
similarity index 96%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
index 7cde4d8670..61ae0c9aee 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
similarity index 97%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
index 1cc37e087f..641da1a588 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
similarity index 98%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
index 615ed83787..0557a2e79e 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
similarity index 96%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
index 0df0fce188..754bc33a46 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
similarity index 96%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
index 28ebcb3b30..78b7f7cc83 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
similarity index 98%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
index 5e08732817..5989ed2026 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
similarity index 97%
rename from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
rename to crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
index e500d3cd6c..e34b546dc1 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
@@ -1,5 +1,5 @@
 ---
-source: crates/ra_flycheck/src/conv/test.rs
+source: crates/ra_cargo_watch/src/conv/test.rs
 expression: diag
 ---
 [
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index eb90b5279c..136857315c 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,19 +1,19 @@
 //! Runs completion for testing purposes.
 
 use crate::{
-    completion::{completion_item::CompletionKind, CompletionConfig},
+    completion::{completion_item::CompletionKind, CompletionOptions},
     mock_analysis::{analysis_and_position, single_file_with_position},
     CompletionItem,
 };
 
 pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
-    do_completion_with_options(code, kind, &CompletionConfig::default())
+    do_completion_with_options(code, kind, &CompletionOptions::default())
 }
 
 pub(crate) fn do_completion_with_options(
     code: &str,
     kind: CompletionKind,
-    options: &CompletionConfig,
+    options: &CompletionOptions,
 ) -> Vec<CompletionItem> {
     let (analysis, position) = if code.contains("//-") {
         analysis_and_position(code)
diff --git a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs
deleted file mode 100644
index 1fafe216b6..0000000000
--- a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-/// Example
-
-fn test() {}
diff --git a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt
deleted file mode 100644
index 91d0c37365..0000000000
--- a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-SOURCE_FILE@[0; 26)
-  FN_DEF@[0; 25)
-    COMMENT@[0; 11) "/// Example"
-    WHITESPACE@[11; 13) "\n\n"
-    FN_KW@[13; 15) "fn"
-    WHITESPACE@[15; 16) " "
-    NAME@[16; 20)
-      IDENT@[16; 20) "test"
-    PARAM_LIST@[20; 22)
-      L_PAREN@[20; 21) "("
-      R_PAREN@[21; 22) ")"
-    WHITESPACE@[22; 23) " "
-    BLOCK_EXPR@[23; 25)
-      BLOCK@[23; 25)
-        L_CURLY@[23; 24) "{"
-        R_CURLY@[24; 25) "}"
-  WHITESPACE@[25; 26) "\n"
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 5af5eaad20..145429571c 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -9,7 +9,7 @@ use lsp_types::{
 };
 use rust_analyzer::req::{
     CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
-    Formatting, GotoDefinition, OnEnter, Runnables, RunnablesParams,
+    Formatting, OnEnter, Runnables, RunnablesParams,
 };
 use serde_json::json;
 use tempfile::TempDir;
@@ -581,47 +581,3 @@ version = \"0.0.0\"
         }),
     );
 }
-
-#[test]
-fn resolve_include_concat_env() {
-    if skip_slow_tests() {
-        return;
-    }
-
-    let server = Project::with_fixture(
-        r###"
-//- Cargo.toml
-[package]
-name = "foo"
-version = "0.0.0"
-
-//- build.rs
-use std::{env, fs, path::Path};
-
-fn main() {
-    let out_dir = env::var_os("OUT_DIR").unwrap();
-    let dest_path = Path::new(&out_dir).join("hello.rs");
-    fs::write(
-        &dest_path,
-        r#"pub fn message() -> &'static str { "Hello, World!" }"#,
-    )
-    .unwrap();
-    println!("cargo:rerun-if-changed=build.rs");
-}
-//- src/main.rs
-include!(concat!(env!("OUT_DIR"), "/hello.rs"));
-
-fn main() { message(); }
-"###,
-    )
-    .with_config(|config| {
-        config.cargo_features.load_out_dirs_from_check = true;
-    })
-    .server();
-    server.wait_until_workspace_is_loaded();
-    let res = server.send_request::<GotoDefinition>(TextDocumentPositionParams::new(
-        server.doc_id("src/main.rs"),
-        Position::new(2, 15),
-    ));
-    assert!(format!("{}", res).contains("hello.rs"));
-}
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index d8bed6d7f4..1d7062bdf2 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -27,12 +27,11 @@ pub struct Project<'a> {
     with_sysroot: bool,
     tmp_dir: Option<TempDir>,
     roots: Vec<PathBuf>,
-    config: Option<Box<dyn Fn(&mut ServerConfig)>>,
 }
 
 impl<'a> Project<'a> {
     pub fn with_fixture(fixture: &str) -> Project {
-        Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None }
+        Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false }
     }
 
     pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> {
@@ -50,11 +49,6 @@ impl<'a> Project<'a> {
         self
     }
 
-    pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> {
-        self.config = Some(Box::new(config));
-        self
-    }
-
     pub fn server(self) -> Server {
         let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap());
         static INIT: Once = Once::new();
@@ -78,14 +72,7 @@ impl<'a> Project<'a> {
 
         let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
 
-        let mut config =
-            ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() };
-
-        if let Some(f) = &self.config {
-            f(&mut config)
-        }
-
-        Server::new(tmp_dir, config, roots, paths)
+        Server::new(tmp_dir, self.with_sysroot, roots, paths)
     }
 }
 
@@ -96,16 +83,15 @@ pub fn project(fixture: &str) -> Server {
 pub struct Server {
     req_id: Cell<u64>,
     messages: RefCell<Vec<Message>>,
+    dir: TempDir,
     _thread: jod_thread::JoinHandle<()>,
     client: Connection,
-    /// XXX: remove the tempdir last
-    dir: TempDir,
 }
 
 impl Server {
     fn new(
         dir: TempDir,
-        config: ServerConfig,
+        with_sysroot: bool,
         roots: Vec<PathBuf>,
         files: Vec<(PathBuf, String)>,
     ) -> Server {
@@ -131,7 +117,7 @@ impl Server {
                         window: None,
                         experimental: None,
                     },
-                    config,
+                    ServerConfig { with_sysroot, ..ServerConfig::default() },
                     connection,
                 )
                 .unwrap()
@@ -201,7 +187,6 @@ impl Server {
         self.client.sender.send(r.into()).unwrap();
         while let Some(msg) = self.recv() {
             match msg {
-                Message::Request(req) if req.method == "window/workDoneProgress/create" => (),
                 Message::Request(req) => panic!("unexpected request: {:?}", req),
                 Message::Notification(_) => (),
                 Message::Response(res) => {