diff options
-rw-r--r-- | crates/ra_assists/src/assists/merge_match_arms.rs | 127 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/replace_if_let_with_match.rs | 49 | ||||
-rw-r--r-- | crates/ra_db/src/fixture.rs | 6 | ||||
-rw-r--r-- | crates/ra_db/src/input.rs | 52 | ||||
-rw-r--r-- | crates/ra_db/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_fmt/src/lib.rs | 10 | ||||
-rw-r--r-- | crates/ra_ide/src/mock_analysis.rs | 6 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 43 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/expr_extensions.rs | 15 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 13 | ||||
-rw-r--r-- | docs/user/README.md | 2 |
11 files changed, 231 insertions, 96 deletions
diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/assists/merge_match_arms.rs index aca391155..64c9379da 100644 --- a/crates/ra_assists/src/assists/merge_match_arms.rs +++ b/crates/ra_assists/src/assists/merge_match_arms.rs | |||
@@ -1,6 +1,12 @@ | |||
1 | use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit}; | 1 | use std::iter::successors; |
2 | |||
2 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
3 | use ra_syntax::ast::{AstNode, MatchArm}; | 4 | use ra_syntax::{ |
5 | ast::{self, AstNode}, | ||
6 | Direction, TextUnit, | ||
7 | }; | ||
8 | |||
9 | use crate::{Assist, AssistCtx, AssistId, TextRange}; | ||
4 | 10 | ||
5 | // Assist: merge_match_arms | 11 | // Assist: merge_match_arms |
6 | // | 12 | // |
@@ -27,62 +33,80 @@ use ra_syntax::ast::{AstNode, MatchArm}; | |||
27 | // } | 33 | // } |
28 | // ``` | 34 | // ``` |
29 | pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 35 | pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
30 | let current_arm = ctx.find_node_at_offset::<MatchArm>()?; | 36 | let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?; |
31 | |||
32 | // We check if the following match arm matches this one. We could, but don't, | ||
33 | // compare to the previous match arm as well. | ||
34 | let next = current_arm.syntax().next_sibling(); | ||
35 | let next_arm = MatchArm::cast(next?)?; | ||
36 | |||
37 | // Don't try to handle arms with guards for now - can add support for this later | 37 | // Don't try to handle arms with guards for now - can add support for this later |
38 | if current_arm.guard().is_some() || next_arm.guard().is_some() { | 38 | if current_arm.guard().is_some() { |
39 | return None; | 39 | return None; |
40 | } | 40 | } |
41 | |||
42 | let current_expr = current_arm.expr()?; | 41 | let current_expr = current_arm.expr()?; |
43 | let next_expr = next_arm.expr()?; | 42 | let current_text_range = current_arm.syntax().text_range(); |
44 | 43 | ||
45 | // Check for match arm equality by comparing lengths and then string contents | 44 | enum CursorPos { |
46 | if current_expr.syntax().text_range().len() != next_expr.syntax().text_range().len() { | 45 | InExpr(TextUnit), |
47 | return None; | 46 | InPat(TextUnit), |
48 | } | 47 | } |
49 | if current_expr.syntax().text() != next_expr.syntax().text() { | 48 | let cursor_pos = ctx.frange.range.start(); |
49 | let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) { | ||
50 | CursorPos::InExpr(current_text_range.end() - cursor_pos) | ||
51 | } else { | ||
52 | CursorPos::InPat(cursor_pos) | ||
53 | }; | ||
54 | |||
55 | // We check if the following match arms match this one. We could, but don't, | ||
56 | // compare to the previous match arm as well. | ||
57 | let arms_to_merge = successors(Some(current_arm), next_arm) | ||
58 | .take_while(|arm| { | ||
59 | if arm.guard().is_some() { | ||
60 | return false; | ||
61 | } | ||
62 | match arm.expr() { | ||
63 | Some(expr) => expr.syntax().text() == current_expr.syntax().text(), | ||
64 | None => false, | ||
65 | } | ||
66 | }) | ||
67 | .collect::<Vec<_>>(); | ||
68 | |||
69 | if arms_to_merge.len() <= 1 { | ||
50 | return None; | 70 | return None; |
51 | } | 71 | } |
52 | 72 | ||
53 | let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start(); | ||
54 | |||
55 | ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", |edit| { | 73 | ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", |edit| { |
56 | fn contains_placeholder(a: &MatchArm) -> bool { | 74 | let pats = if arms_to_merge.iter().any(contains_placeholder) { |
57 | a.pats().any(|x| match x { | ||
58 | ra_syntax::ast::Pat::PlaceholderPat(..) => true, | ||
59 | _ => false, | ||
60 | }) | ||
61 | } | ||
62 | |||
63 | let pats = if contains_placeholder(¤t_arm) || contains_placeholder(&next_arm) { | ||
64 | "_".into() | 75 | "_".into() |
65 | } else { | 76 | } else { |
66 | let ps: Vec<String> = current_arm | 77 | arms_to_merge |
67 | .pats() | 78 | .iter() |
79 | .flat_map(ast::MatchArm::pats) | ||
68 | .map(|x| x.syntax().to_string()) | 80 | .map(|x| x.syntax().to_string()) |
69 | .chain(next_arm.pats().map(|x| x.syntax().to_string())) | 81 | .collect::<Vec<String>>() |
70 | .collect(); | 82 | .join(" | ") |
71 | ps.join(" | ") | ||
72 | }; | 83 | }; |
73 | 84 | ||
74 | let arm = format!("{} => {}", pats, current_expr.syntax().text()); | 85 | let arm = format!("{} => {}", pats, current_expr.syntax().text()); |
75 | let offset = TextUnit::from_usize(arm.len()) - cursor_to_end; | ||
76 | 86 | ||
77 | let start = current_arm.syntax().text_range().start(); | 87 | let start = arms_to_merge.first().unwrap().syntax().text_range().start(); |
78 | let end = next_arm.syntax().text_range().end(); | 88 | let end = arms_to_merge.last().unwrap().syntax().text_range().end(); |
79 | 89 | ||
80 | edit.target(current_arm.syntax().text_range()); | 90 | edit.target(current_text_range); |
91 | edit.set_cursor(match cursor_pos { | ||
92 | CursorPos::InExpr(back_offset) => start + TextUnit::from_usize(arm.len()) - back_offset, | ||
93 | CursorPos::InPat(offset) => offset, | ||
94 | }); | ||
81 | edit.replace(TextRange::from_to(start, end), arm); | 95 | edit.replace(TextRange::from_to(start, end), arm); |
82 | edit.set_cursor(start + offset); | ||
83 | }) | 96 | }) |
84 | } | 97 | } |
85 | 98 | ||
99 | fn contains_placeholder(a: &ast::MatchArm) -> bool { | ||
100 | a.pats().any(|x| match x { | ||
101 | ra_syntax::ast::Pat::PlaceholderPat(..) => true, | ||
102 | _ => false, | ||
103 | }) | ||
104 | } | ||
105 | |||
106 | fn next_arm(arm: &ast::MatchArm) -> Option<ast::MatchArm> { | ||
107 | arm.syntax().siblings(Direction::Next).skip(1).find_map(ast::MatchArm::cast) | ||
108 | } | ||
109 | |||
86 | #[cfg(test)] | 110 | #[cfg(test)] |
87 | mod tests { | 111 | mod tests { |
88 | use super::merge_match_arms; | 112 | use super::merge_match_arms; |
@@ -185,6 +209,37 @@ mod tests { | |||
185 | } | 209 | } |
186 | 210 | ||
187 | #[test] | 211 | #[test] |
212 | fn merges_all_subsequent_arms() { | ||
213 | check_assist( | ||
214 | merge_match_arms, | ||
215 | r#" | ||
216 | enum X { A, B, C, D, E } | ||
217 | |||
218 | fn main() { | ||
219 | match X::A { | ||
220 | X::A<|> => 92, | ||
221 | X::B => 92, | ||
222 | X::C => 92, | ||
223 | X::D => 62, | ||
224 | _ => panic!(), | ||
225 | } | ||
226 | } | ||
227 | "#, | ||
228 | r#" | ||
229 | enum X { A, B, C, D, E } | ||
230 | |||
231 | fn main() { | ||
232 | match X::A { | ||
233 | X::A<|> | X::B | X::C => 92, | ||
234 | X::D => 62, | ||
235 | _ => panic!(), | ||
236 | } | ||
237 | } | ||
238 | "#, | ||
239 | ) | ||
240 | } | ||
241 | |||
242 | #[test] | ||
188 | fn merge_match_arms_rejects_guards() { | 243 | fn merge_match_arms_rejects_guards() { |
189 | check_assist_not_applicable( | 244 | check_assist_not_applicable( |
190 | merge_match_arms, | 245 | merge_match_arms, |
diff --git a/crates/ra_assists/src/assists/replace_if_let_with_match.rs b/crates/ra_assists/src/assists/replace_if_let_with_match.rs index c9b62e5ff..c8b13b7b3 100644 --- a/crates/ra_assists/src/assists/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/assists/replace_if_let_with_match.rs | |||
@@ -1,9 +1,12 @@ | |||
1 | use format_buf::format; | ||
2 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
3 | use ra_fmt::extract_trivial_expression; | 2 | use ra_fmt::unwrap_trivial_block; |
4 | use ra_syntax::{ast, AstNode}; | 3 | use ra_syntax::{ |
4 | ast::{self, make}, | ||
5 | AstNode, | ||
6 | }; | ||
5 | 7 | ||
6 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
9 | use ast::edit::IndentLevel; | ||
7 | 10 | ||
8 | // Assist: replace_if_let_with_match | 11 | // Assist: replace_if_let_with_match |
9 | // | 12 | // |
@@ -43,32 +46,24 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Opt | |||
43 | }; | 46 | }; |
44 | 47 | ||
45 | ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| { | 48 | ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| { |
46 | let match_expr = build_match_expr(expr, pat, then_block, else_block); | 49 | let match_expr = { |
47 | edit.target(if_expr.syntax().text_range()); | 50 | let then_arm = { |
48 | edit.replace_node_and_indent(if_expr.syntax(), match_expr); | 51 | let then_expr = unwrap_trivial_block(then_block); |
49 | edit.set_cursor(if_expr.syntax().text_range().start()) | 52 | make::match_arm(vec![pat], then_expr) |
50 | }) | 53 | }; |
51 | } | 54 | let else_arm = { |
55 | let else_expr = unwrap_trivial_block(else_block); | ||
56 | make::match_arm(vec![make::placeholder_pat().into()], else_expr) | ||
57 | }; | ||
58 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | ||
59 | }; | ||
52 | 60 | ||
53 | fn build_match_expr( | 61 | let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); |
54 | expr: ast::Expr, | ||
55 | pat1: ast::Pat, | ||
56 | arm1: ast::BlockExpr, | ||
57 | arm2: ast::BlockExpr, | ||
58 | ) -> String { | ||
59 | let mut buf = String::new(); | ||
60 | format!(buf, "match {} {{\n", expr.syntax().text()); | ||
61 | format!(buf, " {} => {}\n", pat1.syntax().text(), format_arm(&arm1)); | ||
62 | format!(buf, " _ => {}\n", format_arm(&arm2)); | ||
63 | buf.push_str("}"); | ||
64 | buf | ||
65 | } | ||
66 | 62 | ||
67 | fn format_arm(block: &ast::BlockExpr) -> String { | 63 | edit.target(if_expr.syntax().text_range()); |
68 | match extract_trivial_expression(block) { | 64 | edit.set_cursor(if_expr.syntax().text_range().start()); |
69 | Some(e) if !e.syntax().text().contains_char('\n') => format!("{},", e.syntax().text()), | 65 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr.into()); |
70 | _ => block.syntax().text().to_string(), | 66 | }) |
71 | } | ||
72 | } | 67 | } |
73 | 68 | ||
74 | #[cfg(test)] | 69 | #[cfg(test)] |
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 30b598e9a..17cd138c2 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -8,8 +8,8 @@ use rustc_hash::FxHashMap; | |||
8 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | 8 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, SourceDatabaseExt, | 11 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, |
12 | SourceRoot, SourceRootId, | 12 | SourceDatabaseExt, SourceRoot, SourceRootId, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 15 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
@@ -139,7 +139,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
139 | for (from, to) in crate_deps { | 139 | for (from, to) in crate_deps { |
140 | let from_id = crates[&from]; | 140 | let from_id = crates[&from]; |
141 | let to_id = crates[&to]; | 141 | let to_id = crates[&to]; |
142 | crate_graph.add_dep(from_id, to.into(), to_id).unwrap(); | 142 | crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); |
143 | } | 143 | } |
144 | } | 144 | } |
145 | 145 | ||
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 07269237a..1f1dcea42 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -83,6 +83,26 @@ pub struct CrateGraph { | |||
83 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 83 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
84 | pub struct CrateId(pub u32); | 84 | pub struct CrateId(pub u32); |
85 | 85 | ||
86 | pub struct CrateName(SmolStr); | ||
87 | |||
88 | impl CrateName { | ||
89 | /// Crates a crate name, checking for dashes in the string provided. | ||
90 | /// Dashes are not allowed in the crate names, | ||
91 | /// hence the input string is returned as `Err` for those cases. | ||
92 | pub fn new(name: &str) -> Result<CrateName, &str> { | ||
93 | if name.contains('-') { | ||
94 | Err(name) | ||
95 | } else { | ||
96 | Ok(Self(SmolStr::new(name))) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /// Crates a crate name, unconditionally replacing the dashes with underscores. | ||
101 | pub fn normalize_dashes(name: &str) -> CrateName { | ||
102 | Self(SmolStr::new(name.replace('-', "_"))) | ||
103 | } | ||
104 | } | ||
105 | |||
86 | #[derive(Debug, Clone, PartialEq, Eq)] | 106 | #[derive(Debug, Clone, PartialEq, Eq)] |
87 | struct CrateData { | 107 | struct CrateData { |
88 | file_id: FileId, | 108 | file_id: FileId, |
@@ -131,13 +151,13 @@ impl CrateGraph { | |||
131 | pub fn add_dep( | 151 | pub fn add_dep( |
132 | &mut self, | 152 | &mut self, |
133 | from: CrateId, | 153 | from: CrateId, |
134 | name: SmolStr, | 154 | name: CrateName, |
135 | to: CrateId, | 155 | to: CrateId, |
136 | ) -> Result<(), CyclicDependenciesError> { | 156 | ) -> Result<(), CyclicDependenciesError> { |
137 | if self.dfs_find(from, to, &mut FxHashSet::default()) { | 157 | if self.dfs_find(from, to, &mut FxHashSet::default()) { |
138 | return Err(CyclicDependenciesError); | 158 | return Err(CyclicDependenciesError); |
139 | } | 159 | } |
140 | self.arena.get_mut(&from).unwrap().add_dep(name, to); | 160 | self.arena.get_mut(&from).unwrap().add_dep(name.0, to); |
141 | Ok(()) | 161 | Ok(()) |
142 | } | 162 | } |
143 | 163 | ||
@@ -268,7 +288,7 @@ pub struct CyclicDependenciesError; | |||
268 | 288 | ||
269 | #[cfg(test)] | 289 | #[cfg(test)] |
270 | mod tests { | 290 | mod tests { |
271 | use super::{CfgOptions, CrateGraph, Edition::Edition2018, Env, FileId, SmolStr}; | 291 | use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; |
272 | 292 | ||
273 | #[test] | 293 | #[test] |
274 | fn it_should_panic_because_of_cycle_dependencies() { | 294 | fn it_should_panic_because_of_cycle_dependencies() { |
@@ -279,9 +299,9 @@ mod tests { | |||
279 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 299 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); |
280 | let crate3 = | 300 | let crate3 = |
281 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); | 301 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); |
282 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); | 302 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
283 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); | 303 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
284 | assert!(graph.add_dep(crate3, SmolStr::new("crate1"), crate1).is_err()); | 304 | assert!(graph.add_dep(crate3, CrateName::new("crate1").unwrap(), crate1).is_err()); |
285 | } | 305 | } |
286 | 306 | ||
287 | #[test] | 307 | #[test] |
@@ -293,7 +313,23 @@ mod tests { | |||
293 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 313 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); |
294 | let crate3 = | 314 | let crate3 = |
295 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); | 315 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); |
296 | assert!(graph.add_dep(crate1, SmolStr::new("crate2"), crate2).is_ok()); | 316 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
297 | assert!(graph.add_dep(crate2, SmolStr::new("crate3"), crate3).is_ok()); | 317 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
318 | } | ||
319 | |||
320 | #[test] | ||
321 | fn dashes_are_normalized() { | ||
322 | let mut graph = CrateGraph::default(); | ||
323 | let crate1 = | ||
324 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | ||
325 | let crate2 = | ||
326 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | ||
327 | assert!(graph | ||
328 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) | ||
329 | .is_ok()); | ||
330 | assert_eq!( | ||
331 | graph.dependencies(crate1).collect::<Vec<_>>(), | ||
332 | vec![&Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] | ||
333 | ); | ||
298 | } | 334 | } |
299 | } | 335 | } |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 21341b769..fb002d717 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -10,7 +10,9 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit}; | |||
10 | 10 | ||
11 | pub use crate::{ | 11 | pub use crate::{ |
12 | cancellation::Canceled, | 12 | cancellation::Canceled, |
13 | input::{CrateGraph, CrateId, Dependency, Edition, Env, FileId, SourceRoot, SourceRootId}, | 13 | input::{ |
14 | CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, SourceRoot, SourceRootId, | ||
15 | }, | ||
14 | }; | 16 | }; |
15 | pub use relative_path::{RelativePath, RelativePathBuf}; | 17 | pub use relative_path::{RelativePath, RelativePathBuf}; |
16 | pub use salsa; | 18 | pub use salsa; |
diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs index 10f592257..4bca27b5c 100644 --- a/crates/ra_fmt/src/lib.rs +++ b/crates/ra_fmt/src/lib.rs | |||
@@ -35,8 +35,14 @@ fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | |||
35 | successors(token.prev_token(), |token| token.prev_token()) | 35 | successors(token.prev_token(), |token| token.prev_token()) |
36 | } | 36 | } |
37 | 37 | ||
38 | pub fn extract_trivial_expression(expr: &ast::BlockExpr) -> Option<ast::Expr> { | 38 | pub fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { |
39 | let block = expr.block()?; | 39 | extract_trivial_expression(&block) |
40 | .filter(|expr| !expr.syntax().text().contains_char('\n')) | ||
41 | .unwrap_or_else(|| block.into()) | ||
42 | } | ||
43 | |||
44 | pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | ||
45 | let block = block.block()?; | ||
40 | let expr = block.expr()?; | 46 | let expr = block.expr()?; |
41 | let non_trivial_children = block.syntax().children().filter(|it| match it.kind() { | 47 | let non_trivial_children = block.syntax().children().filter(|it| match it.kind() { |
42 | WHITESPACE | T!['{'] | T!['}'] => false, | 48 | WHITESPACE | T!['{'] | T!['}'] => false, |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index bf8a54932..081aaee8c 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use ra_cfg::CfgOptions; | 5 | use ra_cfg::CfgOptions; |
6 | use ra_db::{Env, RelativePathBuf}; | 6 | use ra_db::{CrateName, Env, RelativePathBuf}; |
7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
@@ -107,7 +107,9 @@ impl MockAnalysis { | |||
107 | crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); | 107 | crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); |
108 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 108 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
109 | if let Some(root_crate) = root_crate { | 109 | if let Some(root_crate) = root_crate { |
110 | crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap(); | 110 | crate_graph |
111 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) | ||
112 | .unwrap(); | ||
111 | } | 113 | } |
112 | } | 114 | } |
113 | change.add_file(source_root, file_id, path, Arc::new(contents)); | 115 | change.add_file(source_root, file_id, path, Arc::new(contents)); |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 6a104e6f2..bc1d15406 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -13,7 +13,7 @@ use std::{ | |||
13 | }; | 13 | }; |
14 | 14 | ||
15 | use ra_cfg::CfgOptions; | 15 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateId, Edition, Env, FileId}; | 16 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
18 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
19 | 19 | ||
@@ -177,7 +177,9 @@ impl ProjectWorkspace { | |||
177 | if let (Some(&from), Some(&to)) = | 177 | if let (Some(&from), Some(&to)) = |
178 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) | 178 | (crates.get(&from_crate_id), crates.get(&to_crate_id)) |
179 | { | 179 | { |
180 | if let Err(_) = crate_graph.add_dep(from, dep.name.clone().into(), to) { | 180 | if let Err(_) = |
181 | crate_graph.add_dep(from, CrateName::new(&dep.name).unwrap(), to) | ||
182 | { | ||
181 | log::error!( | 183 | log::error!( |
182 | "cyclic dependency {:?} -> {:?}", | 184 | "cyclic dependency {:?} -> {:?}", |
183 | from_crate_id, | 185 | from_crate_id, |
@@ -215,7 +217,9 @@ impl ProjectWorkspace { | |||
215 | if let (Some(&from), Some(&to)) = | 217 | if let (Some(&from), Some(&to)) = |
216 | (sysroot_crates.get(&from), sysroot_crates.get(&to)) | 218 | (sysroot_crates.get(&from), sysroot_crates.get(&to)) |
217 | { | 219 | { |
218 | if let Err(_) = crate_graph.add_dep(from, name.into(), to) { | 220 | if let Err(_) = |
221 | crate_graph.add_dep(from, CrateName::new(name).unwrap(), to) | ||
222 | { | ||
219 | log::error!("cyclic dependency between sysroot crates") | 223 | log::error!("cyclic dependency between sysroot crates") |
220 | } | 224 | } |
221 | } | 225 | } |
@@ -257,7 +261,7 @@ impl ProjectWorkspace { | |||
257 | if let Some(proc_macro) = libproc_macro { | 261 | if let Some(proc_macro) = libproc_macro { |
258 | if let Err(_) = crate_graph.add_dep( | 262 | if let Err(_) = crate_graph.add_dep( |
259 | crate_id, | 263 | crate_id, |
260 | "proc_macro".into(), | 264 | CrateName::new("proc_macro").unwrap(), |
261 | proc_macro, | 265 | proc_macro, |
262 | ) { | 266 | ) { |
263 | log::error!( | 267 | log::error!( |
@@ -276,9 +280,14 @@ impl ProjectWorkspace { | |||
276 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 280 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
277 | if let Some(to) = lib_tgt { | 281 | if let Some(to) = lib_tgt { |
278 | if to != from { | 282 | if to != from { |
279 | if let Err(_) = | 283 | if let Err(_) = crate_graph.add_dep( |
280 | crate_graph.add_dep(from, pkg.name(&cargo).into(), to) | 284 | from, |
281 | { | 285 | // For root projects with dashes in their name, |
286 | // cargo metadata does not do any normalization, | ||
287 | // so we do it ourselves currently | ||
288 | CrateName::normalize_dashes(pkg.name(&cargo)), | ||
289 | to, | ||
290 | ) { | ||
282 | log::error!( | 291 | log::error!( |
283 | "cyclic dependency between targets of {}", | 292 | "cyclic dependency between targets of {}", |
284 | pkg.name(&cargo) | 293 | pkg.name(&cargo) |
@@ -289,17 +298,23 @@ impl ProjectWorkspace { | |||
289 | // core is added as a dependency before std in order to | 298 | // core is added as a dependency before std in order to |
290 | // mimic rustcs dependency order | 299 | // mimic rustcs dependency order |
291 | if let Some(core) = libcore { | 300 | if let Some(core) = libcore { |
292 | if let Err(_) = crate_graph.add_dep(from, "core".into(), core) { | 301 | if let Err(_) = |
302 | crate_graph.add_dep(from, CrateName::new("core").unwrap(), core) | ||
303 | { | ||
293 | log::error!("cyclic dependency on core for {}", pkg.name(&cargo)) | 304 | log::error!("cyclic dependency on core for {}", pkg.name(&cargo)) |
294 | } | 305 | } |
295 | } | 306 | } |
296 | if let Some(alloc) = liballoc { | 307 | if let Some(alloc) = liballoc { |
297 | if let Err(_) = crate_graph.add_dep(from, "alloc".into(), alloc) { | 308 | if let Err(_) = |
309 | crate_graph.add_dep(from, CrateName::new("alloc").unwrap(), alloc) | ||
310 | { | ||
298 | log::error!("cyclic dependency on alloc for {}", pkg.name(&cargo)) | 311 | log::error!("cyclic dependency on alloc for {}", pkg.name(&cargo)) |
299 | } | 312 | } |
300 | } | 313 | } |
301 | if let Some(std) = libstd { | 314 | if let Some(std) = libstd { |
302 | if let Err(_) = crate_graph.add_dep(from, "std".into(), std) { | 315 | if let Err(_) = |
316 | crate_graph.add_dep(from, CrateName::new("std").unwrap(), std) | ||
317 | { | ||
303 | log::error!("cyclic dependency on std for {}", pkg.name(&cargo)) | 318 | log::error!("cyclic dependency on std for {}", pkg.name(&cargo)) |
304 | } | 319 | } |
305 | } | 320 | } |
@@ -312,9 +327,11 @@ impl ProjectWorkspace { | |||
312 | for dep in pkg.dependencies(&cargo) { | 327 | for dep in pkg.dependencies(&cargo) { |
313 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 328 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
314 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 329 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
315 | if let Err(_) = | 330 | if let Err(_) = crate_graph.add_dep( |
316 | crate_graph.add_dep(from, dep.name.clone().into(), to) | 331 | from, |
317 | { | 332 | CrateName::new(&dep.name).unwrap(), |
333 | to, | ||
334 | ) { | ||
318 | log::error!( | 335 | log::error!( |
319 | "cyclic dependency {} -> {}", | 336 | "cyclic dependency {} -> {}", |
320 | pkg.name(&cargo), | 337 | pkg.name(&cargo), |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 539759450..2e50a095c 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -7,6 +7,21 @@ use crate::{ | |||
7 | SyntaxToken, T, | 7 | SyntaxToken, T, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | impl ast::Expr { | ||
11 | pub fn is_block_like(&self) -> bool { | ||
12 | match self { | ||
13 | ast::Expr::IfExpr(_) | ||
14 | | ast::Expr::LoopExpr(_) | ||
15 | | ast::Expr::ForExpr(_) | ||
16 | | ast::Expr::WhileExpr(_) | ||
17 | | ast::Expr::BlockExpr(_) | ||
18 | | ast::Expr::MatchExpr(_) | ||
19 | | ast::Expr::TryBlockExpr(_) => true, | ||
20 | _ => false, | ||
21 | } | ||
22 | } | ||
23 | } | ||
24 | |||
10 | #[derive(Debug, Clone, PartialEq, Eq)] | 25 | #[derive(Debug, Clone, PartialEq, Eq)] |
11 | pub enum ElseBranch { | 26 | pub enum ElseBranch { |
12 | Block(ast::BlockExpr), | 27 | Block(ast::BlockExpr), |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 38c0e9a66..629503dc5 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -122,11 +122,18 @@ pub fn match_arm(pats: impl IntoIterator<Item = ast::Pat>, expr: ast::Expr) -> a | |||
122 | } | 122 | } |
123 | 123 | ||
124 | pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { | 124 | pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { |
125 | let arms_str = arms.into_iter().map(|arm| format!("\n {}", arm.syntax())).join(","); | 125 | let arms_str = arms |
126 | return from_text(&format!("{},\n", arms_str)); | 126 | .into_iter() |
127 | .map(|arm| { | ||
128 | let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like()); | ||
129 | let comma = if needs_comma { "," } else { "" }; | ||
130 | format!(" {}{}\n", arm.syntax(), comma) | ||
131 | }) | ||
132 | .collect::<String>(); | ||
133 | return from_text(&format!("{}", arms_str)); | ||
127 | 134 | ||
128 | fn from_text(text: &str) -> ast::MatchArmList { | 135 | fn from_text(text: &str) -> ast::MatchArmList { |
129 | ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text)) | 136 | ast_from_text(&format!("fn f() {{ match () {{\n{}}} }}", text)) |
130 | } | 137 | } |
131 | } | 138 | } |
132 | 139 | ||
diff --git a/docs/user/README.md b/docs/user/README.md index 2948f6ff3..18867cd11 100644 --- a/docs/user/README.md +++ b/docs/user/README.md | |||
@@ -94,7 +94,7 @@ host. | |||
94 | * `rust-analyzer.highlightingOn`: enables experimental syntax highlighting. | 94 | * `rust-analyzer.highlightingOn`: enables experimental syntax highlighting. |
95 | Colors can be configured via `editor.tokenColorCustomizations`. | 95 | Colors can be configured via `editor.tokenColorCustomizations`. |
96 | As an example, [Pale Fire](https://github.com/matklad/pale-fire/) color scheme tweaks rust colors. | 96 | As an example, [Pale Fire](https://github.com/matklad/pale-fire/) color scheme tweaks rust colors. |
97 | * `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts. | 97 | * `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts the |
98 | `Enter` key to make it easier to continue comments. Note that it may conflict with VIM emulation plugin. | 98 | `Enter` key to make it easier to continue comments. Note that it may conflict with VIM emulation plugin. |
99 | * `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable | 99 | * `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable |
100 | * `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo | 100 | * `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo |