aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--Cargo.lock2
-rw-r--r--crates/gen_lsp_server/Cargo.toml3
-rw-r--r--crates/gen_lsp_server/examples/01_gen_lsp_server.rs45
-rw-r--r--crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs118
-rw-r--r--crates/gen_lsp_server/src/lib.rs13
-rw-r--r--crates/ra_assists/src/lib.rs5
-rw-r--r--crates/ra_assists/src/move_guard.rs260
-rw-r--r--crates/ra_assists/src/move_guard_to_arm_body.rs115
-rw-r--r--crates/ra_db/Cargo.toml1
-rw-r--r--crates/ra_db/src/lib.rs5
-rw-r--r--crates/ra_hir/src/ids.rs2
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs30
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs104
-rw-r--r--crates/ra_ide_api/src/lib.rs11
-rw-r--r--crates/ra_ide_api/src/name_ref_kind.rs95
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlighting.snap128
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs49
-rw-r--r--docs/user/README.md2
-rw-r--r--docs/user/features.md33
-rw-r--r--editors/code/package.json47
-rw-r--r--editors/code/src/highlighting.ts7
22 files changed, 839 insertions, 239 deletions
diff --git a/.travis.yml b/.travis.yml
index 940435172..c6e22d765 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,13 @@
1cache: cargo 1cache: cargo
2before_cache: 2before_cache:
3 - find ./target/debug -type f -maxdepth 1 -delete 3 - find ./target/debug -maxdepth 1 -type f -delete
4 - rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*test*,*tools*,*gen_lsp*,*thread_worker*} 4 - rm -fr ./target/debug/{deps,.fingerprint}/{*ra_*,*test*,*tools*,*gen_lsp*,*thread_worker*}
5 - rm -f ./target/.rustc_info.json 5 - rm -f ./target/.rustc_info.json
6 6
7matrix: 7matrix:
8 include: 8 include:
9 - os: linux 9 - os: linux
10 dist: xenial
10 language: rust 11 language: rust
11 rust: stable 12 rust: stable
12 script: 13 script:
diff --git a/Cargo.lock b/Cargo.lock
index 5aa4ff5de..9502fed45 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -491,6 +491,7 @@ version = "0.2.0"
491dependencies = [ 491dependencies = [
492 "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 492 "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
493 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 493 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
494 "flexi_logger 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
494 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 495 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
495 "lsp-types 0.57.1 (registry+https://github.com/rust-lang/crates.io-index)", 496 "lsp-types 0.57.1 (registry+https://github.com/rust-lang/crates.io-index)",
496 "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", 497 "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1089,6 +1090,7 @@ version = "0.1.0"
1089dependencies = [ 1090dependencies = [
1090 "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 1091 "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
1091 "ra_arena 0.1.0", 1092 "ra_arena 0.1.0",
1093 "ra_prof 0.1.0",
1092 "ra_syntax 0.1.0", 1094 "ra_syntax 0.1.0",
1093 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1095 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
1094 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1096 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml
index ba8bfdbd3..fa2fefea5 100644
--- a/crates/gen_lsp_server/Cargo.toml
+++ b/crates/gen_lsp_server/Cargo.toml
@@ -14,3 +14,6 @@ failure = "0.1.4"
14serde_json = "1.0.34" 14serde_json = "1.0.34"
15serde = { version = "1.0.83", features = ["derive"] } 15serde = { version = "1.0.83", features = ["derive"] }
16crossbeam-channel = "0.3.5" 16crossbeam-channel = "0.3.5"
17
18[dev-dependencies]
19flexi_logger = "0.11.0"
diff --git a/crates/gen_lsp_server/examples/01_gen_lsp_server.rs b/crates/gen_lsp_server/examples/01_gen_lsp_server.rs
new file mode 100644
index 000000000..60c581075
--- /dev/null
+++ b/crates/gen_lsp_server/examples/01_gen_lsp_server.rs
@@ -0,0 +1,45 @@
1use crossbeam_channel::{Sender, Receiver};
2use lsp_types::{
3 ServerCapabilities, InitializeParams,
4 request::{GotoDefinition, GotoDefinitionResponse},
5};
6use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse};
7
8fn main() -> Result<(), failure::Error> {
9 let (receiver, sender, io_threads) = stdio_transport();
10 run_server(ServerCapabilities::default(), receiver, sender, main_loop)?;
11 io_threads.join()?;
12 Ok(())
13}
14
15fn main_loop(
16 _params: InitializeParams,
17 receiver: &Receiver<RawMessage>,
18 sender: &Sender<RawMessage>,
19) -> Result<(), failure::Error> {
20 for msg in receiver {
21 match msg {
22 RawMessage::Request(req) => {
23 let req = match handle_shutdown(req, sender) {
24 None => return Ok(()),
25 Some(req) => req,
26 };
27 match req.cast::<GotoDefinition>() {
28 Ok((id, _params)) => {
29 let resp = RawResponse::ok::<GotoDefinition>(
30 id,
31 &Some(GotoDefinitionResponse::Array(Vec::new())),
32 );
33 sender.send(RawMessage::Response(resp))?;
34 continue;
35 }
36 Err(req) => req,
37 };
38 // ...
39 }
40 RawMessage::Response(_resp) => (),
41 RawMessage::Notification(_not) => (),
42 }
43 }
44 Ok(())
45}
diff --git a/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs b/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs
new file mode 100644
index 000000000..27e4f1cbc
--- /dev/null
+++ b/crates/gen_lsp_server/examples/02_gen_lsp_server_with_logging.rs
@@ -0,0 +1,118 @@
1//! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use
2//! this example, execute it and then send an `initialize` request.
3//!
4//! ```no_run
5//! Content-Length: 85
6//!
7//! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}}
8//! ```
9//!
10//! This will respond with a server respose. Then send it a `initialized` notification which will
11//! have no response.
12//!
13//! ```no_run
14//! Content-Length: 59
15//!
16//! {"jsonrpc": "2.0", "method": "initialized", "params": {}}
17//! ```
18//!
19//! Once these two are sent, then we enter the main loop of the server. The only request this
20//! example can handle is `gotoDefinition`:
21//!
22//! ```no_run
23//! Content-Length: 159
24//!
25//! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}}
26//! ```
27//!
28//! To finish up without errors, send a shutdown request:
29//!
30//! ```no_run
31//! Content-Length: 67
32//!
33//! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null}
34//! ```
35//!
36//! The server will exit the main loop and finally we send a `shutdown` notification to stop
37//! the server.
38//!
39//! ```
40//! Content-Length: 54
41//!
42//! {"jsonrpc": "2.0", "method": "exit", "params": null}
43//! ```
44
45use crossbeam_channel::{Sender, Receiver};
46use lsp_types::{
47 ServerCapabilities, InitializeParams,
48 request::{GotoDefinition, GotoDefinitionResponse},
49};
50use log::info;
51use gen_lsp_server::{
52 run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse, RawRequest,
53};
54
55fn main() -> Result<(), failure::Error> {
56 // Set up logging. Because `stdio_transport` gets a lock on stdout and stdin, we must have
57 // our logging only write out to stderr.
58 flexi_logger::Logger::with_str("info").start().unwrap();
59 info!("starting generic LSP server");
60
61 // Create the transport. Includes the stdio (stdin and stdout) versions but this could
62 // also be implemented to use sockets or HTTP.
63 let (receiver, sender, io_threads) = stdio_transport();
64
65 // Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
66 run_server(ServerCapabilities::default(), receiver, sender, main_loop)?;
67 io_threads.join()?;
68
69 // Shut down gracefully.
70 info!("shutting down server");
71 Ok(())
72}
73
74fn main_loop(
75 _params: InitializeParams,
76 receiver: &Receiver<RawMessage>,
77 sender: &Sender<RawMessage>,
78) -> Result<(), failure::Error> {
79 info!("starting example main loop");
80 for msg in receiver {
81 info!("got msg: {:?}", msg);
82 match msg {
83 RawMessage::Request(req) => {
84 let req = match log_handle_shutdown(req, sender) {
85 None => return Ok(()),
86 Some(req) => req,
87 };
88 info!("got request: {:?}", req);
89 match req.cast::<GotoDefinition>() {
90 Ok((id, params)) => {
91 info!("got gotoDefinition request #{}: {:?}", id, params);
92 let resp = RawResponse::ok::<GotoDefinition>(
93 id,
94 &Some(GotoDefinitionResponse::Array(Vec::new())),
95 );
96 info!("sending gotoDefinition response: {:?}", resp);
97 sender.send(RawMessage::Response(resp))?;
98 continue;
99 }
100 Err(req) => req,
101 };
102 // ...
103 }
104 RawMessage::Response(resp) => {
105 info!("got response: {:?}", resp);
106 }
107 RawMessage::Notification(not) => {
108 info!("got notification: {:?}", not);
109 }
110 }
111 }
112 Ok(())
113}
114
115pub fn log_handle_shutdown(req: RawRequest, sender: &Sender<RawMessage>) -> Option<RawRequest> {
116 info!("handle_shutdown: {:?}", req);
117 handle_shutdown(req, sender)
118}
diff --git a/crates/gen_lsp_server/src/lib.rs b/crates/gen_lsp_server/src/lib.rs
index edbdda6c8..1cd5a3a7c 100644
--- a/crates/gen_lsp_server/src/lib.rs
+++ b/crates/gen_lsp_server/src/lib.rs
@@ -2,21 +2,16 @@
2//! This crate handles protocol handshaking and parsing messages, while you 2//! This crate handles protocol handshaking and parsing messages, while you
3//! control the message dispatch loop yourself. 3//! control the message dispatch loop yourself.
4//! 4//!
5//! Run with `RUST_LOG=sync_lsp_server=debug` to see all the messages. 5//! Run with `RUST_LOG=gen_lsp_server=debug` to see all the messages.
6//! 6//!
7//! ```no_run 7//! ```no_run
8//! extern crate gen_lsp_server;
9//! extern crate lsp_types;
10//! extern crate failure;
11//! extern crate crossbeam_channel;
12//!
13//! use crossbeam_channel::{Sender, Receiver}; 8//! use crossbeam_channel::{Sender, Receiver};
14//! use lsp_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}}; 9//! use lsp_types::{ServerCapabilities, InitializeParams, request::{GotoDefinition, GotoDefinitionResponse}};
15//! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse}; 10//! use gen_lsp_server::{run_server, stdio_transport, handle_shutdown, RawMessage, RawResponse};
16//! 11//!
17//! fn main() -> Result<(), failure::Error> { 12//! fn main() -> Result<(), failure::Error> {
18//! let (receiver, sender, io_threads) = stdio_transport(); 13//! let (receiver, sender, io_threads) = stdio_transport();
19//! gen_lsp_server::run_server( 14//! run_server(
20//! ServerCapabilities::default(), 15//! ServerCapabilities::default(),
21//! receiver, 16//! receiver,
22//! sender, 17//! sender,
@@ -38,13 +33,13 @@
38//! None => return Ok(()), 33//! None => return Ok(()),
39//! Some(req) => req, 34//! Some(req) => req,
40//! }; 35//! };
41//! let req = match req.cast::<GotoDefinition>() { 36//! match req.cast::<GotoDefinition>() {
42//! Ok((id, _params)) => { 37//! Ok((id, _params)) => {
43//! let resp = RawResponse::ok::<GotoDefinition>( 38//! let resp = RawResponse::ok::<GotoDefinition>(
44//! id, 39//! id,
45//! &Some(GotoDefinitionResponse::Array(Vec::new())), 40//! &Some(GotoDefinitionResponse::Array(Vec::new())),
46//! ); 41//! );
47//! sender.send(RawMessage::Response(resp)); 42//! sender.send(RawMessage::Response(resp))?;
48//! continue; 43//! continue;
49//! }, 44//! },
50//! Err(req) => req, 45//! Err(req) => req,
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index ae97a1ab5..28eb0226b 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -100,7 +100,7 @@ mod split_import;
100mod remove_dbg; 100mod remove_dbg;
101pub mod auto_import; 101pub mod auto_import;
102mod add_missing_impl_members; 102mod add_missing_impl_members;
103mod move_guard_to_arm_body; 103mod move_guard;
104 104
105fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 105fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
106 &[ 106 &[
@@ -119,7 +119,8 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
119 add_missing_impl_members::add_missing_impl_members, 119 add_missing_impl_members::add_missing_impl_members,
120 add_missing_impl_members::add_missing_default_members, 120 add_missing_impl_members::add_missing_default_members,
121 inline_local_variable::inline_local_varialbe, 121 inline_local_variable::inline_local_varialbe,
122 move_guard_to_arm_body::move_guard_to_arm_body, 122 move_guard::move_guard_to_arm_body,
123 move_guard::move_arm_cond_to_match_guard,
123 ] 124 ]
124} 125}
125 126
diff --git a/crates/ra_assists/src/move_guard.rs b/crates/ra_assists/src/move_guard.rs
new file mode 100644
index 000000000..22ba91fb7
--- /dev/null
+++ b/crates/ra_assists/src/move_guard.rs
@@ -0,0 +1,260 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 TextUnit,
4 SyntaxElement,
5 ast::{MatchArm, AstNode, AstToken, IfExpr},
6 ast,
7};
8
9use crate::{AssistCtx, Assist, AssistId};
10
11pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let match_arm = ctx.node_at_offset::<MatchArm>()?;
13 let guard = match_arm.guard()?;
14 let space_before_guard = guard.syntax().prev_sibling_or_token();
15
16 let guard_conditions = guard.expr()?;
17 let arm_expr = match_arm.expr()?;
18 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
19
20 ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
21 edit.target(guard.syntax().range());
22 let offseting_amount = match space_before_guard {
23 Some(SyntaxElement::Token(tok)) => {
24 if let Some(_) = ast::Whitespace::cast(tok) {
25 let ele = space_before_guard.unwrap().range();
26 edit.delete(ele);
27 ele.len()
28 } else {
29 TextUnit::from(0)
30 }
31 }
32 _ => TextUnit::from(0),
33 };
34
35 edit.delete(guard.syntax().range());
36 edit.replace_node_and_indent(arm_expr.syntax(), buf);
37 edit.set_cursor(arm_expr.syntax().range().start() + TextUnit::from(3) - offseting_amount);
38 });
39 ctx.build()
40}
41
42pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
43 let match_arm: &MatchArm = ctx.node_at_offset::<MatchArm>()?;
44 let last_match_pat = match_arm.pats().last()?;
45
46 let arm_body = match_arm.expr()?;
47 let if_expr: &IfExpr = IfExpr::cast(arm_body.syntax())?;
48 let cond = if_expr.condition()?;
49 let then_block = if_expr.then_branch()?;
50
51 // Not support if with else branch
52 if let Some(_) = if_expr.else_branch() {
53 return None;
54 }
55 // Not support moving if let to arm guard
56 if let Some(_) = cond.pat() {
57 return None;
58 }
59
60 let buf = format!(" if {}", cond.syntax().text());
61
62 ctx.add_action(
63 AssistId("move_arm_cond_to_match_guard"),
64 "move condition to match guard",
65 |edit| {
66 edit.target(if_expr.syntax().range());
67 let then_only_expr = then_block.statements().next().is_none();
68
69 match then_block.expr() {
70 Some(then_expr) if then_only_expr => {
71 edit.replace(if_expr.syntax().range(), then_expr.syntax().text())
72 }
73 _ => edit.replace(if_expr.syntax().range(), then_block.syntax().text()),
74 }
75
76 edit.insert(last_match_pat.syntax().range().end(), buf);
77 edit.set_cursor(last_match_pat.syntax().range().end() + TextUnit::from(1));
78 },
79 );
80 ctx.build()
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 use crate::helpers::{ check_assist, check_assist_target, check_assist_not_applicable };
88
89 #[test]
90 fn move_guard_to_arm_body_target() {
91 check_assist_target(
92 move_guard_to_arm_body,
93 r#"
94 fn f() {
95 let t = 'a';
96 let chars = "abcd";
97 match t {
98 '\r' <|>if chars.clone().next() == Some('\n') => false,
99 _ => true
100 }
101 }
102 "#,
103 r#"if chars.clone().next() == Some('\n')"#,
104 );
105 }
106
107 #[test]
108 fn move_guard_to_arm_body_works() {
109 check_assist(
110 move_guard_to_arm_body,
111 r#"
112 fn f() {
113 let t = 'a';
114 let chars = "abcd";
115 match t {
116 '\r' <|>if chars.clone().next() == Some('\n') => false,
117 _ => true
118 }
119 }
120 "#,
121 r#"
122 fn f() {
123 let t = 'a';
124 let chars = "abcd";
125 match t {
126 '\r' => if chars.clone().next() == Some('\n') { <|>false },
127 _ => true
128 }
129 }
130 "#,
131 );
132 }
133
134 #[test]
135 fn move_guard_to_arm_body_works_complex_match() {
136 check_assist(
137 move_guard_to_arm_body,
138 r#"
139 fn f() {
140 match x {
141 <|>y @ 4 | y @ 5 if y > 5 => true,
142 _ => false
143 }
144 }
145 "#,
146 r#"
147 fn f() {
148 match x {
149 y @ 4 | y @ 5 => if y > 5 { <|>true },
150 _ => false
151 }
152 }
153 "#,
154 );
155 }
156
157 #[test]
158 fn move_arm_cond_to_match_guard_works() {
159 check_assist(
160 move_arm_cond_to_match_guard,
161 r#"
162 fn f() {
163 let t = 'a';
164 let chars = "abcd";
165 match t {
166 '\r' => if chars.clone().next() == Some('\n') { <|>false },
167 _ => true
168 }
169 }
170 "#,
171 r#"
172 fn f() {
173 let t = 'a';
174 let chars = "abcd";
175 match t {
176 '\r' <|>if chars.clone().next() == Some('\n') => false,
177 _ => true
178 }
179 }
180 "#,
181 );
182 }
183
184 #[test]
185 fn move_arm_cond_to_match_guard_if_let_not_works() {
186 check_assist_not_applicable(
187 move_arm_cond_to_match_guard,
188 r#"
189 fn f() {
190 let t = 'a';
191 let chars = "abcd";
192 match t {
193 '\r' => if let Some(_) = chars.clone().next() { <|>false },
194 _ => true
195 }
196 }
197 "#,
198 );
199 }
200
201 #[test]
202 fn move_arm_cond_to_match_guard_if_empty_body_works() {
203 check_assist(
204 move_arm_cond_to_match_guard,
205 r#"
206 fn f() {
207 let t = 'a';
208 let chars = "abcd";
209 match t {
210 '\r' => if chars.clone().next().is_some() { <|> },
211 _ => true
212 }
213 }
214 "#,
215 r#"
216 fn f() {
217 let t = 'a';
218 let chars = "abcd";
219 match t {
220 '\r' <|>if chars.clone().next().is_some() => { },
221 _ => true
222 }
223 }
224 "#,
225 );
226 }
227
228 #[test]
229 fn move_arm_cond_to_match_guard_if_multiline_body_works() {
230 check_assist(
231 move_arm_cond_to_match_guard,
232 r#"
233 fn f() {
234 let mut t = 'a';
235 let chars = "abcd";
236 match t {
237 '\r' => if chars.clone().next().is_some() {
238 t = 'e';<|>
239 false
240 },
241 _ => true
242 }
243 }
244 "#,
245 r#"
246 fn f() {
247 let mut t = 'a';
248 let chars = "abcd";
249 match t {
250 '\r' <|>if chars.clone().next().is_some() => {
251 t = 'e';
252 false
253 },
254 _ => true
255 }
256 }
257 "#,
258 );
259 }
260}
diff --git a/crates/ra_assists/src/move_guard_to_arm_body.rs b/crates/ra_assists/src/move_guard_to_arm_body.rs
deleted file mode 100644
index a8ca19f5d..000000000
--- a/crates/ra_assists/src/move_guard_to_arm_body.rs
+++ /dev/null
@@ -1,115 +0,0 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 TextUnit,
4 SyntaxElement,
5 ast::{MatchArm, AstNode, AstToken},
6 ast,
7};
8
9use crate::{AssistCtx, Assist, AssistId};
10
11pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let match_arm = ctx.node_at_offset::<MatchArm>()?;
13 let guard = match_arm.guard()?;
14 let space_before_guard = guard.syntax().prev_sibling_or_token();
15
16 let guard_conditions = guard.expr()?;
17 let arm_expr = match_arm.expr()?;
18 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
19
20 ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
21 edit.target(guard.syntax().range());
22 let offseting_amount = match space_before_guard {
23 Some(SyntaxElement::Token(tok)) => {
24 if let Some(_) = ast::Whitespace::cast(tok) {
25 let ele = space_before_guard.unwrap().range();
26 edit.delete(ele);
27 ele.len()
28 } else {
29 TextUnit::from(0)
30 }
31 }
32 _ => TextUnit::from(0),
33 };
34
35 edit.delete(guard.syntax().range());
36 edit.replace_node_and_indent(arm_expr.syntax(), buf);
37 edit.set_cursor(arm_expr.syntax().range().start() + TextUnit::from(3) - offseting_amount);
38 });
39 ctx.build()
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 use crate::helpers::{ check_assist, check_assist_target };
47
48 #[test]
49 fn move_guard_to_arm_body_target() {
50 check_assist_target(
51 move_guard_to_arm_body,
52 r#"
53 fn f() {
54 let t = 'a';
55 let chars = "abcd";
56 match t {
57 '\r' <|>if chars.clone().next() == Some('\n') => false,
58 _ => true
59 }
60 }
61 "#,
62 r#"if chars.clone().next() == Some('\n')"#,
63 );
64 }
65
66 #[test]
67 fn move_guard_to_arm_body_works() {
68 check_assist(
69 move_guard_to_arm_body,
70 r#"
71 fn f() {
72 let t = 'a';
73 let chars = "abcd";
74 match t {
75 '\r' <|>if chars.clone().next() == Some('\n') => false,
76 _ => true
77 }
78 }
79 "#,
80 r#"
81 fn f() {
82 let t = 'a';
83 let chars = "abcd";
84 match t {
85 '\r' => if chars.clone().next() == Some('\n') { <|>false },
86 _ => true
87 }
88 }
89 "#,
90 );
91 }
92
93 #[test]
94 fn move_guard_to_arm_body_works_complex_match() {
95 check_assist(
96 move_guard_to_arm_body,
97 r#"
98 fn f() {
99 match x {
100 <|>y @ 4 | y @ 5 if y > 5 => true,
101 _ => false
102 }
103 }
104 "#,
105 r#"
106 fn f() {
107 match x {
108 y @ 4 | y @ 5 => if y > 5 { <|>true },
109 _ => false
110 }
111 }
112 "#,
113 );
114 }
115}
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
index 08aef9bf5..5328303d2 100644
--- a/crates/ra_db/Cargo.toml
+++ b/crates/ra_db/Cargo.toml
@@ -12,4 +12,5 @@ parking_lot = "0.7.0"
12 12
13ra_arena = { path = "../ra_arena" } 13ra_arena = { path = "../ra_arena" }
14ra_syntax = { path = "../ra_syntax" } 14ra_syntax = { path = "../ra_syntax" }
15ra_prof = { path = "../ra_prof" }
15test_utils = { path = "../test_utils" } 16test_utils = { path = "../test_utils" }
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index bf567721a..68b9a7143 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -6,6 +6,7 @@ use std::{panic, sync::Arc};
6 6
7use ra_syntax::{TextUnit, TextRange, SourceFile, TreeArc}; 7use ra_syntax::{TextUnit, TextRange, SourceFile, TreeArc};
8use relative_path::RelativePathBuf; 8use relative_path::RelativePathBuf;
9use ra_prof::profile;
9 10
10pub use ::salsa as salsa; 11pub use ::salsa as salsa;
11pub use crate::{ 12pub use crate::{
@@ -72,6 +73,7 @@ pub trait SourceDatabase: CheckCanceled + std::fmt::Debug {
72 #[salsa::input] 73 #[salsa::input]
73 fn file_text(&self, file_id: FileId) -> Arc<String>; 74 fn file_text(&self, file_id: FileId) -> Arc<String>;
74 // Parses the file into the syntax tree. 75 // Parses the file into the syntax tree.
76 #[salsa::invoke(parse_query)]
75 fn parse(&self, file_id: FileId) -> TreeArc<SourceFile>; 77 fn parse(&self, file_id: FileId) -> TreeArc<SourceFile>;
76 /// Path to a file, relative to the root of its source root. 78 /// Path to a file, relative to the root of its source root.
77 #[salsa::input] 79 #[salsa::input]
@@ -96,7 +98,8 @@ fn source_root_crates(db: &impl SourceDatabase, id: SourceRootId) -> Arc<Vec<Cra
96 Arc::new(res) 98 Arc::new(res)
97} 99}
98 100
99fn parse(db: &impl SourceDatabase, file_id: FileId) -> TreeArc<SourceFile> { 101fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> TreeArc<SourceFile> {
102 let _p = profile("parse_query");
100 let text = db.file_text(file_id); 103 let text = db.file_text(file_id);
101 SourceFile::parse(&*text) 104 SourceFile::parse(&*text)
102} 105}
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index f901a7432..2eb7f0da0 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -5,6 +5,7 @@ use std::{
5 5
6use ra_db::{FileId, salsa}; 6use ra_db::{FileId, salsa};
7use ra_syntax::{TreeArc, AstNode, ast, SyntaxNode}; 7use ra_syntax::{TreeArc, AstNode, ast, SyntaxNode};
8use ra_prof::profile;
8use mbe::MacroRules; 9use mbe::MacroRules;
9 10
10use crate::{ 11use crate::{
@@ -60,6 +61,7 @@ impl HirFileId {
60 db: &impl DefDatabase, 61 db: &impl DefDatabase,
61 file_id: HirFileId, 62 file_id: HirFileId,
62 ) -> Option<TreeArc<SyntaxNode>> { 63 ) -> Option<TreeArc<SyntaxNode>> {
64 let _p = profile("parse_or_expand_query");
63 match file_id.0 { 65 match file_id.0 {
64 HirFileIdRepr::File(file_id) => Some(db.parse(file_id).syntax().to_owned()), 66 HirFileIdRepr::File(file_id) => Some(db.parse(file_id).syntax().to_owned()),
65 HirFileIdRepr::Macro(macro_file) => { 67 HirFileIdRepr::Macro(macro_file) => {
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 7ea336c50..1c694cbc9 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, 5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
6 algo::visit::{visitor, Visitor}, 6 algo::visit::{visitor, Visitor},
7}; 7};
8use hir::{ModuleSource, FieldSource, ImplItem, Either}; 8use hir::{ModuleSource, FieldSource, ImplItem};
9 9
10use crate::{FileSymbol, db::RootDatabase}; 10use crate::{FileSymbol, db::RootDatabase};
11 11
@@ -77,17 +77,12 @@ impl NavigationTarget {
77 pub(crate) fn from_pat( 77 pub(crate) fn from_pat(
78 db: &RootDatabase, 78 db: &RootDatabase,
79 file_id: FileId, 79 file_id: FileId,
80 pat: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, 80 pat: AstPtr<ast::Pat>,
81 ) -> NavigationTarget { 81 ) -> NavigationTarget {
82 let file = db.parse(file_id); 82 let file = db.parse(file_id);
83 let (name, full_range) = match pat { 83 let (name, full_range) = match pat.to_node(file.syntax()).kind() {
84 Either::A(pat) => match pat.to_node(file.syntax()).kind() { 84 ast::PatKind::BindPat(pat) => return NavigationTarget::from_bind_pat(file_id, &pat),
85 ast::PatKind::BindPat(pat) => { 85 _ => ("_".into(), pat.syntax_node_ptr().range()),
86 return NavigationTarget::from_bind_pat(file_id, &pat)
87 }
88 _ => ("_".into(), pat.syntax_node_ptr().range()),
89 },
90 Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()),
91 }; 86 };
92 NavigationTarget { 87 NavigationTarget {
93 file_id, 88 file_id,
@@ -99,6 +94,21 @@ impl NavigationTarget {
99 } 94 }
100 } 95 }
101 96
97 pub(crate) fn from_self_param(
98 file_id: FileId,
99 par: AstPtr<ast::SelfParam>,
100 ) -> NavigationTarget {
101 let (name, full_range) = ("self".into(), par.syntax_node_ptr().range());
102 NavigationTarget {
103 file_id,
104 name,
105 full_range,
106 focus_range: None,
107 kind: NAME,
108 container_name: None,
109 }
110 }
111
102 pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 112 pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
103 let (file_id, source) = module.definition_source(db); 113 let (file_id, source) = module.definition_source(db);
104 let file_id = file_id.as_original_file(); 114 let file_id = file_id.as_original_file();
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index adae29e9c..9c56f17f2 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -1,12 +1,19 @@
1use ra_db::{FileId, SourceDatabase}; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, ast, 3 AstNode, ast,
4 algo::{find_node_at_offset, visit::{visitor, Visitor}}, 4 algo::{
5 find_node_at_offset,
6 visit::{visitor, Visitor},
7 },
5 SyntaxNode, 8 SyntaxNode,
6}; 9};
7use test_utils::tested_by;
8 10
9use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; 11use crate::{
12 FilePosition, NavigationTarget,
13 db::RootDatabase,
14 RangeInfo,
15 name_ref_kind::{NameRefKind::*, classify_name_ref},
16};
10 17
11pub(crate) fn goto_definition( 18pub(crate) fn goto_definition(
12 db: &RootDatabase, 19 db: &RootDatabase,
@@ -50,85 +57,24 @@ pub(crate) fn reference_definition(
50 57
51 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); 58 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
52 59
53 // Special cases: 60 match classify_name_ref(db, &analyzer, name_ref) {
54 61 Some(Method(func)) => return Exact(NavigationTarget::from_function(db, func)),
55 // Check if it is a method 62 Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)),
56 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { 63 Some(FieldAccess(field)) => return Exact(NavigationTarget::from_field(db, field)),
57 tested_by!(goto_definition_works_for_methods); 64 Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_impl_item(db, assoc)),
58 if let Some(func) = analyzer.resolve_method_call(method_call) { 65 Some(Def(def)) => return Exact(NavigationTarget::from_def(db, def)),
59 return Exact(NavigationTarget::from_function(db, func)); 66 Some(SelfType(ty)) => {
60 } 67 if let Some((def_id, _)) = ty.as_adt() {
61 } 68 return Exact(NavigationTarget::from_adt_def(db, def_id));
62
63 //it could be a macro call
64 if let Some(macro_call) = name_ref
65 .syntax()
66 .parent()
67 .and_then(|node| node.parent())
68 .and_then(|node| node.parent())
69 .and_then(ast::MacroCall::cast)
70 {
71 tested_by!(goto_definition_works_for_macros);
72 if let Some(macro_call) = analyzer.resolve_macro_call(macro_call) {
73 return Exact(NavigationTarget::from_macro_def(db, macro_call));
74 }
75 }
76
77 // It could also be a field access
78 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
79 tested_by!(goto_definition_works_for_fields);
80 if let Some(field) = analyzer.resolve_field(field_expr) {
81 return Exact(NavigationTarget::from_field(db, field));
82 };
83 }
84
85 // It could also be a named field
86 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
87 tested_by!(goto_definition_works_for_named_fields);
88
89 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
90
91 if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) {
92 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
93 let hir_path = hir::Path::from_name_ref(name_ref);
94 let hir_name = hir_path.as_ident().unwrap();
95
96 if let Some(field) = s.field(db, hir_name) {
97 return Exact(NavigationTarget::from_field(db, field));
98 }
99 } 69 }
100 } 70 }
101 } 71 Some(Pat(pat)) => return Exact(NavigationTarget::from_pat(db, file_id, pat)),
102 72 Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)),
103 // General case, a path or a local: 73 Some(GenericParam(_)) => {
104 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { 74 // FIXME: go to the generic param def
105 if let Some(resolved) = analyzer.resolve_path(db, path) {
106 match resolved {
107 hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)),
108 hir::PathResolution::LocalBinding(pat) => {
109 let nav = NavigationTarget::from_pat(db, file_id, pat);
110 return Exact(nav);
111 }
112 hir::PathResolution::GenericParam(..) => {
113 // FIXME: go to the generic param def
114 }
115 hir::PathResolution::Macro(def) => {
116 let nav = NavigationTarget::from_macro_def(db, def);
117 return Exact(nav);
118 }
119 hir::PathResolution::SelfType(impl_block) => {
120 let ty = impl_block.target_ty(db);
121
122 if let Some((def_id, _)) = ty.as_adt() {
123 return Exact(NavigationTarget::from_adt_def(db, def_id));
124 }
125 }
126 hir::PathResolution::AssocItem(assoc) => {
127 return Exact(NavigationTarget::from_impl_item(db, assoc));
128 }
129 }
130 } 75 }
131 } 76 None => {}
77 };
132 78
133 // Fallback index based approach: 79 // Fallback index based approach:
134 let navs = crate::symbol_index::index_resolve(db, name_ref) 80 let navs = crate::symbol_index::index_resolve(db, name_ref)
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index d4be8bd6c..f78348f74 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -18,6 +18,7 @@ mod change;
18mod status; 18mod status;
19mod completion; 19mod completion;
20mod runnables; 20mod runnables;
21mod name_ref_kind;
21mod goto_definition; 22mod goto_definition;
22mod goto_type_definition; 23mod goto_type_definition;
23mod extend_selection; 24mod extend_selection;
@@ -53,10 +54,7 @@ use ra_db::{
53}; 54};
54use relative_path::RelativePathBuf; 55use relative_path::RelativePathBuf;
55 56
56use crate::{ 57use crate::{symbol_index::FileSymbol, db::LineIndexDatabase};
57 symbol_index::FileSymbol,
58 db::LineIndexDatabase,
59};
60 58
61pub use crate::{ 59pub use crate::{
62 change::{AnalysisChange, LibraryData}, 60 change::{AnalysisChange, LibraryData},
@@ -73,10 +71,7 @@ pub use crate::{
73 display::{FunctionSignature, NavigationTarget, StructureNode, file_structure}, 71 display::{FunctionSignature, NavigationTarget, StructureNode, file_structure},
74}; 72};
75 73
76pub use ra_db::{ 74pub use ra_db::{Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, Edition};
77 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
78 Edition
79};
80pub use hir::Documentation; 75pub use hir::Documentation;
81 76
82// We use jemalloc mainly to get heap usage statistics, actual performance 77// We use jemalloc mainly to get heap usage statistics, actual performance
diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs
new file mode 100644
index 000000000..b498fe495
--- /dev/null
+++ b/crates/ra_ide_api/src/name_ref_kind.rs
@@ -0,0 +1,95 @@
1use ra_syntax::{AstNode, AstPtr, ast};
2use hir::Either;
3use crate::db::RootDatabase;
4use test_utils::tested_by;
5
6pub enum NameRefKind {
7 Method(hir::Function),
8 Macro(hir::MacroByExampleDef),
9 FieldAccess(hir::StructField),
10 AssocItem(hir::ImplItem),
11 Def(hir::ModuleDef),
12 SelfType(hir::Ty),
13 Pat(AstPtr<ast::Pat>),
14 SelfParam(AstPtr<ast::SelfParam>),
15 GenericParam(u32),
16}
17
18pub(crate) fn classify_name_ref(
19 db: &RootDatabase,
20 analyzer: &hir::SourceAnalyzer,
21 name_ref: &ast::NameRef,
22) -> Option<NameRefKind> {
23 use NameRefKind::*;
24
25 // Check if it is a method
26 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
27 tested_by!(goto_definition_works_for_methods);
28 if let Some(func) = analyzer.resolve_method_call(method_call) {
29 return Some(Method(func));
30 }
31 }
32
33 // It could be a macro call
34 if let Some(macro_call) = name_ref
35 .syntax()
36 .parent()
37 .and_then(|node| node.parent())
38 .and_then(|node| node.parent())
39 .and_then(ast::MacroCall::cast)
40 {
41 tested_by!(goto_definition_works_for_macros);
42 if let Some(mac) = analyzer.resolve_macro_call(macro_call) {
43 return Some(Macro(mac));
44 }
45 }
46
47 // It could also be a field access
48 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
49 tested_by!(goto_definition_works_for_fields);
50 if let Some(field) = analyzer.resolve_field(field_expr) {
51 return Some(FieldAccess(field));
52 };
53 }
54
55 // It could also be a named field
56 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
57 tested_by!(goto_definition_works_for_named_fields);
58
59 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
60
61 if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) {
62 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
63 let hir_path = hir::Path::from_name_ref(name_ref);
64 let hir_name = hir_path.as_ident().unwrap();
65
66 if let Some(field) = s.field(db, hir_name) {
67 return Some(FieldAccess(field));
68 }
69 }
70 }
71 }
72
73 // General case, a path or a local:
74 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
75 if let Some(resolved) = analyzer.resolve_path(db, path) {
76 return match resolved {
77 hir::PathResolution::Def(def) => Some(Def(def)),
78 hir::PathResolution::LocalBinding(Either::A(pat)) => Some(Pat(pat)),
79 hir::PathResolution::LocalBinding(Either::B(par)) => Some(SelfParam(par)),
80 hir::PathResolution::GenericParam(par) => {
81 // FIXME: get generic param def
82 Some(GenericParam(par))
83 }
84 hir::PathResolution::Macro(def) => Some(Macro(def)),
85 hir::PathResolution::SelfType(impl_block) => {
86 let ty = impl_block.target_ty(db);
87 Some(SelfType(ty))
88 }
89 hir::PathResolution::AssocItem(assoc) => Some(AssocItem(assoc)),
90 };
91 }
92 }
93
94 None
95}
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
index 72029e0ed..9d4c04db3 100644
--- a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
+++ b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
@@ -1,33 +1,145 @@
1--- 1---
2created: "2019-03-23T16:20:31.394314144Z" 2created: "2019-05-23T12:10:32.628883358Z"
3creator: insta@0.7.1 3creator: insta@0.8.1
4source: crates/ra_ide_api/src/syntax_highlighting.rs 4source: crates/ra_ide_api/src/syntax_highlighting.rs
5expression: result 5expression: result
6--- 6---
7Ok( 7Ok(
8 [ 8 [
9 HighlightedRange { 9 HighlightedRange {
10 range: [1; 11), 10 range: [1; 24),
11 tag: "attribute"
12 },
13 HighlightedRange {
14 range: [25; 31),
15 tag: "keyword"
16 },
17 HighlightedRange {
18 range: [32; 35),
19 tag: "function"
20 },
21 HighlightedRange {
22 range: [42; 45),
23 tag: "keyword"
24 },
25 HighlightedRange {
26 range: [46; 47),
27 tag: "function"
28 },
29 HighlightedRange {
30 range: [49; 52),
31 tag: "text"
32 },
33 HighlightedRange {
34 range: [58; 61),
35 tag: "keyword"
36 },
37 HighlightedRange {
38 range: [62; 63),
39 tag: "function"
40 },
41 HighlightedRange {
42 range: [65; 68),
43 tag: "text"
44 },
45 HighlightedRange {
46 range: [73; 75),
47 tag: "keyword"
48 },
49 HighlightedRange {
50 range: [76; 79),
51 tag: "function"
52 },
53 HighlightedRange {
54 range: [80; 81),
55 tag: "type"
56 },
57 HighlightedRange {
58 range: [80; 81),
59 tag: "function"
60 },
61 HighlightedRange {
62 range: [88; 89),
63 tag: "type"
64 },
65 HighlightedRange {
66 range: [96; 110),
67 tag: "macro"
68 },
69 HighlightedRange {
70 range: [117; 127),
11 tag: "comment" 71 tag: "comment"
12 }, 72 },
13 HighlightedRange { 73 HighlightedRange {
14 range: [12; 14), 74 range: [128; 130),
15 tag: "keyword" 75 tag: "keyword"
16 }, 76 },
17 HighlightedRange { 77 HighlightedRange {
18 range: [15; 19), 78 range: [131; 135),
19 tag: "function" 79 tag: "function"
20 }, 80 },
21 HighlightedRange { 81 HighlightedRange {
22 range: [29; 37), 82 range: [145; 153),
23 tag: "macro" 83 tag: "macro"
24 }, 84 },
25 HighlightedRange { 85 HighlightedRange {
26 range: [38; 50), 86 range: [154; 166),
27 tag: "string" 87 tag: "string"
28 }, 88 },
29 HighlightedRange { 89 HighlightedRange {
30 range: [52; 54), 90 range: [168; 170),
91 tag: "literal"
92 },
93 HighlightedRange {
94 range: [178; 181),
95 tag: "keyword"
96 },
97 HighlightedRange {
98 range: [182; 185),
99 tag: "keyword"
100 },
101 HighlightedRange {
102 range: [186; 189),
103 tag: "macro"
104 },
105 HighlightedRange {
106 range: [197; 200),
107 tag: "macro"
108 },
109 HighlightedRange {
110 range: [192; 195),
111 tag: "text"
112 },
113 HighlightedRange {
114 range: [208; 211),
115 tag: "macro"
116 },
117 HighlightedRange {
118 range: [212; 216),
119 tag: "macro"
120 },
121 HighlightedRange {
122 range: [226; 227),
123 tag: "literal"
124 },
125 HighlightedRange {
126 range: [232; 233),
127 tag: "literal"
128 },
129 HighlightedRange {
130 range: [242; 248),
131 tag: "keyword.unsafe"
132 },
133 HighlightedRange {
134 range: [251; 254),
135 tag: "text"
136 },
137 HighlightedRange {
138 range: [255; 262),
139 tag: "text"
140 },
141 HighlightedRange {
142 range: [263; 264),
31 tag: "literal" 143 tag: "literal"
32 } 144 }
33 ] 145 ]
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 2158291dc..89f20260f 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -40,8 +40,41 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
40 COMMENT => "comment", 40 COMMENT => "comment",
41 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", 41 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
42 ATTR => "attribute", 42 ATTR => "attribute",
43 NAME_REF => "text", 43 NAME_REF => {
44 if let Some(name_ref) = node.as_node().and_then(|n| ast::NameRef::cast(n)) {
45 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
46 use hir::{ModuleDef, ImplItem};
47
48 // FIXME: try to reuse the SourceAnalyzers
49 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
50 match classify_name_ref(db, &analyzer, name_ref) {
51 Some(Method(_)) => "function",
52 Some(Macro(_)) => "macro",
53 Some(FieldAccess(_)) => "field",
54 Some(AssocItem(ImplItem::Method(_))) => "function",
55 Some(AssocItem(ImplItem::Const(_))) => "constant",
56 Some(AssocItem(ImplItem::TypeAlias(_))) => "type",
57 Some(Def(ModuleDef::Module(_))) => "module",
58 Some(Def(ModuleDef::Function(_))) => "function",
59 Some(Def(ModuleDef::Struct(_))) => "type",
60 Some(Def(ModuleDef::Enum(_))) => "type",
61 Some(Def(ModuleDef::EnumVariant(_))) => "constant",
62 Some(Def(ModuleDef::Const(_))) => "constant",
63 Some(Def(ModuleDef::Static(_))) => "constant",
64 Some(Def(ModuleDef::Trait(_))) => "type",
65 Some(Def(ModuleDef::TypeAlias(_))) => "type",
66 Some(SelfType(_)) => "type",
67 Some(Pat(_)) => "text",
68 Some(SelfParam(_)) => "type",
69 Some(GenericParam(_)) => "type",
70 None => "text",
71 }
72 } else {
73 "text"
74 }
75 }
44 NAME => "function", 76 NAME => "function",
77 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type",
45 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", 78 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
46 LIFETIME => "parameter", 79 LIFETIME => "parameter",
47 T![unsafe] => "keyword.unsafe", 80 T![unsafe] => "keyword.unsafe",
@@ -87,9 +120,23 @@ mod tests {
87 fn test_highlighting() { 120 fn test_highlighting() {
88 let (analysis, file_id) = single_file( 121 let (analysis, file_id) = single_file(
89 r#" 122 r#"
123#[derive(Clone, Debug)]
124struct Foo {
125 pub x: i32,
126 pub y: i32,
127}
128
129fn foo<T>() -> T {
130 unimplemented!();
131}
132
90// comment 133// comment
91fn main() {} 134fn main() {}
92 println!("Hello, {}!", 92); 135 println!("Hello, {}!", 92);
136
137 let mut vec = Vec::new();
138 vec.push(Foo { x: 0, y: 1 });
139 unsafe { vec.set_len(0); }
93"#, 140"#,
94 ); 141 );
95 let result = analysis.highlight(file_id); 142 let result = analysis.highlight(file_id);
diff --git a/docs/user/README.md b/docs/user/README.md
index 1b4d13d0f..affb96939 100644
--- a/docs/user/README.md
+++ b/docs/user/README.md
@@ -64,7 +64,7 @@ for details.
64* `rust-analyzer.showWorkspaceLoadedNotification`: to ease troubleshooting, a 64* `rust-analyzer.showWorkspaceLoadedNotification`: to ease troubleshooting, a
65 notification is shown by default when a workspace is loaded 65 notification is shown by default when a workspace is loaded
66* `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts 66* `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts
67 `Enter` key to make it easier to continue comments 67 `Enter` key to make it easier to continue comments. Note that it may conflict with VIM emulation plugin.
68* `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable 68* `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable
69* `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo 69* `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo
70 watch` for live error highlighting (note, this **does not** use rust-analyzer) 70 watch` for live error highlighting (note, this **does not** use rust-analyzer)
diff --git a/docs/user/features.md b/docs/user/features.md
index a714574fb..22470bc56 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -390,14 +390,14 @@ fn foo() {
390 390
391- Move guard expression to match arm body 391- Move guard expression to match arm body
392```rust 392```rust
393//before: 393// before:
394fn f() { 394fn f() {
395 match x { 395 match x {
396 <|>y @ 4 | y @ 5 if y > 5 => true, 396 <|>y @ 4 | y @ 5 if y > 5 => true,
397 _ => false 397 _ => false
398 } 398 }
399} 399}
400//after: 400// after:
401fn f() { 401fn f() {
402 match x { 402 match x {
403 y @ 4 | y @ 5 => if y > 5 { <|>true }, 403 y @ 4 | y @ 5 => if y > 5 { <|>true },
@@ -406,6 +406,35 @@ fn f() {
406} 406}
407``` 407```
408 408
409- Move if condition to match arm guard
410```rust
411// before:
412fn f() {
413 let mut t = 'a';
414 let chars = "abcd";
415 match t {
416 '\r' => if chars.clone().next().is_some() {
417 t = 'e';<|>
418 false
419 },
420 _ => true
421 }
422}
423
424// after:
425fn f() {
426 let mut t = 'a';
427 let chars = "abcd";
428 match t {
429 '\r' <|>if chars.clone().next().is_some() => {
430 t = 'e';
431 false
432 },
433 _ => true
434 }
435}
436```
437
409### Magic Completions 438### Magic Completions
410 439
411In addition to usual reference completion, rust-analyzer provides some ✨magic✨ 440In addition to usual reference completion, rust-analyzer provides some ✨magic✨
diff --git a/editors/code/package.json b/editors/code/package.json
index 750c97bb1..cde5fbcb8 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -371,12 +371,57 @@
371 }, 371 },
372 { 372 {
373 "id": "ralsp.macro", 373 "id": "ralsp.macro",
374 "description": "Color for DFAF8F", 374 "description": "Color for macros",
375 "defaults": { 375 "defaults": {
376 "dark": "#BFEBBF", 376 "dark": "#BFEBBF",
377 "light": "#DD6718", 377 "light": "#DD6718",
378 "highContrast": "#ED7718" 378 "highContrast": "#ED7718"
379 } 379 }
380 },
381 {
382 "id": "ralsp.constant",
383 "description": "Color for constants",
384 "defaults": {
385 "dark": "#569cd6",
386 "light": "#267cb6",
387 "highContrast": "#569cd6"
388 }
389 },
390 {
391 "id": "ralsp.type",
392 "description": "Color for types",
393 "defaults": {
394 "dark": "#4EC9B0",
395 "light": "#267F99",
396 "highContrast": "#4EC9B0"
397 }
398 },
399 {
400 "id": "ralsp.field",
401 "description": "Color for fields",
402 "defaults": {
403 "dark": "#4EC9B0",
404 "light": "#267F99",
405 "highContrast": "#4EC9B0"
406 }
407 },
408 {
409 "id": "ralsp.variable",
410 "description": "Color for variables",
411 "defaults": {
412 "dark": "#4EC9B0",
413 "light": "#267F99",
414 "highContrast": "#4EC9B0"
415 }
416 },
417 {
418 "id": "ralsp.module",
419 "description": "Color for modules",
420 "defaults": {
421 "dark": "#D4D4D4",
422 "light": "#000000",
423 "highContrast": "#FFFFFF"
424 }
380 } 425 }
381 ] 426 ]
382 } 427 }
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index e1a68544a..8389d94b8 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -33,11 +33,16 @@ export class Highlighter {
33 colorContrib('keyword.unsafe'), 33 colorContrib('keyword.unsafe'),
34 colorContrib('function'), 34 colorContrib('function'),
35 colorContrib('parameter'), 35 colorContrib('parameter'),
36 colorContrib('constant'),
37 colorContrib('type'),
36 colorContrib('builtin'), 38 colorContrib('builtin'),
37 colorContrib('text'), 39 colorContrib('text'),
38 colorContrib('attribute'), 40 colorContrib('attribute'),
39 colorContrib('literal'), 41 colorContrib('literal'),
40 colorContrib('macro') 42 colorContrib('macro'),
43 colorContrib('variable'),
44 colorContrib('field'),
45 colorContrib('module')
41 ]; 46 ];
42 47
43 return new Map<string, vscode.TextEditorDecorationType>(decorations); 48 return new Map<string, vscode.TextEditorDecorationType>(decorations);