diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/paths/Cargo.toml | 8 | ||||
-rw-r--r-- | crates/paths/src/lib.rs | 123 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/fill_match_arms.rs | 50 |
3 files changed, 179 insertions, 2 deletions
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml new file mode 100644 index 000000000..646ee7fd5 --- /dev/null +++ b/crates/paths/Cargo.toml | |||
@@ -0,0 +1,8 @@ | |||
1 | [package] | ||
2 | name = "paths" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | |||
7 | [lib] | ||
8 | doctest = false | ||
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs new file mode 100644 index 000000000..c7ce0c42f --- /dev/null +++ b/crates/paths/src/lib.rs | |||
@@ -0,0 +1,123 @@ | |||
1 | //! Thin wrappers around `std::path`, distinguishing between absolute and | ||
2 | //! relative paths. | ||
3 | use std::{ | ||
4 | convert::{TryFrom, TryInto}, | ||
5 | ops, | ||
6 | path::{Component, Path, PathBuf}, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
10 | pub struct AbsPathBuf(PathBuf); | ||
11 | |||
12 | impl From<AbsPathBuf> for PathBuf { | ||
13 | fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { | ||
14 | path_buf | ||
15 | } | ||
16 | } | ||
17 | |||
18 | impl ops::Deref for AbsPathBuf { | ||
19 | type Target = AbsPath; | ||
20 | fn deref(&self) -> &AbsPath { | ||
21 | self.as_path() | ||
22 | } | ||
23 | } | ||
24 | |||
25 | impl AsRef<Path> for AbsPathBuf { | ||
26 | fn as_ref(&self) -> &Path { | ||
27 | self.0.as_path() | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl TryFrom<PathBuf> for AbsPathBuf { | ||
32 | type Error = PathBuf; | ||
33 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { | ||
34 | if !path_buf.is_absolute() { | ||
35 | return Err(path_buf); | ||
36 | } | ||
37 | Ok(AbsPathBuf(path_buf)) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl TryFrom<&str> for AbsPathBuf { | ||
42 | type Error = PathBuf; | ||
43 | fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> { | ||
44 | AbsPathBuf::try_from(PathBuf::from(path)) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl AbsPathBuf { | ||
49 | pub fn as_path(&self) -> &AbsPath { | ||
50 | AbsPath::new_unchecked(self.0.as_path()) | ||
51 | } | ||
52 | pub fn pop(&mut self) -> bool { | ||
53 | self.0.pop() | ||
54 | } | ||
55 | } | ||
56 | |||
57 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
58 | #[repr(transparent)] | ||
59 | pub struct AbsPath(Path); | ||
60 | |||
61 | impl ops::Deref for AbsPath { | ||
62 | type Target = Path; | ||
63 | fn deref(&self) -> &Path { | ||
64 | &self.0 | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl AsRef<Path> for AbsPath { | ||
69 | fn as_ref(&self) -> &Path { | ||
70 | &self.0 | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl<'a> TryFrom<&'a Path> for &'a AbsPath { | ||
75 | type Error = &'a Path; | ||
76 | fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { | ||
77 | if !path.is_absolute() { | ||
78 | return Err(path); | ||
79 | } | ||
80 | Ok(AbsPath::new_unchecked(path)) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl AbsPath { | ||
85 | fn new_unchecked(path: &Path) -> &AbsPath { | ||
86 | unsafe { &*(path as *const Path as *const AbsPath) } | ||
87 | } | ||
88 | |||
89 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { | ||
90 | self.as_ref().join(path).try_into().unwrap() | ||
91 | } | ||
92 | pub fn normalize(&self) -> AbsPathBuf { | ||
93 | AbsPathBuf(normalize_path(&self.0)) | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | ||
98 | fn normalize_path(path: &Path) -> PathBuf { | ||
99 | let mut components = path.components().peekable(); | ||
100 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | ||
101 | components.next(); | ||
102 | PathBuf::from(c.as_os_str()) | ||
103 | } else { | ||
104 | PathBuf::new() | ||
105 | }; | ||
106 | |||
107 | for component in components { | ||
108 | match component { | ||
109 | Component::Prefix(..) => unreachable!(), | ||
110 | Component::RootDir => { | ||
111 | ret.push(component.as_os_str()); | ||
112 | } | ||
113 | Component::CurDir => {} | ||
114 | Component::ParentDir => { | ||
115 | ret.pop(); | ||
116 | } | ||
117 | Component::Normal(c) => { | ||
118 | ret.push(c); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | ret | ||
123 | } | ||
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index cc303285b..569efb768 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -136,8 +136,20 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | |||
136 | } | 136 | } |
137 | 137 | ||
138 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { | 138 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { |
139 | let pat_head = pat.syntax().first_child().map(|node| node.text()); | 139 | let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text()); |
140 | let var_head = var.syntax().first_child().map(|node| node.text()); | 140 | |
141 | let pat_head = match pat { | ||
142 | Pat::BindPat(bind_pat) => { | ||
143 | if let Some(p) = bind_pat.pat() { | ||
144 | first_node_text(&p) | ||
145 | } else { | ||
146 | return false; | ||
147 | } | ||
148 | } | ||
149 | pat => first_node_text(pat), | ||
150 | }; | ||
151 | |||
152 | let var_head = first_node_text(var); | ||
141 | 153 | ||
142 | pat_head == var_head | 154 | pat_head == var_head |
143 | } | 155 | } |
@@ -351,6 +363,40 @@ mod tests { | |||
351 | } | 363 | } |
352 | 364 | ||
353 | #[test] | 365 | #[test] |
366 | fn partial_fill_bind_pat() { | ||
367 | check_assist( | ||
368 | fill_match_arms, | ||
369 | r#" | ||
370 | enum A { | ||
371 | As, | ||
372 | Bs, | ||
373 | Cs(Option<i32>), | ||
374 | } | ||
375 | fn main() { | ||
376 | match A::As<|> { | ||
377 | A::As(_) => {} | ||
378 | a @ A::Bs(_) => {} | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | r#" | ||
383 | enum A { | ||
384 | As, | ||
385 | Bs, | ||
386 | Cs(Option<i32>), | ||
387 | } | ||
388 | fn main() { | ||
389 | match A::As { | ||
390 | A::As(_) => {} | ||
391 | a @ A::Bs(_) => {} | ||
392 | $0A::Cs(_) => {} | ||
393 | } | ||
394 | } | ||
395 | "#, | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
354 | fn fill_match_arms_empty_body() { | 400 | fn fill_match_arms_empty_body() { |
355 | check_assist( | 401 | check_assist( |
356 | fill_match_arms, | 402 | fill_match_arms, |