aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock11
-rw-r--r--Cargo.toml3
-rw-r--r--crates/ra_analysis/src/completion/complete_keyword.rs43
-rw-r--r--crates/ra_analysis/src/completion/complete_path.rs14
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs38
-rw-r--r--crates/ra_analysis/src/completion/completion_context.rs22
-rw-r--r--crates/ra_analysis/src/completion/completion_item.rs29
-rw-r--r--crates/ra_analysis/src/imp.rs3
-rw-r--r--crates/ra_analysis/src/lib.rs3
-rw-r--r--crates/ra_hir/src/db.rs6
-rw-r--r--crates/ra_hir/src/query_definitions.rs14
-rw-r--r--crates/ra_hir/src/ty.rs77
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/caps.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs37
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs58
-rw-r--r--crates/ra_vfs/src/io.rs4
-rw-r--r--crates/ra_vfs/src/lib.rs16
-rw-r--r--crates/tools/src/lib.rs2
-rw-r--r--editors/README.md2
22 files changed, 299 insertions, 88 deletions
diff --git a/.gitignore b/.gitignore
index 76bc7bf57..68158e41c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ crates/*/target
4.idea/* 4.idea/*
5.vscode/* 5.vscode/*
6*.log 6*.log
7*.iml
diff --git a/Cargo.lock b/Cargo.lock
index fa3363bd2..8e962d352 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -696,7 +696,7 @@ dependencies = [
696 "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 696 "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
697 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 697 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
698 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 698 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
699 "salsa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 699 "salsa 0.9.0 (git+https://github.com/matklad/salsa.git?branch=transitive-untracked)",
700 "test_utils 0.1.0", 700 "test_utils 0.1.0",
701] 701]
702 702
@@ -722,7 +722,7 @@ dependencies = [
722 "ra_syntax 0.1.0", 722 "ra_syntax 0.1.0",
723 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 723 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
724 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 724 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
725 "salsa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 725 "salsa 0.9.0 (git+https://github.com/matklad/salsa.git?branch=transitive-untracked)",
726 "test_utils 0.1.0", 726 "test_utils 0.1.0",
727] 727]
728 728
@@ -755,7 +755,7 @@ dependencies = [
755 "ra_syntax 0.1.0", 755 "ra_syntax 0.1.0",
756 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 756 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
757 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 757 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
758 "salsa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 758 "salsa 0.9.0 (git+https://github.com/matklad/salsa.git?branch=transitive-untracked)",
759 "test_utils 0.1.0", 759 "test_utils 0.1.0",
760] 760]
761 761
@@ -790,6 +790,7 @@ dependencies = [
790 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 790 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
791 "thread_worker 0.1.0", 791 "thread_worker 0.1.0",
792 "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 792 "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
793 "tools 0.1.0",
793 "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 794 "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
794 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 795 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
795] 796]
@@ -1059,7 +1060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1059[[package]] 1060[[package]]
1060name = "salsa" 1061name = "salsa"
1061version = "0.9.0" 1062version = "0.9.0"
1062source = "registry+https://github.com/rust-lang/crates.io-index" 1063source = "git+https://github.com/matklad/salsa.git?branch=transitive-untracked#a2198f1f8a1d09894b842035a1af6b0b4c133c93"
1063dependencies = [ 1064dependencies = [
1064 "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", 1065 "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
1065 "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1066 "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1631,7 +1632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1631"checksum rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9591f190d2852720b679c21f66ad929f9f1d7bb09d1193c26167586029d8489c" 1632"checksum rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9591f190d2852720b679c21f66ad929f9f1d7bb09d1193c26167586029d8489c"
1632"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" 1633"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
1633"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" 1634"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
1634"checksum salsa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0246018bf824f282cb89c2ba9b9ef81093c563487c29ffafe20e1c129a6850" 1635"checksum salsa 0.9.0 (git+https://github.com/matklad/salsa.git?branch=transitive-untracked)" = "<none>"
1635"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" 1636"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
1636"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1637"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
1637"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1638"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
diff --git a/Cargo.toml b/Cargo.toml
index 95830561d..dd8499c06 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,3 +4,6 @@ members = [ "crates/*" ]
4[profile.release] 4[profile.release]
5incremental = true 5incremental = true
6debug = true 6debug = true
7
8[patch.'crates-io']
9salsa = { git = "https://github.com/matklad/salsa.git", branch = "transitive-untracked" }
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs
index d1e0a20a8..2869e67e0 100644
--- a/crates/ra_analysis/src/completion/complete_keyword.rs
+++ b/crates/ra_analysis/src/completion/complete_keyword.rs
@@ -35,7 +35,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
35 acc.add(keyword("continue", "continue")); 35 acc.add(keyword("continue", "continue"));
36 acc.add(keyword("break", "break")); 36 acc.add(keyword("break", "break"));
37 } 37 }
38 acc.add_all(complete_return(fn_def, ctx.is_stmt)); 38 acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
39} 39}
40 40
41fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool { 41fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
@@ -57,8 +57,8 @@ fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
57 false 57 false
58} 58}
59 59
60fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem> { 60fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
61 let snip = match (is_stmt, fn_def.ret_type().is_some()) { 61 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
62 (true, true) => "return $0;", 62 (true, true) => "return $0;",
63 (true, false) => "return;", 63 (true, false) => "return;",
64 (false, true) => "return $0", 64 (false, true) => "return $0",
@@ -75,7 +75,7 @@ mod tests {
75 } 75 }
76 76
77 #[test] 77 #[test]
78 fn test_completion_kewords() { 78 fn completes_various_keywords_in_function() {
79 check_keyword_completion( 79 check_keyword_completion(
80 r" 80 r"
81 fn quux() { 81 fn quux() {
@@ -87,13 +87,13 @@ mod tests {
87 match "match $0 {}" 87 match "match $0 {}"
88 while "while $0 {}" 88 while "while $0 {}"
89 loop "loop {$0}" 89 loop "loop {$0}"
90 return "return" 90 return "return;"
91 "#, 91 "#,
92 ); 92 );
93 } 93 }
94 94
95 #[test] 95 #[test]
96 fn test_completion_else() { 96 fn completes_else_after_if() {
97 check_keyword_completion( 97 check_keyword_completion(
98 r" 98 r"
99 fn quux() { 99 fn quux() {
@@ -109,7 +109,7 @@ mod tests {
109 loop "loop {$0}" 109 loop "loop {$0}"
110 else "else {$0}" 110 else "else {$0}"
111 else if "else if $0 {}" 111 else if "else if $0 {}"
112 return "return" 112 return "return;"
113 "#, 113 "#,
114 ); 114 );
115 } 115 }
@@ -149,7 +149,7 @@ mod tests {
149 } 149 }
150 150
151 #[test] 151 #[test]
152 fn test_completion_return_no_stmt() { 152 fn dont_add_semi_after_return_if_not_a_statement() {
153 check_keyword_completion( 153 check_keyword_completion(
154 r" 154 r"
155 fn quux() -> i32 { 155 fn quux() -> i32 {
@@ -169,7 +169,27 @@ mod tests {
169 } 169 }
170 170
171 #[test] 171 #[test]
172 fn test_continue_break_completion() { 172 fn last_return_in_block_has_semi() {
173 check_keyword_completion(
174 r"
175 fn quux() -> i32 {
176 if condition {
177 <|>
178 }
179 }
180 ",
181 r#"
182 if "if $0 {}"
183 match "match $0 {}"
184 while "while $0 {}"
185 loop "loop {$0}"
186 return "return $0;"
187 "#,
188 );
189 }
190
191 #[test]
192 fn completes_break_and_continue_in_loops() {
173 check_keyword_completion( 193 check_keyword_completion(
174 r" 194 r"
175 fn quux() -> i32 { 195 fn quux() -> i32 {
@@ -183,9 +203,10 @@ mod tests {
183 loop "loop {$0}" 203 loop "loop {$0}"
184 continue "continue" 204 continue "continue"
185 break "break" 205 break "break"
186 return "return $0" 206 return "return $0;"
187 "#, 207 "#,
188 ); 208 );
209 // No completion: lambda isolates control flow
189 check_keyword_completion( 210 check_keyword_completion(
190 r" 211 r"
191 fn quux() -> i32 { 212 fn quux() -> i32 {
@@ -197,7 +218,7 @@ mod tests {
197 match "match $0 {}" 218 match "match $0 {}"
198 while "while $0 {}" 219 while "while $0 {}"
199 loop "loop {$0}" 220 loop "loop {$0}"
200 return "return $0" 221 return "return $0;"
201 "#, 222 "#,
202 ); 223 );
203 } 224 }
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs
index c73a083a4..4723a65a6 100644
--- a/crates/ra_analysis/src/completion/complete_path.rs
+++ b/crates/ra_analysis/src/completion/complete_path.rs
@@ -17,7 +17,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
17 let module_scope = module.scope(ctx.db)?; 17 let module_scope = module.scope(ctx.db)?;
18 module_scope.entries().for_each(|(name, res)| { 18 module_scope.entries().for_each(|(name, res)| {
19 CompletionItem::new(CompletionKind::Reference, name.to_string()) 19 CompletionItem::new(CompletionKind::Reference, name.to_string())
20 .from_resolution(ctx.db, res) 20 .from_resolution(ctx, res)
21 .add_to(acc) 21 .add_to(acc)
22 }); 22 });
23 } 23 }
@@ -113,4 +113,16 @@ mod tests {
113 "Foo;Bar", 113 "Foo;Bar",
114 ); 114 );
115 } 115 }
116
117 #[test]
118 fn dont_render_function_parens_in_use_item() {
119 check_reference_completion(
120 "
121 //- /lib.rs
122 mod m { pub fn foo() {} }
123 use crate::m::f<|>;
124 ",
125 "foo",
126 )
127 }
116} 128}
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index 514fd2f88..daf666505 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -34,7 +34,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
34 }) 34 })
35 .for_each(|(name, res)| { 35 .for_each(|(name, res)| {
36 CompletionItem::new(CompletionKind::Reference, name.to_string()) 36 CompletionItem::new(CompletionKind::Reference, name.to_string())
37 .from_resolution(ctx.db, res) 37 .from_resolution(ctx, res)
38 .add_to(acc) 38 .add_to(acc)
39 }); 39 });
40 Ok(()) 40 Ok(())
@@ -74,7 +74,7 @@ mod tests {
74 let z = (); 74 let z = ();
75 } 75 }
76 ", 76 ",
77 "y;x;quux", 77 r#"y;x;quux "quux($0)""#,
78 ); 78 );
79 } 79 }
80 80
@@ -92,7 +92,7 @@ mod tests {
92 } 92 }
93 } 93 }
94 ", 94 ",
95 "b;a;quux", 95 r#"b;a;quux "quux()$0""#,
96 ); 96 );
97 } 97 }
98 98
@@ -106,7 +106,7 @@ mod tests {
106 } 106 }
107 } 107 }
108 ", 108 ",
109 "x;quux", 109 r#"x;quux "quux()$0""#,
110 ); 110 );
111 } 111 }
112 112
@@ -120,7 +120,7 @@ mod tests {
120 <|> 120 <|>
121 } 121 }
122 ", 122 ",
123 "quux;Foo;Baz", 123 r#"quux "quux()$0";Foo;Baz"#,
124 ); 124 );
125 } 125 }
126 126
@@ -134,7 +134,7 @@ mod tests {
134 fn quux() { <|> } 134 fn quux() { <|> }
135 } 135 }
136 ", 136 ",
137 "quux;Bar", 137 r#"quux "quux()$0";Bar"#,
138 ); 138 );
139 } 139 }
140 140
@@ -145,12 +145,12 @@ mod tests {
145 struct Foo; 145 struct Foo;
146 fn x() -> <|> 146 fn x() -> <|>
147 ", 147 ",
148 "Foo;x", 148 r#"Foo;x "x()$0""#,
149 ) 149 )
150 } 150 }
151 151
152 #[test] 152 #[test]
153 fn dont_show_to_completions_for_shadowing() { 153 fn dont_show_both_completions_for_shadowing() {
154 check_reference_completion( 154 check_reference_completion(
155 r" 155 r"
156 fn foo() -> { 156 fn foo() -> {
@@ -161,7 +161,7 @@ mod tests {
161 } 161 }
162 } 162 }
163 ", 163 ",
164 "bar;foo", 164 r#"bar;foo "foo()$0""#,
165 ) 165 )
166 } 166 }
167 167
@@ -169,4 +169,24 @@ mod tests {
169 fn completes_self_in_methods() { 169 fn completes_self_in_methods() {
170 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") 170 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
171 } 171 }
172
173 #[test]
174 fn inserts_parens_for_function_calls() {
175 check_reference_completion(
176 r"
177 fn no_args() {}
178 fn main() { no_<|> }
179 ",
180 r#"no_args "no_args()$0"
181 main "main()$0""#,
182 );
183 check_reference_completion(
184 r"
185 fn with_args(x: i32, y: String) {}
186 fn main() { with_<|> }
187 ",
188 r#"main "main()$0"
189 with_args "with_args($0)""#,
190 );
191 }
172} 192}
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs
index 71bf7fd32..4685c9328 100644
--- a/crates/ra_analysis/src/completion/completion_context.rs
+++ b/crates/ra_analysis/src/completion/completion_context.rs
@@ -24,13 +24,15 @@ pub(super) struct CompletionContext<'a> {
24 pub(super) module: Option<hir::Module>, 24 pub(super) module: Option<hir::Module>,
25 pub(super) function: Option<hir::Function>, 25 pub(super) function: Option<hir::Function>,
26 pub(super) function_syntax: Option<ast::FnDef<'a>>, 26 pub(super) function_syntax: Option<ast::FnDef<'a>>,
27 pub(super) use_item_syntax: Option<ast::UseItem<'a>>,
27 pub(super) is_param: bool, 28 pub(super) is_param: bool,
28 /// A single-indent path, like `foo`. 29 /// A single-indent path, like `foo`.
29 pub(super) is_trivial_path: bool, 30 pub(super) is_trivial_path: bool,
30 /// If not a trivial, path, the prefix (qualifier). 31 /// If not a trivial, path, the prefix (qualifier).
31 pub(super) path_prefix: Option<hir::Path>, 32 pub(super) path_prefix: Option<hir::Path>,
32 pub(super) after_if: bool, 33 pub(super) after_if: bool,
33 pub(super) is_stmt: bool, 34 /// `true` if we are a statement or a last expr in the block.
35 pub(super) can_be_stmt: bool,
34 /// Something is typed at the "top" level, in module or impl/trait. 36 /// Something is typed at the "top" level, in module or impl/trait.
35 pub(super) is_new_item: bool, 37 pub(super) is_new_item: bool,
36 /// The receiver if this is a field or method access, i.e. writing something.<|> 38 /// The receiver if this is a field or method access, i.e. writing something.<|>
@@ -55,11 +57,12 @@ impl<'a> CompletionContext<'a> {
55 module, 57 module,
56 function: None, 58 function: None,
57 function_syntax: None, 59 function_syntax: None,
60 use_item_syntax: None,
58 is_param: false, 61 is_param: false,
59 is_trivial_path: false, 62 is_trivial_path: false,
60 path_prefix: None, 63 path_prefix: None,
61 after_if: false, 64 after_if: false,
62 is_stmt: false, 65 can_be_stmt: false,
63 is_new_item: false, 66 is_new_item: false,
64 dot_receiver: None, 67 dot_receiver: None,
65 is_method_call: false, 68 is_method_call: false,
@@ -114,6 +117,8 @@ impl<'a> CompletionContext<'a> {
114 _ => (), 117 _ => (),
115 } 118 }
116 119
120 self.use_item_syntax = self.leaf.ancestors().find_map(ast::UseItem::cast);
121
117 self.function_syntax = self 122 self.function_syntax = self
118 .leaf 123 .leaf
119 .ancestors() 124 .ancestors()
@@ -143,13 +148,22 @@ impl<'a> CompletionContext<'a> {
143 if path.qualifier().is_none() { 148 if path.qualifier().is_none() {
144 self.is_trivial_path = true; 149 self.is_trivial_path = true;
145 150
146 self.is_stmt = match name_ref 151 self.can_be_stmt = match name_ref
147 .syntax() 152 .syntax()
148 .ancestors() 153 .ancestors()
149 .filter_map(ast::ExprStmt::cast) 154 .filter_map(ast::ExprStmt::cast)
150 .next() 155 .next()
151 { 156 {
152 None => false, 157 None => {
158 name_ref
159 .syntax()
160 .ancestors()
161 .filter_map(ast::Block::cast)
162 .next()
163 .and_then(|block| block.expr())
164 .map(|e| e.syntax().range())
165 == Some(name_ref.syntax().range())
166 }
153 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(), 167 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
154 }; 168 };
155 169
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs
index 1d294c553..cd4d529f9 100644
--- a/crates/ra_analysis/src/completion/completion_item.rs
+++ b/crates/ra_analysis/src/completion/completion_item.rs
@@ -1,7 +1,7 @@
1use crate::db;
2
3use hir::PerNs; 1use hir::PerNs;
4 2
3use crate::completion::CompletionContext;
4
5/// `CompletionItem` describes a single completion variant in the editor pop-up. 5/// `CompletionItem` describes a single completion variant in the editor pop-up.
6/// It is basically a POD with various properties. To construct a 6/// It is basically a POD with various properties. To construct a
7/// `CompletionItem`, use `new` method and the `Builder` struct. 7/// `CompletionItem`, use `new` method and the `Builder` struct.
@@ -118,12 +118,12 @@ impl Builder {
118 self.kind = Some(kind); 118 self.kind = Some(kind);
119 self 119 self
120 } 120 }
121 pub(crate) fn from_resolution( 121 pub(super) fn from_resolution(
122 mut self, 122 mut self,
123 db: &db::RootDatabase, 123 ctx: &CompletionContext,
124 resolution: &hir::Resolution, 124 resolution: &hir::Resolution,
125 ) -> Builder { 125 ) -> Builder {
126 let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); 126 let resolved = resolution.def_id.and_then(|d| d.resolve(ctx.db).ok());
127 let kind = match resolved { 127 let kind = match resolved {
128 PerNs { 128 PerNs {
129 types: Some(hir::Def::Module(..)), 129 types: Some(hir::Def::Module(..)),
@@ -138,14 +138,29 @@ impl Builder {
138 .. 138 ..
139 } => CompletionItemKind::Enum, 139 } => CompletionItemKind::Enum,
140 PerNs { 140 PerNs {
141 values: Some(hir::Def::Function(..)), 141 values: Some(hir::Def::Function(function)),
142 .. 142 ..
143 } => CompletionItemKind::Function, 143 } => return self.from_function(ctx, function),
144 _ => return self, 144 _ => return self,
145 }; 145 };
146 self.kind = Some(kind); 146 self.kind = Some(kind);
147 self 147 self
148 } 148 }
149
150 fn from_function(mut self, ctx: &CompletionContext, function: hir::Function) -> Builder {
151 // If not an import, add parenthesis automatically.
152 if ctx.use_item_syntax.is_none() {
153 if let Some(sig_info) = function.signature_info(ctx.db) {
154 if sig_info.params.is_empty() {
155 self.snippet = Some(format!("{}()$0", self.label));
156 } else {
157 self.snippet = Some(format!("{}($0)", self.label));
158 }
159 }
160 }
161 self.kind = Some(CompletionItemKind::Function);
162 self
163 }
149} 164}
150 165
151impl Into<CompletionItem> for Builder { 166impl Into<CompletionItem> for Builder {
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index fcb4cd957..bff2e00c9 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -140,6 +140,9 @@ impl fmt::Debug for AnalysisImpl {
140} 140}
141 141
142impl AnalysisImpl { 142impl AnalysisImpl {
143 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
144 self.db.file_text(file_id)
145 }
143 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 146 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
144 self.db.source_file(file_id) 147 self.db.source_file(file_id)
145 } 148 }
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 67b1c1482..9f5e9f358 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -274,6 +274,9 @@ pub struct Analysis {
274} 274}
275 275
276impl Analysis { 276impl Analysis {
277 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
278 self.imp.file_text(file_id)
279 }
277 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 280 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
278 self.imp.file_syntax(file_id).clone() 281 self.imp.file_syntax(file_id).clone()
279 } 282 }
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index b41a7429a..5a8ca3b47 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -36,17 +36,17 @@ pub trait HirDatabase: SyntaxDatabase
36 36
37 fn infer(def_id: DefId) -> Cancelable<Arc<InferenceResult>> { 37 fn infer(def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
38 type InferQuery; 38 type InferQuery;
39 use fn query_definitions::infer; 39 use fn crate::ty::infer;
40 } 40 }
41 41
42 fn type_for_def(def_id: DefId) -> Cancelable<Ty> { 42 fn type_for_def(def_id: DefId) -> Cancelable<Ty> {
43 type TypeForDefQuery; 43 type TypeForDefQuery;
44 use fn query_definitions::type_for_def; 44 use fn crate::ty::type_for_def;
45 } 45 }
46 46
47 fn type_for_field(def_id: DefId, field: Name) -> Cancelable<Ty> { 47 fn type_for_field(def_id: DefId, field: Name) -> Cancelable<Ty> {
48 type TypeForFieldQuery; 48 type TypeForFieldQuery;
49 use fn query_definitions::type_for_field; 49 use fn crate::ty::type_for_field;
50 } 50 }
51 51
52 fn file_items(file_id: FileId) -> Arc<SourceFileItems> { 52 fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index 016d86ee6..721bd4195 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -19,7 +19,6 @@ use crate::{
19 imp::Submodule, 19 imp::Submodule,
20 nameres::{InputModuleItems, ItemMap, Resolver}, 20 nameres::{InputModuleItems, ItemMap, Resolver},
21 }, 21 },
22 ty::{self, InferenceResult, Ty},
23 adt::{StructData, EnumData}, 22 adt::{StructData, EnumData},
24}; 23};
25 24
@@ -30,19 +29,6 @@ pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> {
30 Arc::new(res) 29 Arc::new(res)
31} 30}
32 31
33pub(super) fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
34 let function = Function::new(def_id);
35 ty::infer(db, function).map(Arc::new)
36}
37
38pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
39 ty::type_for_def(db, def_id)
40}
41
42pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable<Ty> {
43 ty::type_for_field(db, def_id, field)
44}
45
46pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { 32pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
47 let def_loc = def_id.loc(db); 33 let def_loc = def_id.loc(db);
48 assert!(def_loc.kind == DefKind::Struct); 34 assert!(def_loc.kind == DefKind::Struct);
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 4ebd44d27..719b3f7cd 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -1,3 +1,18 @@
1//! The type system. We currently use this to infer types for completion.
2//!
3//! For type inference, compare the implementations in rustc (the various
4//! check_* methods in librustc_typeck/check/mod.rs are a good entry point) and
5//! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for
6//! inference here is the `infer` function, which infers the types of all
7//! expressions in a given function.
8//!
9//! The central struct here is `Ty`, which represents a type. During inference,
10//! it can contain type 'variables' which represent currently unknown types; as
11//! we walk through the expressions, we might determine that certain variables
12//! need to be equal to each other, or to certain types. To record this, we use
13//! the union-find implementation from the `ena` crate, which is extracted from
14//! rustc.
15
1mod primitive; 16mod primitive;
2#[cfg(test)] 17#[cfg(test)]
3mod tests; 18mod tests;
@@ -21,6 +36,7 @@ use crate::{
21 type_ref::{TypeRef, Mutability}, 36 type_ref::{TypeRef, Mutability},
22}; 37};
23 38
39/// The ID of a type variable.
24#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 40#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
25pub struct TypeVarId(u32); 41pub struct TypeVarId(u32);
26 42
@@ -40,6 +56,8 @@ impl UnifyKey for TypeVarId {
40 } 56 }
41} 57}
42 58
59/// The value of a type variable: either we already know the type, or we don't
60/// know it yet.
43#[derive(Clone, PartialEq, Eq, Debug)] 61#[derive(Clone, PartialEq, Eq, Debug)]
44pub enum TypeVarValue { 62pub enum TypeVarValue {
45 Known(Ty), 63 Known(Ty),
@@ -47,7 +65,7 @@ pub enum TypeVarValue {
47} 65}
48 66
49impl TypeVarValue { 67impl TypeVarValue {
50 pub fn known(&self) -> Option<&Ty> { 68 fn known(&self) -> Option<&Ty> {
51 match self { 69 match self {
52 TypeVarValue::Known(ty) => Some(ty), 70 TypeVarValue::Known(ty) => Some(ty),
53 TypeVarValue::Unknown => None, 71 TypeVarValue::Unknown => None,
@@ -75,10 +93,13 @@ impl UnifyValue for TypeVarValue {
75 } 93 }
76} 94}
77 95
96/// The kinds of placeholders we need during type inference. Currently, we only
97/// have type variables; in the future, we will probably also need int and float
98/// variables, for inference of literal values (e.g. `100` could be one of
99/// several integer types).
78#[derive(Clone, PartialEq, Eq, Hash, Debug)] 100#[derive(Clone, PartialEq, Eq, Hash, Debug)]
79pub enum InferTy { 101pub enum InferTy {
80 TypeVar(TypeVarId), 102 TypeVar(TypeVarId),
81 // later we'll have IntVar and FloatVar as well
82} 103}
83 104
84/// When inferring an expression, we propagate downward whatever type hint we 105/// When inferring an expression, we propagate downward whatever type hint we
@@ -92,15 +113,21 @@ struct Expectation {
92} 113}
93 114
94impl Expectation { 115impl Expectation {
116 /// The expectation that the type of the expression needs to equal the given
117 /// type.
95 fn has_type(ty: Ty) -> Self { 118 fn has_type(ty: Ty) -> Self {
96 Expectation { ty } 119 Expectation { ty }
97 } 120 }
98 121
122 /// This expresses no expectation on the type.
99 fn none() -> Self { 123 fn none() -> Self {
100 Expectation { ty: Ty::Unknown } 124 Expectation { ty: Ty::Unknown }
101 } 125 }
102} 126}
103 127
128/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
129///
130/// This should be cheap to clone.
104#[derive(Clone, PartialEq, Eq, Hash, Debug)] 131#[derive(Clone, PartialEq, Eq, Hash, Debug)]
105pub enum Ty { 132pub enum Ty {
106 /// The primitive boolean type. Written as `bool`. 133 /// The primitive boolean type. Written as `bool`.
@@ -134,14 +161,14 @@ pub enum Ty {
134 // An array with the given length. Written as `[T; n]`. 161 // An array with the given length. Written as `[T; n]`.
135 // Array(Ty, ty::Const), 162 // Array(Ty, ty::Const),
136 /// The pointee of an array slice. Written as `[T]`. 163 /// The pointee of an array slice. Written as `[T]`.
137 Slice(TyRef), 164 Slice(Arc<Ty>),
138 165
139 /// A raw pointer. Written as `*mut T` or `*const T` 166 /// A raw pointer. Written as `*mut T` or `*const T`
140 RawPtr(TyRef, Mutability), 167 RawPtr(Arc<Ty>, Mutability),
141 168
142 /// A reference; a pointer with an associated lifetime. Written as 169 /// A reference; a pointer with an associated lifetime. Written as
143 /// `&'a mut T` or `&'a T`. 170 /// `&'a mut T` or `&'a T`.
144 Ref(TyRef, Mutability), 171 Ref(Arc<Ty>, Mutability),
145 172
146 /// A pointer to a function. Written as `fn() -> i32`. 173 /// A pointer to a function. Written as `fn() -> i32`.
147 /// 174 ///
@@ -153,6 +180,10 @@ pub enum Ty {
153 /// ``` 180 /// ```
154 FnPtr(Arc<FnSig>), 181 FnPtr(Arc<FnSig>),
155 182
183 // rustc has a separate type for each function, which just coerces to the
184 // above function pointer type. Once we implement generics, we will probably
185 // need this as well.
186
156 // A trait, defined with `dyn trait`. 187 // A trait, defined with `dyn trait`.
157 // Dynamic(), 188 // Dynamic(),
158 // The anonymous type of a closure. Used to represent the type of 189 // The anonymous type of a closure. Used to represent the type of
@@ -166,7 +197,7 @@ pub enum Ty {
166 // A type representin the types stored inside a generator. 197 // A type representin the types stored inside a generator.
167 // This should only appear in GeneratorInteriors. 198 // This should only appear in GeneratorInteriors.
168 // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>), 199 // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>),
169 /// The never type `!` 200 /// The never type `!`.
170 Never, 201 Never,
171 202
172 /// A tuple type. For example, `(i32, bool)`. 203 /// A tuple type. For example, `(i32, bool)`.
@@ -177,10 +208,6 @@ pub enum Ty {
177 // Projection(ProjectionTy), 208 // Projection(ProjectionTy),
178 209
179 // Opaque (`impl Trait`) type found in a return type. 210 // Opaque (`impl Trait`) type found in a return type.
180 // The `DefId` comes either from
181 // * the `impl Trait` ast::Ty node,
182 // * or the `existential type` declaration
183 // The substitutions are for the generics of the function in question.
184 // Opaque(DefId, Substs), 211 // Opaque(DefId, Substs),
185 212
186 // A type parameter; for example, `T` in `fn f<T>(x: T) {} 213 // A type parameter; for example, `T` in `fn f<T>(x: T) {}
@@ -192,12 +219,12 @@ pub enum Ty {
192 /// A placeholder for a type which could not be computed; this is propagated 219 /// A placeholder for a type which could not be computed; this is propagated
193 /// to avoid useless error messages. Doubles as a placeholder where type 220 /// to avoid useless error messages. Doubles as a placeholder where type
194 /// variables are inserted before type checking, since we want to try to 221 /// variables are inserted before type checking, since we want to try to
195 /// infer a better type here anyway. 222 /// infer a better type here anyway -- for the IDE use case, we want to try
223 /// to infer as much as possible even in the presence of type errors.
196 Unknown, 224 Unknown,
197} 225}
198 226
199type TyRef = Arc<Ty>; 227/// A function signature.
200
201#[derive(Clone, PartialEq, Eq, Hash, Debug)] 228#[derive(Clone, PartialEq, Eq, Hash, Debug)]
202pub struct FnSig { 229pub struct FnSig {
203 input: Vec<Ty>, 230 input: Vec<Ty>,
@@ -368,7 +395,11 @@ impl fmt::Display for Ty {
368 } 395 }
369} 396}
370 397
371pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { 398// Functions returning declared types for items
399
400/// Compute the declared type of a function. This should not need to look at the
401/// function body (but currently uses the function AST, so does anyway - TODO).
402fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
372 let syntax = f.syntax(db); 403 let syntax = f.syntax(db);
373 let module = f.module(db)?; 404 let module = f.module(db)?;
374 let node = syntax.borrowed(); 405 let node = syntax.borrowed();
@@ -390,7 +421,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
390 Ok(Ty::FnPtr(Arc::new(sig))) 421 Ok(Ty::FnPtr(Arc::new(sig)))
391} 422}
392 423
393pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { 424fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> {
394 Ok(Ty::Adt { 425 Ok(Ty::Adt {
395 def_id: s.def_id(), 426 def_id: s.def_id(),
396 name: s.name(db)?.unwrap_or_else(Name::missing), 427 name: s.name(db)?.unwrap_or_else(Name::missing),
@@ -404,7 +435,7 @@ pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> {
404 }) 435 })
405} 436}
406 437
407pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { 438pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
408 let def = def_id.resolve(db)?; 439 let def = def_id.resolve(db)?;
409 match def { 440 match def {
410 Def::Module(..) => { 441 Def::Module(..) => {
@@ -444,19 +475,25 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name)
444 Ty::from_hir(db, &module, &type_ref) 475 Ty::from_hir(db, &module, &type_ref)
445} 476}
446 477
478/// The result of type inference: A mapping from expressions and patterns to types.
447#[derive(Clone, PartialEq, Eq, Debug)] 479#[derive(Clone, PartialEq, Eq, Debug)]
448pub struct InferenceResult { 480pub struct InferenceResult {
449 type_of: FxHashMap<LocalSyntaxPtr, Ty>, 481 type_of: FxHashMap<LocalSyntaxPtr, Ty>,
450} 482}
451 483
452impl InferenceResult { 484impl InferenceResult {
485 /// Returns the type of the given syntax node, if it was inferred. Will
486 /// return `None` for syntax nodes not in the inferred function or not
487 /// pointing to an expression/pattern, `Some(Ty::Unknown)` for
488 /// expressions/patterns that could not be inferred.
453 pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> { 489 pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> {
454 self.type_of.get(&LocalSyntaxPtr::new(node)).cloned() 490 self.type_of.get(&LocalSyntaxPtr::new(node)).cloned()
455 } 491 }
456} 492}
457 493
494/// The inference context contains all information needed during type inference.
458#[derive(Clone, Debug)] 495#[derive(Clone, Debug)]
459pub struct InferenceContext<'a, D: HirDatabase> { 496struct InferenceContext<'a, D: HirDatabase> {
460 db: &'a D, 497 db: &'a D,
461 scopes: Arc<FnScopes>, 498 scopes: Arc<FnScopes>,
462 module: Module, 499 module: Module,
@@ -738,6 +775,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
738 ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr(), expected)?, 775 ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr(), expected)?,
739 ast::Expr::Label(_e) => Ty::Unknown, 776 ast::Expr::Label(_e) => Ty::Unknown,
740 ast::Expr::ReturnExpr(e) => { 777 ast::Expr::ReturnExpr(e) => {
778 // TODO expect return type of function
741 self.infer_expr_opt(e.expr(), &Expectation::none())?; 779 self.infer_expr_opt(e.expr(), &Expectation::none())?;
742 Ty::Never 780 Ty::Never
743 } 781 }
@@ -870,7 +908,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
870 } 908 }
871} 909}
872 910
873pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> { 911pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
912 let function = Function::new(def_id); // TODO: consts also need inference
874 let scopes = function.scopes(db); 913 let scopes = function.scopes(db);
875 let module = function.module(db)?; 914 let module = function.module(db)?;
876 let mut ctx = InferenceContext::new(db, scopes, module); 915 let mut ctx = InferenceContext::new(db, scopes, module);
@@ -909,5 +948,5 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
909 ctx.infer_block(block, &Expectation::has_type(ret_ty))?; 948 ctx.infer_block(block, &Expectation::has_type(ret_ty))?;
910 } 949 }
911 950
912 Ok(ctx.resolve_all()) 951 Ok(Arc::new(ctx.resolve_all()))
913} 952}
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index c53106a62..646df2497 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -37,3 +37,4 @@ ra_vfs = { path = "../ra_vfs" }
37[dev-dependencies] 37[dev-dependencies]
38tempdir = "0.3.7" 38tempdir = "0.3.7"
39test_utils = { path = "../test_utils" } 39test_utils = { path = "../test_utils" }
40tools = { path = "../tools" }
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index 5f7038f63..8d508a3ba 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -33,7 +33,7 @@ pub fn server_capabilities() -> ServerCapabilities {
33 workspace_symbol_provider: Some(true), 33 workspace_symbol_provider: Some(true),
34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)), 34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
35 code_lens_provider: None, 35 code_lens_provider: None,
36 document_formatting_provider: None, 36 document_formatting_provider: Some(true),
37 document_range_formatting_provider: None, 37 document_range_formatting_provider: None,
38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
39 first_trigger_character: "=".to_string(), 39 first_trigger_character: "=".to_string(),
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 1edb9fae4..97c1be778 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -295,6 +295,7 @@ fn on_request(
295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)? 295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
296 .on::<req::Rename>(handlers::handle_rename)? 296 .on::<req::Rename>(handlers::handle_rename)?
297 .on::<req::References>(handlers::handle_references)? 297 .on::<req::References>(handlers::handle_references)?
298 .on::<req::Formatting>(handlers::handle_formatting)?
298 .finish(); 299 .finish();
299 match req { 300 match req {
300 Ok(id) => { 301 Ok(id) => {
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index d6f3dbe28..a2c12a4c1 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -6,13 +6,16 @@ use languageserver_types::{
6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, 6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, 7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, 8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
9 Range,
9 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, 10 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
11 DocumentFormattingParams,
10}; 12};
11use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; 13use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
12use ra_syntax::{TextUnit, text_utils::intersect}; 14use ra_syntax::{TextUnit, text_utils::intersect};
13use ra_text_edit::text_utils::contains_offset_nonstrict; 15use ra_text_edit::text_utils::contains_offset_nonstrict;
14use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
15use serde_json::to_value; 17use serde_json::to_value;
18use std::io::Write;
16 19
17use crate::{ 20use crate::{
18 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith}, 21 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith},
@@ -601,6 +604,40 @@ pub fn handle_references(
601 )) 604 ))
602} 605}
603 606
607pub fn handle_formatting(
608 world: ServerWorld,
609 params: DocumentFormattingParams,
610) -> Result<Option<Vec<TextEdit>>> {
611 let file_id = params.text_document.try_conv_with(&world)?;
612 let file = world.analysis().file_text(file_id);
613
614 let file_line_index = world.analysis().file_line_index(file_id);
615 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
616
617 use std::process;
618 let mut rustfmt = process::Command::new("rustfmt")
619 .stdin(process::Stdio::piped())
620 .stdout(process::Stdio::piped())
621 .spawn()?;
622
623 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
624
625 let output = rustfmt.wait_with_output()?;
626 let captured_stdout = String::from_utf8(output.stdout)?;
627 if !output.status.success() {
628 failure::bail!(
629 "rustfmt exited with error code {}: {}.",
630 output.status,
631 captured_stdout,
632 );
633 }
634
635 Ok(Some(vec![TextEdit {
636 range: Range::new(Position::new(0, 0), end_position),
637 new_text: captured_stdout,
638 }]))
639}
640
604pub fn handle_code_action( 641pub fn handle_code_action(
605 world: ServerWorld, 642 world: ServerWorld,
606 params: req::CodeActionParams, 643 params: req::CodeActionParams,
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs
index 1f5cc5e8b..b0e1e65b6 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/main.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs
@@ -1,8 +1,8 @@
1mod support; 1mod support;
2 2
3use serde_json::json; 3use serde_json::json;
4use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams}; 4use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting};
5use languageserver_types::{Position, Range, CodeActionContext}; 5use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions};
6 6
7use crate::support::project; 7use crate::support::project;
8 8
@@ -118,6 +118,60 @@ fn test_eggs() {}
118 ); 118 );
119} 119}
120 120
121use std::collections::HashMap;
122#[test]
123fn test_format_document() {
124 tools::install_rustfmt().unwrap();
125
126 let server = project(
127 r#"
128[package]
129name = "foo"
130version = "0.0.0"
131
132//- src/lib.rs
133mod bar;
134
135fn main() {
136}
137
138pub use std::collections::HashMap;
139"#,
140 );
141 server.wait_for_feedback("workspace loaded");
142
143 server.request::<Formatting>(
144 DocumentFormattingParams {
145 text_document: server.doc_id("src/lib.rs"),
146 options: FormattingOptions {
147 tab_size: 4,
148 insert_spaces: false,
149 properties: HashMap::new(),
150 },
151 },
152 json!([
153 {
154 "newText": r#"mod bar;
155
156fn main() {}
157
158pub use std::collections::HashMap;
159"#,
160 "range": {
161 "end": {
162 "character": 0,
163 "line": 6
164 },
165 "start": {
166 "character": 0,
167 "line": 0
168 }
169 }
170 }
171 ]),
172 );
173}
174
121#[test] 175#[test]
122fn test_missing_module_code_action() { 176fn test_missing_module_code_action() {
123 let server = project( 177 let server = project(
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index be400bae9..4cfdb83da 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -8,7 +8,7 @@ use walkdir::{DirEntry, WalkDir};
8use thread_worker::{WorkerHandle}; 8use thread_worker::{WorkerHandle};
9use relative_path::RelativePathBuf; 9use relative_path::RelativePathBuf;
10 10
11use crate::VfsRoot; 11use crate::{VfsRoot, has_rs_extension};
12 12
13pub(crate) struct Task { 13pub(crate) struct Task {
14 pub(crate) root: VfsRoot, 14 pub(crate) root: VfsRoot,
@@ -59,7 +59,7 @@ fn load_root(root: &Path, filter: &dyn Fn(&DirEntry) -> bool) -> Vec<(RelativePa
59 continue; 59 continue;
60 } 60 }
61 let path = entry.path(); 61 let path = entry.path();
62 if path.extension().and_then(|os| os.to_str()) != Some("rs") { 62 if !has_rs_extension(path) {
63 continue; 63 continue;
64 } 64 }
65 let text = match fs::read_to_string(path) { 65 let text = match fs::read_to_string(path) {
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 4de07b093..90d5e21f4 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -2,11 +2,13 @@
2//! 2//!
3//! When doing analysis, we don't want to do any IO, we want to keep all source 3//! When doing analysis, we don't want to do any IO, we want to keep all source
4//! code in memory. However, the actual source code is stored on disk, so you 4//! code in memory. However, the actual source code is stored on disk, so you
5//! component which does this.
6//! need to get it into the memory in the first place somehow. VFS is the 5//! need to get it into the memory in the first place somehow. VFS is the
6//! component which does this.
7//! 7//!
8//! It also is responsible for watching the disk for changes, and for merging 8//! It is also responsible for watching the disk for changes, and for merging
9//! editor state (modified, unsaved files) with disk state. 9//! editor state (modified, unsaved files) with disk state.
10//! TODO: Some LSP clients support watching the disk, so this crate should
11//! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131)
10//! 12//!
11//! VFS is based on a concept of roots: a set of directories on the file system 13//! VFS is based on a concept of roots: a set of directories on the file system
12//! whihc are watched for changes. Typically, there will be a root for each 14//! whihc are watched for changes. Typically, there will be a root for each
@@ -29,7 +31,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
29use relative_path::RelativePathBuf; 31use relative_path::RelativePathBuf;
30use crossbeam_channel::Receiver; 32use crossbeam_channel::Receiver;
31use walkdir::DirEntry; 33use walkdir::DirEntry;
32use thread_worker::{WorkerHandle}; 34use thread_worker::WorkerHandle;
33 35
34use crate::{ 36use crate::{
35 arena::{ArenaId, Arena}, 37 arena::{ArenaId, Arena},
@@ -57,12 +59,8 @@ impl RootFilter {
57 if !(self.file_filter)(path) { 59 if !(self.file_filter)(path) {
58 return None; 60 return None;
59 } 61 }
60 if !(path.starts_with(&self.root)) { 62 let path = path.strip_prefix(&self.root).ok()?;
61 return None; 63 RelativePathBuf::from_path(path).ok()
62 }
63 let path = path.strip_prefix(&self.root).unwrap();
64 let path = RelativePathBuf::from_path(path).unwrap();
65 Some(path)
66 } 64 }
67} 65}
68 66
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
index 6f96b8120..e5b32c25c 100644
--- a/crates/tools/src/lib.rs
+++ b/crates/tools/src/lib.rs
@@ -117,7 +117,7 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
117 Ok(()) 117 Ok(())
118} 118}
119 119
120fn install_rustfmt() -> Result<()> { 120pub fn install_rustfmt() -> Result<()> {
121 run(&format!("rustup install {}", TOOLCHAIN), ".")?; 121 run(&format!("rustup install {}", TOOLCHAIN), ".")?;
122 run( 122 run(
123 &format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), 123 &format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN),
diff --git a/editors/README.md b/editors/README.md
index ad2ce1695..a63ced725 100644
--- a/editors/README.md
+++ b/editors/README.md
@@ -45,6 +45,8 @@ It's better to remove existing Rust plugins to avoid interference.
45 `#[test]`, this action runs this specific test. If the cursor is 45 `#[test]`, this action runs this specific test. If the cursor is
46 outside of the test function, this re-runs the last test. Do bind 46 outside of the test function, this re-runs the last test. Do bind
47 this to a shortcut! 47 this to a shortcut!
48 - **Format document**. Formats the current file with rustfmt.
49 Rustfmt must be installed separately with `rustup component add rustfmt`.
48 50
49* Typing assists 51* Typing assists
50 - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression. 52 - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression.