Repository: rust-analyzer/rust-analyzer
Pull request #521 (2019-01-13 12:04:17)

refresh docs


Patch:
diff --git a/README.md b/README.md
index 0b7203d2c5..cbb72c8cfe 100644
--- a/README.md
+++ b/README.md
@@ -2,15 +2,13 @@
 
 [![Build Status](https://travis-ci.org/rust-analyzer/rust-analyzer.svg?branch=master)](https://travis-ci.org/rust-analyzer/rust-analyzer)
 
-Rust Analyzer is an **experimental** modular compiler frontend for the
-Rust language, which aims to lay a foundation for excellent IDE
-support.
+Rust Analyzer is an **experimental** modular compiler frontend for the Rust
+language, which aims to lay a foundation for excellent IDE support.
 
-It doesn't implement much of compiler functionality yet, but the
-white-space preserving Rust parser works, and there are significant
-chunks of overall architecture (indexing, on-demand & lazy
-computation, snapshotable world view) in place. Some basic IDE
-functionality is provided via a language server.
+It doesn't implement much of compiler functionality yet, but the white-space
+preserving Rust parser works, and there are significant chunks of overall
+architecture (indexing, on-demand & lazy computation, snapshotable world view)
+in place. Some basic IDE functionality is provided via a language server.
 
 Work on the Rust Analyzer is sponsored by
 
@@ -30,53 +28,45 @@ $ cargo run --package ra_cli parse < crates/ra_syntax/src/lib.rs
 
 # show symbols of a Rust file
 $ cargo run --package ra_cli symbols < crates/ra_syntax/src/lib.rs
-```
 
-To try out the language server, see [these
-instructions](./editors/README.md). Please note that the server is not
-ready for general use yet. If you are looking for a Rust IDE that
-works, use [IntelliJ
-Rust](https://github.com/intellij-rust/intellij-rust) or
-[RLS](https://github.com/rust-lang-nursery/rls). That being said, the
-basic stuff works, and rust analyzer is developed in the rust analyzer
-powered editor.
+# install the language server
+$ cargo install --path crates/ra_lsp_server
+```
 
+See [these instructions](./editors/README.md) for VS Code setup and the list of
+features (some of which are VS Code specific).
 
 ## Current Status and Plans
 
-Rust analyzer aims to fill the same niche as the official [Rust
-Language Server](https://github.com/rust-lang-nursery/rls), but uses a
-significantly different architecture. More details can be found [in
-this
+Rust analyzer aims to fill the same niche as the official [Rust Language
+Server](https://github.com/rust-lang-nursery/rls), but uses a significantly
+different architecture. More details can be found [in this
 thread](https://internals.rust-lang.org/t/2019-strategy-for-rustc-and-the-rls/8361),
-but the core issue is that RLS works in the "wait until user stops
-typing, run the build process, save the results of the analysis" mode,
-which arguably is the wrong foundation for IDE.
+but the core issue is that RLS works in the "wait until user stops typing, run
+the build process, save the results of the analysis" mode, which arguably is the
+wrong foundation for IDE.
 
-Rust Analyzer is a hobby project at the moment, there's exactly zero
+Rust Analyzer is an experimental project at the moment, there's exactly zero
 guarantees that it becomes production-ready one day.
 
-The near/mid term plan is to work independently of the main rustc
-compiler and implement at least simplistic versions of name
-resolution, macro expansion and type inference. The purpose is two
-fold:
+The near/mid term plan is to work independently of the main rustc compiler and
+implement at least simplistic versions of name resolution, macro expansion and
+type inference. The purpose is two fold:
 
-* to quickly bootstrap usable and useful language server: solution
-  that covers 80% of Rust code will be useful for IDEs, and will be
-  vastly simpler than 100% solution.
+* to quickly bootstrap usable and useful language server: solution that covers
+  80% of Rust code will be useful for IDEs, and will be vastly simpler than 100%
+  solution.
 
 * to understand how the consumer-side of compiler API should look like
-  (especially it's on-demand aspects). If you have
-  `get_expression_type` function, you can write a ton of purely-IDE
-  features on top of it, even if the function is only partially
-  correct. Plugin in the precise function afterwards should just make
-  IDE features more reliable.
-
-The long term plan is to merge with the mainline rustc compiler,
-probably around the HIR boundary? That is, use rust analyzer for
-parsing, macro expansion and related bits of name resolution, but
-leave the rest (including type inference and trait selection) to the
-existing rustc.
+  (especially it's on-demand aspects). If you have `get_expression_type`
+  function, you can write a ton of purely-IDE features on top of it, even if the
+  function is only partially correct. Pluging in the precise function afterwards
+  should just make IDE features more reliable.
+
+The long term plan is to merge with the mainline rustc compiler, probably around
+the HIR boundary? That is, use rust analyzer for parsing, macro expansion and
+related bits of name resolution, but leave the rest (including type inference
+and trait selection) to the existing rustc.
 
 ## Getting in touch
 
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 2b761ea0c4..023183e290 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -53,9 +53,6 @@ pub struct CrateGraph {
     arena: FxHashMap<CrateId, CrateData>,
 }
 
-#[derive(Debug)]
-pub struct CyclicDependencies;
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct CrateId(pub u32);
 
@@ -97,16 +94,12 @@ impl CrateGraph {
         assert!(prev.is_none());
         crate_id
     }
-    pub fn add_dep(
-        &mut self,
-        from: CrateId,
-        name: SmolStr,
-        to: CrateId,
-    ) -> Result<(), CyclicDependencies> {
-        if self.dfs_find(from, to, &mut FxHashSet::default()) {
-            return Err(CyclicDependencies);
+    pub fn add_dep(&mut self, from: CrateId, name: SmolStr, to: CrateId) {
+        let mut visited = FxHashSet::default();
+        if self.dfs_find(from, to, &mut visited) {
+            panic!("Cycle dependencies found.")
         }
-        Ok(self.arena.get_mut(&from).unwrap().add_dep(name, to))
+        self.arena.get_mut(&from).unwrap().add_dep(name, to)
     }
     pub fn is_empty(&self) -> bool {
         self.arena.is_empty()
@@ -146,6 +139,35 @@ impl CrateGraph {
     }
 }
 
+#[cfg(test)]
+mod tests {
+    use super::{CrateGraph, FxHashMap, FileId, SmolStr};
+
+    #[test]
+    #[should_panic]
+    fn it_should_painc_because_of_cycle_dependencies() {
+        let mut graph = CrateGraph::default();
+        let crate1 = graph.add_crate_root(FileId(1u32));
+        let crate2 = graph.add_crate_root(FileId(2u32));
+        let crate3 = graph.add_crate_root(FileId(3u32));
+        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
+        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
+        graph.add_dep(crate3, SmolStr::new("crate1"), crate1);
+    }
+
+    #[test]
+    fn it_works() {
+        let mut graph = CrateGraph {
+            arena: FxHashMap::default(),
+        };
+        let crate1 = graph.add_crate_root(FileId(1u32));
+        let crate2 = graph.add_crate_root(FileId(2u32));
+        let crate3 = graph.add_crate_root(FileId(3u32));
+        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
+        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
+    }
+}
+
 salsa::query_group! {
     pub trait FilesDatabase: salsa::Database {
         /// Text of the file.
@@ -187,39 +209,3 @@ salsa::query_group! {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::{CrateGraph, FileId, SmolStr};
-
-    #[test]
-    fn it_should_painc_because_of_cycle_dependencies() {
-        let mut graph = CrateGraph::default();
-        let crate1 = graph.add_crate_root(FileId(1u32));
-        let crate2 = graph.add_crate_root(FileId(2u32));
-        let crate3 = graph.add_crate_root(FileId(3u32));
-        assert!(graph
-            .add_dep(crate1, SmolStr::new("crate2"), crate2)
-            .is_ok());
-        assert!(graph
-            .add_dep(crate2, SmolStr::new("crate3"), crate3)
-            .is_ok());
-        assert!(graph
-            .add_dep(crate3, SmolStr::new("crate1"), crate1)
-            .is_err());
-    }
-
-    #[test]
-    fn it_works() {
-        let mut graph = CrateGraph::default();
-        let crate1 = graph.add_crate_root(FileId(1u32));
-        let crate2 = graph.add_crate_root(FileId(2u32));
-        let crate3 = graph.add_crate_root(FileId(3u32));
-        assert!(graph
-            .add_dep(crate1, SmolStr::new("crate2"), crate2)
-            .is_ok());
-        assert!(graph
-            .add_dep(crate2, SmolStr::new("crate3"), crate3)
-            .is_ok());
-    }
-}
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index e2537758dd..8d2ff561ac 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -13,11 +13,8 @@ pub(crate) fn goto_definition(
     let file = db.source_file(position.file_id);
     let syntax = file.syntax();
     if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
-        let navs = reference_definition(db, position.file_id, name_ref)?.to_vec();
-        return Ok(Some(RangeInfo::new(
-            name_ref.syntax().range(),
-            navs.to_vec(),
-        )));
+        let navs = reference_definition(db, position.file_id, name_ref)?;
+        return Ok(Some(RangeInfo::new(name_ref.syntax().range(), navs)));
     }
     if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
         let navs = ctry!(name_definition(db, position.file_id, name)?);
@@ -26,27 +23,11 @@ pub(crate) fn goto_definition(
     Ok(None)
 }
 
-pub(crate) enum ReferenceResult {
-    Exact(NavigationTarget),
-    Approximate(Vec<NavigationTarget>),
-}
-
-impl ReferenceResult {
-    fn to_vec(self) -> Vec<NavigationTarget> {
-        use self::ReferenceResult::*;
-        match self {
-            Exact(target) => vec![target],
-            Approximate(vec) => vec,
-        }
-    }
-}
-
 pub(crate) fn reference_definition(
     db: &RootDatabase,
     file_id: FileId,
     name_ref: &ast::NameRef,
-) -> Cancelable<ReferenceResult> {
-    use self::ReferenceResult::*;
+) -> Cancelable<Vec<NavigationTarget>> {
     if let Some(fn_descr) =
         hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())?
     {
@@ -54,7 +35,7 @@ pub(crate) fn reference_definition(
         // First try to resolve the symbol locally
         if let Some(entry) = scope.resolve_local_name(name_ref) {
             let nav = NavigationTarget::from_scope_entry(file_id, &entry);
-            return Ok(Exact(nav));
+            return Ok(vec![nav]);
         };
     }
     // Then try module name resolution
@@ -70,7 +51,7 @@ pub(crate) fn reference_definition(
             let resolved = module.resolve_path(db, &path)?;
             if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
                 if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)?)? {
-                    return Ok(Exact(target));
+                    return Ok(vec![target]);
                 }
             }
         }
@@ -81,7 +62,7 @@ pub(crate) fn reference_definition(
         .into_iter()
         .map(NavigationTarget::from_symbol)
         .collect();
-    Ok(Approximate(navs))
+    Ok(navs)
 }
 
 fn name_definition(
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 2968b807ca..b66774cdfa 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -16,16 +16,9 @@ pub(crate) fn hover(
 
     let mut range = None;
     if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
-        use crate::goto_definition::{ReferenceResult::*, reference_definition};
-        let ref_result = reference_definition(db, position.file_id, name_ref)?;
-        match ref_result {
-            Exact(nav) => res.extend(doc_text_for(db, nav)?),
-            Approximate(navs) => {
-                res.push("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits.  \nThese methods were found instead:".to_string());
-                for nav in navs {
-                    res.extend(doc_text_for(db, nav)?)
-                }
-            }
+        let navs = crate::goto_definition::reference_definition(db, position.file_id, name_ref)?;
+        for nav in navs {
+            res.extend(doc_text_for(db, nav)?)
         }
         if !res.is_empty() {
             range = Some(name_ref.syntax().range())
@@ -41,7 +34,7 @@ pub(crate) fn hover(
             file_id: position.file_id,
             range: node.range(),
         };
-        res.extend(type_of(db, frange)?.map(Into::into));
+        res.extend(type_of(db, frange)?);
         range = Some(node.range());
     };
 
diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs
index 53e49da5ba..98b1d2d552 100644
--- a/crates/ra_ide_api/src/runnables.rs
+++ b/crates/ra_ide_api/src/runnables.rs
@@ -17,7 +17,6 @@ pub struct Runnable {
 pub enum RunnableKind {
     Test { name: String },
     TestMod { path: String },
-    Bench { name: String },
     Bin,
 }
 
@@ -49,10 +48,6 @@ fn runnable_fn(fn_def: &ast::FnDef) -> Option<Runnable> {
         RunnableKind::Test {
             name: name.to_string(),
         }
-    } else if fn_def.has_atom_attr("bench") {
-        RunnableKind::Bench {
-            name: name.to_string(),
-        }
     } else {
         return None;
     };
diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs
index db9496bbe0..a66f14b821 100644
--- a/crates/ra_lsp_server/src/cargo_target_spec.rs
+++ b/crates/ra_lsp_server/src/cargo_target_spec.rs
@@ -32,15 +32,6 @@ pub(crate) fn runnable_args(
             res.push(path.to_string());
             res.push("--nocapture".to_string());
         }
-        RunnableKind::Bench { name } => {
-            res.push("bench".to_string());
-            if let Some(spec) = spec {
-                spec.push_to(&mut res);
-            }
-            res.push("--".to_string());
-            res.push(name.to_string());
-            res.push("--nocapture".to_string());
-        }
         RunnableKind::Bin => {
             res.push("run".to_string());
             if let Some(spec) = spec {
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 7326a727dc..a781df181b 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -2,23 +2,22 @@ use std::collections::HashMap;
 
 use gen_lsp_server::ErrorCode;
 use languageserver_types::{
-    CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity,
-    DocumentFormattingParams, DocumentHighlight, DocumentSymbol, Documentation, FoldingRange,
-    FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
-    MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range,
-    RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit,
-    WorkspaceEdit,
+    CodeActionResponse, Command, CodeLens, Diagnostic, DiagnosticSeverity, DocumentFormattingParams,
+    DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
+    FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind,
+    ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams,
+    SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit,
 };
 use ra_ide_api::{
-    FileId, FilePosition, FileRange, FoldKind, Query, RangeInfo, RunnableKind, Severity,
+    FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, RangeInfo,
 };
-use ra_syntax::{AstNode, TextUnit};
+use ra_syntax::{TextUnit, AstNode};
 use rustc_hash::FxHashMap;
 use serde_json::to_value;
 use std::io::Write;
 
 use crate::{
-    cargo_target_spec::{runnable_args, CargoTargetSpec},
+    cargo_target_spec::{CargoTargetSpec, runnable_args},
     conv::{to_location, to_location_link, Conv, ConvWith, MapConvWith, TryConvWith},
     req::{self, Decoration},
     server_world::ServerWorld,
@@ -259,7 +258,6 @@ pub fn handle_runnables(
             label: match &runnable.kind {
                 RunnableKind::Test { name } => format!("test {}", name),
                 RunnableKind::TestMod { path } => format!("test-mod {}", path),
-                RunnableKind::Bench { name } => format!("bench {}", name),
                 RunnableKind::Bin => "run binary".to_string(),
             },
             bin: "cargo".to_string(),
@@ -588,37 +586,35 @@ pub fn handle_code_lens(
     let mut lenses: Vec<CodeLens> = Default::default();
 
     for runnable in world.analysis().runnables(file_id)? {
-        let title = match &runnable.kind {
-            RunnableKind::Test { name: _ } | RunnableKind::TestMod { path: _ } => Some("Run Test"),
-            RunnableKind::Bench { name: _ } => Some("Run Bench"),
-            _ => None,
-        };
-
-        if let Some(title) = title {
-            let args = runnable_args(&world, file_id, &runnable.kind)?;
-            let range = runnable.range.conv_with(&line_index);
-
-            // This represents the actual command that will be run.
-            let r: req::Runnable = req::Runnable {
-                range,
-                label: Default::default(),
-                bin: "cargo".into(),
-                args,
-                env: Default::default(),
-            };
+        match &runnable.kind {
+            RunnableKind::Test { name: _ } | RunnableKind::TestMod { path: _ } => {
+                let args = runnable_args(&world, file_id, &runnable.kind)?;
+
+                let range = runnable.range.conv_with(&line_index);
+
+                // This represents the actual command that will be run.
+                let r: req::Runnable = req::Runnable {
+                    range,
+                    label: Default::default(),
+                    bin: "cargo".into(),
+                    args,
+                    env: Default::default(),
+                };
 
-            let lens = CodeLens {
-                range,
-                command: Some(Command {
-                    title: title.into(),
-                    command: "ra-lsp.run-single".into(),
-                    arguments: Some(vec![to_value(r).unwrap()]),
-                }),
-                data: None,
-            };
+                let lens = CodeLens {
+                    range,
+                    command: Some(Command {
+                        title: "Run Test".into(),
+                        command: "ra-lsp.run-single".into(),
+                        arguments: Some(vec![to_value(r).unwrap()]),
+                    }),
+                    data: None,
+                };
 
-            lenses.push(lens);
-        }
+                lenses.push(lens);
+            }
+            _ => continue,
+        };
     }
 
     return Ok(Some(lenses));
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index d5dbf999fd..4f3c231d31 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -73,9 +73,7 @@ impl ServerWorldState {
                     if let (Some(&from), Some(&to)) =
                         (sysroot_crates.get(&from), sysroot_crates.get(&to))
                     {
-                        if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
-                            log::error!("cyclic dependency between sysroot crates")
-                        }
+                        crate_graph.add_dep(from, name.clone(), to);
                     }
                 }
             }
@@ -110,20 +108,11 @@ impl ServerWorldState {
                 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
                     if let Some(to) = lib_tgt {
                         if to != from {
-                            if let Err(_) =
-                                crate_graph.add_dep(from, pkg.name(&ws.cargo).into(), to)
-                            {
-                                log::error!(
-                                    "cyclic dependency between targets of {}",
-                                    pkg.name(&ws.cargo)
-                                )
-                            }
+                            crate_graph.add_dep(from, pkg.name(&ws.cargo).into(), to);
                         }
                     }
                     if let Some(std) = libstd {
-                        if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
-                            log::error!("cyclic dependency on std for {}", pkg.name(&ws.cargo))
-                        }
+                        crate_graph.add_dep(from, "std".into(), std);
                     }
                 }
             }
@@ -134,13 +123,7 @@ impl ServerWorldState {
                 for dep in pkg.dependencies(&ws.cargo) {
                     if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
                         for &from in pkg_crates.get(&pkg).into_iter().flatten() {
-                            if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) {
-                                log::error!(
-                                    "cyclic dependency {} -> {}",
-                                    pkg.name(&ws.cargo),
-                                    dep.pkg.name(&ws.cargo)
-                                )
-                            }
+                            crate_graph.add_dep(from, dep.name.clone(), to);
                         }
                     }
                 }
diff --git a/editors/README.md b/editors/README.md
index 5b09750e6d..9ed65e631c 100644
--- a/editors/README.md
+++ b/editors/README.md
@@ -4,72 +4,96 @@ To install experimental VS Code plugin:
 $ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1
 $ cd rust-analyzer
 $ cargo install-code
+
+# for stdlib support
+$ rustup component add rust-src
 ```
 
-This will run `cargo install --packge ra_lsp_server` to install the
-server binary into `~/.cargo/bin`, and then will build and install
-plugin from `editors/code`. See
-[this](https://github.com/matklad/rust-analyzer/blob/cc76b0d31d8ba013c499dd3a4ca69b37004795e6/crates/tools/src/main.rs#L192)
-for details
+This will run `cargo install --packge ra_lsp_server` to install the server
+binary into `~/.cargo/bin`, and then will build and install plugin from
+`editors/code`. See
+[this](https://github.com/rust-analyzer/rust-analyzer/blob/0199572a3d06ff66eeae85a2d2c9762996f0d2d8/crates/tools/src/main.rs#L150)
+for details. The installation is expected to *just work*, if it doesn't, report
+bugs!
 
 It's better to remove existing Rust plugins to avoid interference.
 
-### Features:
-
-* syntax highlighting (LSP does not have API for it, so impl is hacky
-  and sometimes fall-backs to the horrible built-in highlighting)
-
-* **Go to symbol in workspace** (`ctrl+t`)
-  - `#Foo` searches for `Foo` type in the current workspace
-  - `#foo#` searches for `foo` function in the current workspace
-  - `#Foo*` searches for `Foo` type among dependencies, excluding `stdlib`
-  - Sorry for a weired UI, neither LSP, not VSCode have any sane API for filtering! :)
-
-* **Go to symbol in file** (`alt+shift+o`)
-
-* **Go to definition** ("correct" for `mod foo;` decls, approximate for other things).
-
-* commands (`ctrl+shift+p` or keybindings)
-  - **Show Rust Syntax Tree** (use it to verify that plugin works)
-  - **Rust Extend Selection**. Extends the current selection to the
-    encompassing syntactic construct (expression, statement, item,
-    module, etc). It works with multiple cursors. Do bind this command
-    to a key, its super-useful!
-  - **Rust Matching Brace**. If the cursor is on any brace
-    (`<>(){}[]`) which is a part of a brace-pair, moves cursor to the
-    matching brace.
-  - **Rust Parent Module**. Navigate to the parent module of the current module
-  - **Rust Join Lines**. Join selected lines into one, smartly fixing
-    up whitespace and trailing commas.
-  - **Run test at caret**. When cursor is inside a function marked
-    `#[test]`, this action runs this specific test. If the cursor is
-    outside of the test function, this re-runs the last test. Do bind
-    this to a shortcut!
-  - **Format document**. Formats the current file with rustfmt.
-    Rustfmt must be installed separately with `rustup component add rustfmt`.
-
-* Typing assists
-  - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression.
-  - Enter inside comments continues comment (`<|>` signifies cursor position):
+## Rust Analyzer Specifc Features
 
-```
-/// Docs<|>
-fn foo() {}
-```
+These features are implemented as extensions to the langauge server protocol.
+They are more experimental in nature and work only with VS Code.
 
-```
-/// Docs
-/// <|>
-fn foo() {}
-```
+### Syntax highlighting
+
+It overrides built-in highlighting, and works only with a specific theme
+(zenburn). `ra-lsp.highlightingOn` setting can be used to disable it.
+
+### Go to symbol in workspace <kbd>ctrl+t</kbd>
+
+It mostly works on top of the built-in LSP functionality, however `#` and `*`
+symbols can be used to narrow down the search. Specifically,
+
+- `#Foo` searches for `Foo` type in the current workspace
+- `#foo#` searches for `foo` function in the current workspace
+- `#Foo*` searches for `Foo` type among dependencies, excluding `stdlib`
+- `#foo#*` seaches for `foo` function among dependencies.
+
+That is, `#` switches from "types" to all symbols, `*` switches from the current
+workspace to dependencies.
+
+### Commands <kbd>ctrl+shift+p</kbd>
+
+#### Show Rust Syntax Tree
+
+Shows the parse tree of the current file. It exists mostly for debugging
+rust-analyzer itself.
+
+#### Extend Selection
+
+Extends the current selection to the encompassing syntactic construct
+(expression, statement, item, module, etc). It works with multiple cursors. Do
+bind this command to a key, its super-useful! Expected to be upstreamed to LSP soonish:
+https://github.com/Microsoft/language-server-protocol/issues/613
+
+#### Matching Brace
+
+If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
+moves cursor to the matching brace. It uses the actual parser to determine
+braces, so it won't confuse generics with comparisons.
 
-* code actions (use `ctrl+.` to activate).
+#### Parent Module
 
+Navigates to the parent module of the current module.
+
+#### Join Lines
+
+Join selected lines into one, smartly fixing up whitespace and trailing commas.
+
+#### Run
+
+Shows popup suggesting to run a test/benchmark/binary **at the current cursor
+location**. Super useful for repeatedly running just a single test. Do bind this
+to a shortcut!
+
+
+### On Typing Assists
+
+Some features trigger on typing certain characters:
+
+- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression.
+- Enter inside comments automatically inserts `///`
+- typing `.` in a chain method call auto-indents
+
+
+### Code Actions (Assists)
+
+These are triggered in a particular context via lightbulb. We use custom code on
+the VS Code side to be able to position cursor.
 
 
 - Flip `,`
 
-```
+```rust
 // before:
 fn foo(x: usize,<|> dim: (usize, usize))
 // after:
@@ -78,7 +102,7 @@ fn foo(dim: (usize, usize), x: usize)
 
 - Add `#[derive]`
 
-```
+```rust
 // before:
 struct Foo {
     <|>x: i32
@@ -92,7 +116,7 @@ struct Foo {
 
 - Add `impl`
 
-```
+```rust
 // before:
 struct Foo<'a, T: Debug> {
     <|>t: T
@@ -106,3 +130,90 @@ impl<'a, T: Debug> Foo<'a, T> {
     <|>
 }
 ```
+
+- Change visibility
+
+```rust
+// before:
+fn<|> foo() {}
+
+// after
+pub(crate) fn foo() {}
+```
+
+- Introduce variable:
+
+```rust
+// before:
+fn foo() {
+    foo(<|>1 + 1<|>);
+}
+
+// after:
+fn foo() {
+    let var_name = 1 + 1;
+    foo(var_name);
+}
+```
+
+- Replace if-let with match:
+
+```rust
+// before:
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        if <|>let VariantData::Struct(..) = *self {
+            true
+        } else {
+            false
+        }
+    }
+}
+
+// after:
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        <|>match *self {
+            VariantData::Struct(..) => true,
+            _ => false,
+        }
+    }
+}
+```
+
+- Split import
+
+```rust
+// before:
+use algo:<|>:visitor::{Visitor, visit};
+//after:
+use algo::{<|>visitor::{Visitor, visit}};
+```
+
+## LSP features
+
+* **Go to definition**: works correctly for local variables and some paths,
+  falls back to heuristic name matching for other things for the time being.
+
+* **Completion**: completes paths, including dependencies and standard library.
+  Does not handle glob imports and macros. Completes fields and inherent methods
+
+* **Outline** <kbd>alt+shift+o</kbd>
+
+* **Signature Info**
+
+* **Format document**. Formats the current file with rustfmt. Rustfmt must be
+  installed separately with `rustup component add rustfmt`.
+
+* **Hover** shows types of expressions and docstings
+
+* **Rename** works for local variables
+
+* **Code Lens** for running tests
+
+* **Folding**
+
+* **Diagnostics**
+  - missing module for `mod foo;` with a fix to create `foo.rs`.
+  - struct field shorthand
+  - unnessary braces in use item
diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts
index 675a534c8f..d96ace9795 100644
--- a/editors/code/src/commands/apply_source_change.ts
+++ b/editors/code/src/commands/apply_source_change.ts
@@ -10,9 +10,7 @@ export interface SourceChange {
 }
 
 export async function handle(change: SourceChange) {
-    const wsEdit = Server.client.protocol2CodeConverter.asWorkspaceEdit(
-        change.workspaceEdit
-    );
+    const wsEdit = Server.client.protocol2CodeConverter.asWorkspaceEdit(change.workspaceEdit);
     let created;
     let moved;
     if (change.workspaceEdit.documentChanges) {
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index bb376e3cb4..64401b6840 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -6,6 +6,7 @@ import {
     SourceChange
 } from './apply_source_change';
 
+
 export async function handle(event: { text: string }): Promise<boolean> {
     const editor = vscode.window.activeTextEditor;
     if (
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index aa5817c21f..f9a4e2fc93 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -48,11 +48,7 @@ function createTask(spec: Runnable): vscode.Task {
         cwd: '.',
         env: definition.env
     };
-    const exec = new vscode.ShellExecution(
-        definition.command,
-        definition.args,
-        execOption
-    );
+    const exec = new vscode.ShellExecution(definition.command, definition.args, execOption);
 
     const f = vscode.workspace.workspaceFolders![0];
     const t = new vscode.Task(
@@ -63,7 +59,7 @@ function createTask(spec: Runnable): vscode.Task {
         exec,
         ['$rustc']
     );
-    t.presentationOptions.clear = true;
+    t.presentationOptions.clear = true
     return t;
 }
 
@@ -118,8 +114,8 @@ export async function handleSingle(runnable: Runnable) {
     task.group = vscode.TaskGroup.Build;
     task.presentationOptions = {
         reveal: vscode.TaskRevealKind.Always,
-        panel: vscode.TaskPanelKind.Dedicated
+        panel: vscode.TaskPanelKind.Dedicated,
     };
-
+    
     return vscode.tasks.executeTask(task);
-}
+}
\ No newline at end of file
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
index 0098c94547..9edfb13b5f 100644
--- a/editors/code/src/extension.ts
+++ b/editors/code/src/extension.ts
@@ -37,10 +37,8 @@ export function activate(context: vscode.ExtensionContext) {
                     return await original(...args);
                 }
             });
-        } catch (_) {
-            vscode.window.showWarningMessage(
-                'Enhanced typing feature is disabled because of incompatibility with VIM extension'
-            );
+        } catch(_) {
+            vscode.window.showWarningMessage('Enhanced typing feature is disabled because of incompatibility with VIM extension');
         }
     }
 


Test patch:
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index 647fd92aad..ba9fcb3d1d 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -235,9 +235,7 @@ fn item_map_across_crates() {
     let mut crate_graph = CrateGraph::default();
     let main_crate = crate_graph.add_crate_root(main_id);
     let lib_crate = crate_graph.add_crate_root(lib_id);
-    crate_graph
-        .add_dep(main_crate, "test_crate".into(), lib_crate)
-        .unwrap();
+    crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate);
 
     db.set_crate_graph(crate_graph);
 
@@ -290,9 +288,7 @@ fn import_across_source_roots() {
     let mut crate_graph = CrateGraph::default();
     let main_crate = crate_graph.add_crate_root(main_id);
     let lib_crate = crate_graph.add_crate_root(lib_id);
-    crate_graph
-        .add_dep(main_crate, "test_crate".into(), lib_crate)
-        .unwrap();
+    crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate);
 
     db.set_crate_graph(crate_graph);
 
@@ -334,9 +330,7 @@ fn reexport_across_crates() {
     let mut crate_graph = CrateGraph::default();
     let main_crate = crate_graph.add_crate_root(main_id);
     let lib_crate = crate_graph.add_crate_root(lib_id);
-    crate_graph
-        .add_dep(main_crate, "test_crate".into(), lib_crate)
-        .unwrap();
+    crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate);
 
     db.set_crate_graph(crate_graph);