diff options
62 files changed, 1654 insertions, 472 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 633015956..ee74d7486 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml | |||
@@ -13,7 +13,7 @@ jobs: | |||
13 | runs-on: ubuntu-latest | 13 | runs-on: ubuntu-latest |
14 | steps: | 14 | steps: |
15 | - name: Checkout repository | 15 | - name: Checkout repository |
16 | uses: actions/checkout@v1 | 16 | uses: actions/checkout@v2 |
17 | 17 | ||
18 | - run: cargo install cargo-audit | 18 | - run: cargo install cargo-audit |
19 | - run: cargo audit | 19 | - run: cargo audit |
@@ -37,7 +37,7 @@ jobs: | |||
37 | 37 | ||
38 | steps: | 38 | steps: |
39 | - name: Checkout repository | 39 | - name: Checkout repository |
40 | uses: actions/checkout@v1 | 40 | uses: actions/checkout@v2 |
41 | 41 | ||
42 | # We need to disable the existing toolchain to avoid updating rust-docs | 42 | # We need to disable the existing toolchain to avoid updating rust-docs |
43 | # which takes a long time. The fastest way to do this is to rename the | 43 | # which takes a long time. The fastest way to do this is to rename the |
@@ -94,7 +94,7 @@ jobs: | |||
94 | CC: gcc-4.9 | 94 | CC: gcc-4.9 |
95 | steps: | 95 | steps: |
96 | - name: Checkout repository | 96 | - name: Checkout repository |
97 | uses: actions/checkout@v1 | 97 | uses: actions/checkout@v2 |
98 | 98 | ||
99 | - name: Install Nodejs | 99 | - name: Install Nodejs |
100 | uses: actions/setup-node@v1 | 100 | uses: actions/setup-node@v1 |
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d58be0fd7..0434b6128 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml | |||
@@ -23,7 +23,7 @@ jobs: | |||
23 | 23 | ||
24 | steps: | 24 | steps: |
25 | - name: Checkout repository | 25 | - name: Checkout repository |
26 | uses: actions/checkout@v1 | 26 | uses: actions/checkout@v2 |
27 | 27 | ||
28 | # We need to disable the existing toolchain to avoid updating rust-docs | 28 | # We need to disable the existing toolchain to avoid updating rust-docs |
29 | # which takes a long time. The fastest way to do this is to rename the | 29 | # which takes a long time. The fastest way to do this is to rename the |
@@ -42,6 +42,7 @@ jobs: | |||
42 | override: true | 42 | override: true |
43 | 43 | ||
44 | - name: Install Nodejs | 44 | - name: Install Nodejs |
45 | if: matrix.os == 'ubuntu-latest' | ||
45 | uses: actions/setup-node@v1 | 46 | uses: actions/setup-node@v1 |
46 | with: | 47 | with: |
47 | node-version: 12.x | 48 | node-version: 12.x |
@@ -70,14 +71,14 @@ jobs: | |||
70 | with: | 71 | with: |
71 | node-version: 12.x | 72 | node-version: 12.x |
72 | 73 | ||
73 | - run: echo "::set-env name=TAG::$(date --iso)" | 74 | - run: echo "::set-env name=TAG::$(date --iso --utc)" |
74 | if: github.event_name == 'push' | 75 | if: github.event_name == 'push' |
75 | - run: echo "::set-env name=TAG::nightly" | 76 | - run: echo "::set-env name=TAG::nightly" |
76 | if: github.event_name == 'schedule' | 77 | if: github.event_name == 'schedule' |
77 | - run: 'echo "TAG: $TAG"' | 78 | - run: 'echo "TAG: $TAG"' |
78 | 79 | ||
79 | - name: Checkout repository | 80 | - name: Checkout repository |
80 | uses: actions/checkout@v1 | 81 | uses: actions/checkout@v2 |
81 | 82 | ||
82 | - uses: actions/download-artifact@v1 | 83 | - uses: actions/download-artifact@v1 |
83 | with: | 84 | with: |
@@ -107,4 +108,4 @@ jobs: | |||
107 | if: github.event_name == 'push' | 108 | if: github.event_name == 'push' |
108 | working-directory: ./editors/code | 109 | working-directory: ./editors/code |
109 | # token from https://dev.azure.com/rust-analyzer/ | 110 | # token from https://dev.azure.com/rust-analyzer/ |
110 | run: npx vsce publish 0.1.$(date +%Y%m%d) --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer.vsix | 111 | run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer.vsix |
diff --git a/.github/workflows/rustdoc.yaml b/.github/workflows/rustdoc.yaml index e75e92695..c84ce5d48 100644 --- a/.github/workflows/rustdoc.yaml +++ b/.github/workflows/rustdoc.yaml | |||
@@ -13,7 +13,7 @@ jobs: | |||
13 | 13 | ||
14 | steps: | 14 | steps: |
15 | - name: Checkout repository | 15 | - name: Checkout repository |
16 | uses: actions/checkout@v1 | 16 | uses: actions/checkout@v2 |
17 | 17 | ||
18 | - name: Install Rust toolchain | 18 | - name: Install Rust toolchain |
19 | uses: actions-rs/toolchain@v1 | 19 | uses: actions-rs/toolchain@v1 |
diff --git a/Cargo.lock b/Cargo.lock index 80e778bcf..330bdd1cb 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -2,9 +2,9 @@ | |||
2 | # It is not intended for manual editing. | 2 | # It is not intended for manual editing. |
3 | [[package]] | 3 | [[package]] |
4 | name = "aho-corasick" | 4 | name = "aho-corasick" |
5 | version = "0.7.9" | 5 | version = "0.7.10" |
6 | source = "registry+https://github.com/rust-lang/crates.io-index" | 6 | source = "registry+https://github.com/rust-lang/crates.io-index" |
7 | checksum = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec" | 7 | checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" |
8 | dependencies = [ | 8 | dependencies = [ |
9 | "memchr", | 9 | "memchr", |
10 | ] | 10 | ] |
@@ -88,21 +88,6 @@ dependencies = [ | |||
88 | ] | 88 | ] |
89 | 89 | ||
90 | [[package]] | 90 | [[package]] |
91 | name = "byteorder" | ||
92 | version = "1.3.4" | ||
93 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
94 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | ||
95 | |||
96 | [[package]] | ||
97 | name = "c2-chacha" | ||
98 | version = "0.2.3" | ||
99 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
100 | checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" | ||
101 | dependencies = [ | ||
102 | "ppv-lite86", | ||
103 | ] | ||
104 | |||
105 | [[package]] | ||
106 | name = "cargo_metadata" | 91 | name = "cargo_metadata" |
107 | version = "0.9.1" | 92 | version = "0.9.1" |
108 | source = "registry+https://github.com/rust-lang/crates.io-index" | 93 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -213,15 +198,14 @@ dependencies = [ | |||
213 | 198 | ||
214 | [[package]] | 199 | [[package]] |
215 | name = "console" | 200 | name = "console" |
216 | version = "0.9.2" | 201 | version = "0.10.0" |
217 | source = "registry+https://github.com/rust-lang/crates.io-index" | 202 | source = "registry+https://github.com/rust-lang/crates.io-index" |
218 | checksum = "45e0f3986890b3acbc782009e2629dfe2baa430ac091519ce3be26164a2ae6c0" | 203 | checksum = "6728a28023f207181b193262711102bfbaf47cc9d13bc71d0736607ef8efe88c" |
219 | dependencies = [ | 204 | dependencies = [ |
220 | "clicolors-control", | 205 | "clicolors-control", |
221 | "encode_unicode", | 206 | "encode_unicode", |
222 | "lazy_static", | 207 | "lazy_static", |
223 | "libc", | 208 | "libc", |
224 | "regex", | ||
225 | "termios", | 209 | "termios", |
226 | "winapi 0.3.8", | 210 | "winapi 0.3.8", |
227 | ] | 211 | ] |
@@ -402,12 +386,9 @@ dependencies = [ | |||
402 | 386 | ||
403 | [[package]] | 387 | [[package]] |
404 | name = "fst" | 388 | name = "fst" |
405 | version = "0.3.5" | 389 | version = "0.4.0" |
406 | source = "registry+https://github.com/rust-lang/crates.io-index" | 390 | source = "registry+https://github.com/rust-lang/crates.io-index" |
407 | checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6" | 391 | checksum = "3f7c13470d799474d44e2b9c6a0925807def7af4d120cd4de761433be76f7579" |
408 | dependencies = [ | ||
409 | "byteorder", | ||
410 | ] | ||
411 | 392 | ||
412 | [[package]] | 393 | [[package]] |
413 | name = "fuchsia-zircon" | 394 | name = "fuchsia-zircon" |
@@ -509,9 +490,9 @@ dependencies = [ | |||
509 | 490 | ||
510 | [[package]] | 491 | [[package]] |
511 | name = "insta" | 492 | name = "insta" |
512 | version = "0.13.1" | 493 | version = "0.15.0" |
513 | source = "registry+https://github.com/rust-lang/crates.io-index" | 494 | source = "registry+https://github.com/rust-lang/crates.io-index" |
514 | checksum = "8df742abee84dbf27d20869c9adf77b0d8f7ea3eead13c2c9e3998d136a97058" | 495 | checksum = "8de3f029212a3fe78a6090f1f2b993877ca245a9ded863f3fcbd6eae084fc1ed" |
515 | dependencies = [ | 496 | dependencies = [ |
516 | "console", | 497 | "console", |
517 | "difference", | 498 | "difference", |
@@ -655,9 +636,9 @@ dependencies = [ | |||
655 | 636 | ||
656 | [[package]] | 637 | [[package]] |
657 | name = "lsp-types" | 638 | name = "lsp-types" |
658 | version = "0.72.0" | 639 | version = "0.73.0" |
659 | source = "registry+https://github.com/rust-lang/crates.io-index" | 640 | source = "registry+https://github.com/rust-lang/crates.io-index" |
660 | checksum = "face91691e558746745dc9dc6c67a4e2a24e044926e274d8378e6f19659329f0" | 641 | checksum = "93d0cf64ea141b43d9e055f6b9df13f0bce32b103d84237509ce0a571ab9b159" |
661 | dependencies = [ | 642 | dependencies = [ |
662 | "base64", | 643 | "base64", |
663 | "bitflags", | 644 | "bitflags", |
@@ -960,6 +941,7 @@ name = "ra_hir" | |||
960 | version = "0.1.0" | 941 | version = "0.1.0" |
961 | dependencies = [ | 942 | dependencies = [ |
962 | "either", | 943 | "either", |
944 | "itertools", | ||
963 | "log", | 945 | "log", |
964 | "ra_db", | 946 | "ra_db", |
965 | "ra_hir_def", | 947 | "ra_hir_def", |
@@ -1180,11 +1162,11 @@ dependencies = [ | |||
1180 | 1162 | ||
1181 | [[package]] | 1163 | [[package]] |
1182 | name = "rand_chacha" | 1164 | name = "rand_chacha" |
1183 | version = "0.2.1" | 1165 | version = "0.2.2" |
1184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1185 | checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" | 1167 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" |
1186 | dependencies = [ | 1168 | dependencies = [ |
1187 | "c2-chacha", | 1169 | "ppv-lite86", |
1188 | "rand_core", | 1170 | "rand_core", |
1189 | ] | 1171 | ] |
1190 | 1172 | ||
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml index 731d50371..741345a21 100644 --- a/crates/ra_cargo_watch/Cargo.toml +++ b/crates/ra_cargo_watch/Cargo.toml | |||
@@ -6,10 +6,10 @@ authors = ["rust-analyzer developers"] | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | crossbeam-channel = "0.4.0" | 8 | crossbeam-channel = "0.4.0" |
9 | lsp-types = { version = "0.72.0", features = ["proposed"] } | 9 | lsp-types = { version = "0.73.0", features = ["proposed"] } |
10 | log = "0.4.8" | 10 | log = "0.4.8" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | serde_json = "1.0.48" | 12 | serde_json = "1.0.48" |
13 | 13 | ||
14 | [dev-dependencies] | 14 | [dev-dependencies] |
15 | insta = "0.13.1" | 15 | insta = "0.15.0" |
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index da7af110c..947d6ad56 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -56,6 +56,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { | |||
56 | crate_graph.add_crate_root( | 56 | crate_graph.add_crate_root( |
57 | file_id, | 57 | file_id, |
58 | Edition::Edition2018, | 58 | Edition::Edition2018, |
59 | None, | ||
59 | CfgOptions::default(), | 60 | CfgOptions::default(), |
60 | Env::default(), | 61 | Env::default(), |
61 | ); | 62 | ); |
@@ -98,8 +99,13 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
98 | assert!(meta.path.starts_with(&source_root_prefix)); | 99 | assert!(meta.path.starts_with(&source_root_prefix)); |
99 | 100 | ||
100 | if let Some(krate) = meta.krate { | 101 | if let Some(krate) = meta.krate { |
101 | let crate_id = | 102 | let crate_id = crate_graph.add_crate_root( |
102 | crate_graph.add_crate_root(file_id, meta.edition, meta.cfg, Env::default()); | 103 | file_id, |
104 | meta.edition, | ||
105 | Some(krate.clone()), | ||
106 | meta.cfg, | ||
107 | Env::default(), | ||
108 | ); | ||
103 | let prev = crates.insert(krate.clone(), crate_id); | 109 | let prev = crates.insert(krate.clone(), crate_id); |
104 | assert!(prev.is_none()); | 110 | assert!(prev.is_none()); |
105 | for dep in meta.deps { | 111 | for dep in meta.deps { |
@@ -132,6 +138,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
132 | crate_graph.add_crate_root( | 138 | crate_graph.add_crate_root( |
133 | crate_root, | 139 | crate_root, |
134 | Edition::Edition2018, | 140 | Edition::Edition2018, |
141 | None, | ||
135 | CfgOptions::default(), | 142 | CfgOptions::default(), |
136 | Env::default(), | 143 | Env::default(), |
137 | ); | 144 | ); |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index eaff99fd3..b77640b2b 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -6,7 +6,7 @@ | |||
6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how | 6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how |
7 | //! actual IO is done and lowered to input. | 7 | //! actual IO is done and lowered to input. |
8 | 8 | ||
9 | use std::{fmt, str::FromStr}; | 9 | use std::{fmt, ops, str::FromStr}; |
10 | 10 | ||
11 | use ra_cfg::CfgOptions; | 11 | use ra_cfg::CfgOptions; |
12 | use ra_syntax::SmolStr; | 12 | use ra_syntax::SmolStr; |
@@ -86,7 +86,7 @@ pub struct CrateId(pub u32); | |||
86 | pub struct CrateName(SmolStr); | 86 | pub struct CrateName(SmolStr); |
87 | 87 | ||
88 | impl CrateName { | 88 | impl CrateName { |
89 | /// Crates a crate name, checking for dashes in the string provided. | 89 | /// Creates a crate name, checking for dashes in the string provided. |
90 | /// Dashes are not allowed in the crate names, | 90 | /// Dashes are not allowed in the crate names, |
91 | /// hence the input string is returned as `Err` for those cases. | 91 | /// hence the input string is returned as `Err` for those cases. |
92 | pub fn new(name: &str) -> Result<CrateName, &str> { | 92 | pub fn new(name: &str) -> Result<CrateName, &str> { |
@@ -97,19 +97,23 @@ impl CrateName { | |||
97 | } | 97 | } |
98 | } | 98 | } |
99 | 99 | ||
100 | /// Crates a crate name, unconditionally replacing the dashes with underscores. | 100 | /// Creates a crate name, unconditionally replacing the dashes with underscores. |
101 | pub fn normalize_dashes(name: &str) -> CrateName { | 101 | pub fn normalize_dashes(name: &str) -> CrateName { |
102 | Self(SmolStr::new(name.replace('-', "_"))) | 102 | Self(SmolStr::new(name.replace('-', "_"))) |
103 | } | 103 | } |
104 | } | 104 | } |
105 | 105 | ||
106 | #[derive(Debug, Clone, PartialEq, Eq)] | 106 | #[derive(Debug, Clone, PartialEq, Eq)] |
107 | struct CrateData { | 107 | pub struct CrateData { |
108 | file_id: FileId, | 108 | pub root_file_id: FileId, |
109 | edition: Edition, | 109 | pub edition: Edition, |
110 | cfg_options: CfgOptions, | 110 | /// The name to display to the end user. |
111 | env: Env, | 111 | /// This actual crate name can be different in a particular dependent crate |
112 | dependencies: Vec<Dependency>, | 112 | /// or may even be missing for some cases, such as a dummy crate for the code snippet. |
113 | pub display_name: Option<String>, | ||
114 | pub cfg_options: CfgOptions, | ||
115 | pub env: Env, | ||
116 | pub dependencies: Vec<Dependency>, | ||
113 | } | 117 | } |
114 | 118 | ||
115 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 119 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -134,20 +138,24 @@ impl CrateGraph { | |||
134 | &mut self, | 138 | &mut self, |
135 | file_id: FileId, | 139 | file_id: FileId, |
136 | edition: Edition, | 140 | edition: Edition, |
141 | display_name: Option<String>, | ||
137 | cfg_options: CfgOptions, | 142 | cfg_options: CfgOptions, |
138 | env: Env, | 143 | env: Env, |
139 | ) -> CrateId { | 144 | ) -> CrateId { |
140 | let data = CrateData::new(file_id, edition, cfg_options, env); | 145 | let data = CrateData { |
146 | root_file_id: file_id, | ||
147 | edition, | ||
148 | display_name, | ||
149 | cfg_options, | ||
150 | env, | ||
151 | dependencies: Vec::new(), | ||
152 | }; | ||
141 | let crate_id = CrateId(self.arena.len() as u32); | 153 | let crate_id = CrateId(self.arena.len() as u32); |
142 | let prev = self.arena.insert(crate_id, data); | 154 | let prev = self.arena.insert(crate_id, data); |
143 | assert!(prev.is_none()); | 155 | assert!(prev.is_none()); |
144 | crate_id | 156 | crate_id |
145 | } | 157 | } |
146 | 158 | ||
147 | pub fn cfg_options(&self, crate_id: CrateId) -> &CfgOptions { | ||
148 | &self.arena[&crate_id].cfg_options | ||
149 | } | ||
150 | |||
151 | pub fn add_dep( | 159 | pub fn add_dep( |
152 | &mut self, | 160 | &mut self, |
153 | from: CrateId, | 161 | from: CrateId, |
@@ -169,24 +177,13 @@ impl CrateGraph { | |||
169 | self.arena.keys().copied() | 177 | self.arena.keys().copied() |
170 | } | 178 | } |
171 | 179 | ||
172 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | ||
173 | self.arena[&crate_id].file_id | ||
174 | } | ||
175 | |||
176 | pub fn edition(&self, crate_id: CrateId) -> Edition { | ||
177 | self.arena[&crate_id].edition | ||
178 | } | ||
179 | |||
180 | // FIXME: this only finds one crate with the given root; we could have multiple | 180 | // FIXME: this only finds one crate with the given root; we could have multiple |
181 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | 181 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { |
182 | let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?; | 182 | let (&crate_id, _) = |
183 | self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; | ||
183 | Some(crate_id) | 184 | Some(crate_id) |
184 | } | 185 | } |
185 | 186 | ||
186 | pub fn dependencies(&self, crate_id: CrateId) -> impl Iterator<Item = &Dependency> { | ||
187 | self.arena[&crate_id].dependencies.iter() | ||
188 | } | ||
189 | |||
190 | /// Extends this crate graph by adding a complete disjoint second crate | 187 | /// Extends this crate graph by adding a complete disjoint second crate |
191 | /// graph. | 188 | /// graph. |
192 | /// | 189 | /// |
@@ -209,8 +206,8 @@ impl CrateGraph { | |||
209 | return false; | 206 | return false; |
210 | } | 207 | } |
211 | 208 | ||
212 | for dep in self.dependencies(from) { | 209 | for dep in &self[from].dependencies { |
213 | let crate_id = dep.crate_id(); | 210 | let crate_id = dep.crate_id; |
214 | if crate_id == target { | 211 | if crate_id == target { |
215 | return true; | 212 | return true; |
216 | } | 213 | } |
@@ -223,6 +220,13 @@ impl CrateGraph { | |||
223 | } | 220 | } |
224 | } | 221 | } |
225 | 222 | ||
223 | impl ops::Index<CrateId> for CrateGraph { | ||
224 | type Output = CrateData; | ||
225 | fn index(&self, crate_id: CrateId) -> &CrateData { | ||
226 | &self.arena[&crate_id] | ||
227 | } | ||
228 | } | ||
229 | |||
226 | impl CrateId { | 230 | impl CrateId { |
227 | pub fn shift(self, amount: u32) -> CrateId { | 231 | pub fn shift(self, amount: u32) -> CrateId { |
228 | CrateId(self.0 + amount) | 232 | CrateId(self.0 + amount) |
@@ -230,10 +234,6 @@ impl CrateId { | |||
230 | } | 234 | } |
231 | 235 | ||
232 | impl CrateData { | 236 | impl CrateData { |
233 | fn new(file_id: FileId, edition: Edition, cfg_options: CfgOptions, env: Env) -> CrateData { | ||
234 | CrateData { file_id, edition, dependencies: Vec::new(), cfg_options, env } | ||
235 | } | ||
236 | |||
237 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { | 237 | fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { |
238 | self.dependencies.push(Dependency { name, crate_id }) | 238 | self.dependencies.push(Dependency { name, crate_id }) |
239 | } | 239 | } |
@@ -261,12 +261,6 @@ impl fmt::Display for Edition { | |||
261 | } | 261 | } |
262 | } | 262 | } |
263 | 263 | ||
264 | impl Dependency { | ||
265 | pub fn crate_id(&self) -> CrateId { | ||
266 | self.crate_id | ||
267 | } | ||
268 | } | ||
269 | |||
270 | #[derive(Debug)] | 264 | #[derive(Debug)] |
271 | pub struct ParseEditionError { | 265 | pub struct ParseEditionError { |
272 | invalid_input: String, | 266 | invalid_input: String, |
@@ -290,12 +284,27 @@ mod tests { | |||
290 | #[test] | 284 | #[test] |
291 | fn it_should_panic_because_of_cycle_dependencies() { | 285 | fn it_should_panic_because_of_cycle_dependencies() { |
292 | let mut graph = CrateGraph::default(); | 286 | let mut graph = CrateGraph::default(); |
293 | let crate1 = | 287 | let crate1 = graph.add_crate_root( |
294 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | 288 | FileId(1u32), |
295 | let crate2 = | 289 | Edition2018, |
296 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 290 | None, |
297 | let crate3 = | 291 | CfgOptions::default(), |
298 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); | 292 | Env::default(), |
293 | ); | ||
294 | let crate2 = graph.add_crate_root( | ||
295 | FileId(2u32), | ||
296 | Edition2018, | ||
297 | None, | ||
298 | CfgOptions::default(), | ||
299 | Env::default(), | ||
300 | ); | ||
301 | let crate3 = graph.add_crate_root( | ||
302 | FileId(3u32), | ||
303 | Edition2018, | ||
304 | None, | ||
305 | CfgOptions::default(), | ||
306 | Env::default(), | ||
307 | ); | ||
299 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 308 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
300 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 309 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
301 | assert!(graph.add_dep(crate3, CrateName::new("crate1").unwrap(), crate1).is_err()); | 310 | assert!(graph.add_dep(crate3, CrateName::new("crate1").unwrap(), crate1).is_err()); |
@@ -304,12 +313,27 @@ mod tests { | |||
304 | #[test] | 313 | #[test] |
305 | fn it_works() { | 314 | fn it_works() { |
306 | let mut graph = CrateGraph::default(); | 315 | let mut graph = CrateGraph::default(); |
307 | let crate1 = | 316 | let crate1 = graph.add_crate_root( |
308 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | 317 | FileId(1u32), |
309 | let crate2 = | 318 | Edition2018, |
310 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 319 | None, |
311 | let crate3 = | 320 | CfgOptions::default(), |
312 | graph.add_crate_root(FileId(3u32), Edition2018, CfgOptions::default(), Env::default()); | 321 | Env::default(), |
322 | ); | ||
323 | let crate2 = graph.add_crate_root( | ||
324 | FileId(2u32), | ||
325 | Edition2018, | ||
326 | None, | ||
327 | CfgOptions::default(), | ||
328 | Env::default(), | ||
329 | ); | ||
330 | let crate3 = graph.add_crate_root( | ||
331 | FileId(3u32), | ||
332 | Edition2018, | ||
333 | None, | ||
334 | CfgOptions::default(), | ||
335 | Env::default(), | ||
336 | ); | ||
313 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 337 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
314 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 338 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
315 | } | 339 | } |
@@ -317,16 +341,26 @@ mod tests { | |||
317 | #[test] | 341 | #[test] |
318 | fn dashes_are_normalized() { | 342 | fn dashes_are_normalized() { |
319 | let mut graph = CrateGraph::default(); | 343 | let mut graph = CrateGraph::default(); |
320 | let crate1 = | 344 | let crate1 = graph.add_crate_root( |
321 | graph.add_crate_root(FileId(1u32), Edition2018, CfgOptions::default(), Env::default()); | 345 | FileId(1u32), |
322 | let crate2 = | 346 | Edition2018, |
323 | graph.add_crate_root(FileId(2u32), Edition2018, CfgOptions::default(), Env::default()); | 347 | None, |
348 | CfgOptions::default(), | ||
349 | Env::default(), | ||
350 | ); | ||
351 | let crate2 = graph.add_crate_root( | ||
352 | FileId(2u32), | ||
353 | Edition2018, | ||
354 | None, | ||
355 | CfgOptions::default(), | ||
356 | Env::default(), | ||
357 | ); | ||
324 | assert!(graph | 358 | assert!(graph |
325 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) | 359 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) |
326 | .is_ok()); | 360 | .is_ok()); |
327 | assert_eq!( | 361 | assert_eq!( |
328 | graph.dependencies(crate1).collect::<Vec<_>>(), | 362 | graph[crate1].dependencies, |
329 | vec![&Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] | 363 | vec![Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] |
330 | ); | 364 | ); |
331 | } | 365 | } |
332 | } | 366 | } |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 0555a0de7..266c4cff3 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -12,6 +12,8 @@ log = "0.4.8" | |||
12 | rustc-hash = "1.1.0" | 12 | rustc-hash = "1.1.0" |
13 | either = "1.5.3" | 13 | either = "1.5.3" |
14 | 14 | ||
15 | itertools = "0.8.2" | ||
16 | |||
15 | ra_syntax = { path = "../ra_syntax" } | 17 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_db = { path = "../ra_db" } | 18 | ra_db = { path = "../ra_db" } |
17 | ra_prof = { path = "../ra_prof" } | 19 | ra_prof = { path = "../ra_prof" } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 2944926e6..41d4e2ed3 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -54,10 +54,11 @@ pub struct CrateDependency { | |||
54 | 54 | ||
55 | impl Crate { | 55 | impl Crate { |
56 | pub fn dependencies(self, db: &impl DefDatabase) -> Vec<CrateDependency> { | 56 | pub fn dependencies(self, db: &impl DefDatabase) -> Vec<CrateDependency> { |
57 | db.crate_graph() | 57 | db.crate_graph()[self.id] |
58 | .dependencies(self.id) | 58 | .dependencies |
59 | .iter() | ||
59 | .map(|dep| { | 60 | .map(|dep| { |
60 | let krate = Crate { id: dep.crate_id() }; | 61 | let krate = Crate { id: dep.crate_id }; |
61 | let name = dep.as_name(); | 62 | let name = dep.as_name(); |
62 | CrateDependency { krate, name } | 63 | CrateDependency { krate, name } |
63 | }) | 64 | }) |
@@ -69,7 +70,9 @@ impl Crate { | |||
69 | let crate_graph = db.crate_graph(); | 70 | let crate_graph = db.crate_graph(); |
70 | crate_graph | 71 | crate_graph |
71 | .iter() | 72 | .iter() |
72 | .filter(|&krate| crate_graph.dependencies(krate).any(|it| it.crate_id == self.id)) | 73 | .filter(|&krate| { |
74 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
75 | }) | ||
73 | .map(|id| Crate { id }) | 76 | .map(|id| Crate { id }) |
74 | .collect() | 77 | .collect() |
75 | } | 78 | } |
@@ -80,12 +83,11 @@ impl Crate { | |||
80 | } | 83 | } |
81 | 84 | ||
82 | pub fn root_file(self, db: &impl DefDatabase) -> FileId { | 85 | pub fn root_file(self, db: &impl DefDatabase) -> FileId { |
83 | db.crate_graph().crate_root(self.id) | 86 | db.crate_graph()[self.id].root_file_id |
84 | } | 87 | } |
85 | 88 | ||
86 | pub fn edition(self, db: &impl DefDatabase) -> Edition { | 89 | pub fn edition(self, db: &impl DefDatabase) -> Edition { |
87 | let crate_graph = db.crate_graph(); | 90 | db.crate_graph()[self.id].edition |
88 | crate_graph.edition(self.id) | ||
89 | } | 91 | } |
90 | 92 | ||
91 | pub fn all(db: &impl DefDatabase) -> Vec<Crate> { | 93 | pub fn all(db: &impl DefDatabase) -> Vec<Crate> { |
@@ -204,10 +206,26 @@ impl Module { | |||
204 | } | 206 | } |
205 | 207 | ||
206 | /// Returns a `ModuleScope`: a set of items, visible in this module. | 208 | /// Returns a `ModuleScope`: a set of items, visible in this module. |
207 | pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef)> { | 209 | pub fn scope( |
210 | self, | ||
211 | db: &impl HirDatabase, | ||
212 | visible_from: Option<Module>, | ||
213 | ) -> Vec<(Name, ScopeDef)> { | ||
208 | db.crate_def_map(self.id.krate)[self.id.local_id] | 214 | db.crate_def_map(self.id.krate)[self.id.local_id] |
209 | .scope | 215 | .scope |
210 | .entries() | 216 | .entries() |
217 | .filter_map(|(name, def)| { | ||
218 | if let Some(m) = visible_from { | ||
219 | let filtered = def.filter_visibility(|vis| vis.is_visible_from(db, m.id)); | ||
220 | if filtered.is_none() && !def.is_none() { | ||
221 | None | ||
222 | } else { | ||
223 | Some((name, filtered)) | ||
224 | } | ||
225 | } else { | ||
226 | Some((name, def)) | ||
227 | } | ||
228 | }) | ||
211 | .map(|(name, def)| (name.clone(), def.into())) | 229 | .map(|(name, def)| (name.clone(), def.into())) |
212 | .collect() | 230 | .collect() |
213 | } | 231 | } |
@@ -480,6 +498,14 @@ impl Adt { | |||
480 | pub fn krate(self, db: &impl HirDatabase) -> Option<Crate> { | 498 | pub fn krate(self, db: &impl HirDatabase) -> Option<Crate> { |
481 | Some(self.module(db).krate()) | 499 | Some(self.module(db).krate()) |
482 | } | 500 | } |
501 | |||
502 | pub fn name(&self, db: &impl HirDatabase) -> Name { | ||
503 | match self { | ||
504 | Adt::Struct(s) => s.name(db), | ||
505 | Adt::Union(u) => u.name(db), | ||
506 | Adt::Enum(e) => e.name(db), | ||
507 | } | ||
508 | } | ||
483 | } | 509 | } |
484 | 510 | ||
485 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 511 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
@@ -507,6 +533,14 @@ impl VariantDef { | |||
507 | } | 533 | } |
508 | } | 534 | } |
509 | 535 | ||
536 | pub fn name(&self, db: &impl HirDatabase) -> Name { | ||
537 | match self { | ||
538 | VariantDef::Struct(s) => s.name(db), | ||
539 | VariantDef::Union(u) => u.name(db), | ||
540 | VariantDef::EnumVariant(e) => e.name(db), | ||
541 | } | ||
542 | } | ||
543 | |||
510 | pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> { | 544 | pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> { |
511 | match self { | 545 | match self { |
512 | VariantDef::Struct(it) => it.variant_data(db), | 546 | VariantDef::Struct(it) => it.variant_data(db), |
@@ -534,6 +568,14 @@ impl DefWithBody { | |||
534 | DefWithBody::Static(s) => s.module(db), | 568 | DefWithBody::Static(s) => s.module(db), |
535 | } | 569 | } |
536 | } | 570 | } |
571 | |||
572 | pub fn name(self, db: &impl HirDatabase) -> Option<Name> { | ||
573 | match self { | ||
574 | DefWithBody::Function(f) => Some(f.name(db)), | ||
575 | DefWithBody::Static(s) => s.name(db), | ||
576 | DefWithBody::Const(c) => c.name(db), | ||
577 | } | ||
578 | } | ||
537 | } | 579 | } |
538 | 580 | ||
539 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 581 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -571,6 +613,14 @@ impl Function { | |||
571 | } | 613 | } |
572 | } | 614 | } |
573 | 615 | ||
616 | impl HasVisibility for Function { | ||
617 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
618 | let function_data = db.function_data(self.id); | ||
619 | let visibility = &function_data.visibility; | ||
620 | visibility.resolve(db, &self.id.resolver(db)) | ||
621 | } | ||
622 | } | ||
623 | |||
574 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 624 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
575 | pub struct Const { | 625 | pub struct Const { |
576 | pub(crate) id: ConstId, | 626 | pub(crate) id: ConstId, |
@@ -590,6 +640,14 @@ impl Const { | |||
590 | } | 640 | } |
591 | } | 641 | } |
592 | 642 | ||
643 | impl HasVisibility for Const { | ||
644 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
645 | let function_data = db.const_data(self.id); | ||
646 | let visibility = &function_data.visibility; | ||
647 | visibility.resolve(db, &self.id.resolver(db)) | ||
648 | } | ||
649 | } | ||
650 | |||
593 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 651 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
594 | pub struct Static { | 652 | pub struct Static { |
595 | pub(crate) id: StaticId, | 653 | pub(crate) id: StaticId, |
@@ -664,6 +722,14 @@ impl TypeAlias { | |||
664 | } | 722 | } |
665 | } | 723 | } |
666 | 724 | ||
725 | impl HasVisibility for TypeAlias { | ||
726 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
727 | let function_data = db.type_alias_data(self.id); | ||
728 | let visibility = &function_data.visibility; | ||
729 | visibility.resolve(db, &self.id.resolver(db)) | ||
730 | } | ||
731 | } | ||
732 | |||
667 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 733 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
668 | pub struct MacroDef { | 734 | pub struct MacroDef { |
669 | pub(crate) id: MacroDefId, | 735 | pub(crate) id: MacroDefId, |
@@ -751,6 +817,16 @@ impl AssocItem { | |||
751 | } | 817 | } |
752 | } | 818 | } |
753 | 819 | ||
820 | impl HasVisibility for AssocItem { | ||
821 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
822 | match self { | ||
823 | AssocItem::Function(f) => f.visibility(db), | ||
824 | AssocItem::Const(c) => c.visibility(db), | ||
825 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
826 | } | ||
827 | } | ||
828 | } | ||
829 | |||
754 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | 830 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] |
755 | pub enum GenericDef { | 831 | pub enum GenericDef { |
756 | Function(Function), | 832 | Function(Function), |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 965d185a4..3782a9984 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors}; | |||
6 | 6 | ||
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | resolver::{self, HasResolver, Resolver}, | 8 | resolver::{self, HasResolver, Resolver}, |
9 | TraitId, | 9 | AsMacroCall, TraitId, |
10 | }; | 10 | }; |
11 | use hir_expand::ExpansionInfo; | 11 | use hir_expand::ExpansionInfo; |
12 | use ra_db::{FileId, FileRange}; | 12 | use ra_db::{FileId, FileRange}; |
@@ -70,6 +70,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
70 | Some(node) | 70 | Some(node) |
71 | } | 71 | } |
72 | 72 | ||
73 | pub fn expand_hypothetical( | ||
74 | &self, | ||
75 | actual_macro_call: &ast::MacroCall, | ||
76 | hypothetical_args: &ast::TokenTree, | ||
77 | token_to_map: SyntaxToken, | ||
78 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
79 | let macro_call = | ||
80 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | ||
81 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
82 | let macro_call_id = macro_call | ||
83 | .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; | ||
84 | hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) | ||
85 | } | ||
86 | |||
73 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | 87 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { |
74 | let parent = token.parent(); | 88 | let parent = token.parent(); |
75 | let parent = self.find_file(parent); | 89 | let parent = self.find_file(parent); |
@@ -104,6 +118,25 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
104 | node.ancestors_with_macros(self.db).map(|it| it.value) | 118 | node.ancestors_with_macros(self.db).map(|it| it.value) |
105 | } | 119 | } |
106 | 120 | ||
121 | pub fn ancestors_at_offset_with_macros( | ||
122 | &self, | ||
123 | node: &SyntaxNode, | ||
124 | offset: TextUnit, | ||
125 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
126 | use itertools::Itertools; | ||
127 | node.token_at_offset(offset) | ||
128 | .map(|token| self.ancestors_with_macros(token.parent())) | ||
129 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) | ||
130 | } | ||
131 | |||
132 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
133 | &self, | ||
134 | node: &SyntaxNode, | ||
135 | offset: TextUnit, | ||
136 | ) -> Option<N> { | ||
137 | self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
138 | } | ||
139 | |||
107 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 140 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
108 | self.analyze(expr.syntax()).type_of(self.db, &expr) | 141 | self.analyze(expr.syntax()).type_of(self.db, &expr) |
109 | } | 142 | } |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index fa25cc4fb..30a12337e 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -26,4 +26,4 @@ ra_cfg = { path = "../ra_cfg" } | |||
26 | tt = { path = "../ra_tt", package = "ra_tt" } | 26 | tt = { path = "../ra_tt", package = "ra_tt" } |
27 | 27 | ||
28 | [dev-dependencies] | 28 | [dev-dependencies] |
29 | insta = "0.13.1" | 29 | insta = "0.15.0" |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 9fc43f3fb..a72eb5369 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -7,13 +7,16 @@ use hir_expand::{ | |||
7 | AstId, InFile, | 7 | AstId, InFile, |
8 | }; | 8 | }; |
9 | use ra_prof::profile; | 9 | use ra_prof::profile; |
10 | use ra_syntax::ast::{self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner}; | 10 | use ra_syntax::ast::{ |
11 | self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner, | ||
12 | }; | ||
11 | 13 | ||
12 | use crate::{ | 14 | use crate::{ |
13 | db::DefDatabase, | 15 | db::DefDatabase, |
14 | path::{path, GenericArgs, Path}, | 16 | path::{path, GenericArgs, Path}, |
15 | src::HasSource, | 17 | src::HasSource, |
16 | type_ref::{Mutability, TypeBound, TypeRef}, | 18 | type_ref::{Mutability, TypeBound, TypeRef}, |
19 | visibility::RawVisibility, | ||
17 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, | 20 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, |
18 | ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, | 21 | ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, |
19 | }; | 22 | }; |
@@ -26,6 +29,7 @@ pub struct FunctionData { | |||
26 | /// True if the first param is `self`. This is relevant to decide whether this | 29 | /// True if the first param is `self`. This is relevant to decide whether this |
27 | /// can be called as a method. | 30 | /// can be called as a method. |
28 | pub has_self_param: bool, | 31 | pub has_self_param: bool, |
32 | pub visibility: RawVisibility, | ||
29 | } | 33 | } |
30 | 34 | ||
31 | impl FunctionData { | 35 | impl FunctionData { |
@@ -72,7 +76,9 @@ impl FunctionData { | |||
72 | ret_type | 76 | ret_type |
73 | }; | 77 | }; |
74 | 78 | ||
75 | let sig = FunctionData { name, params, ret_type, has_self_param }; | 79 | let visibility = RawVisibility::from_ast(db, src.map(|s| s.visibility())); |
80 | |||
81 | let sig = FunctionData { name, params, ret_type, has_self_param, visibility }; | ||
76 | Arc::new(sig) | 82 | Arc::new(sig) |
77 | } | 83 | } |
78 | } | 84 | } |
@@ -91,6 +97,7 @@ fn desugar_future_path(orig: TypeRef) -> Path { | |||
91 | pub struct TypeAliasData { | 97 | pub struct TypeAliasData { |
92 | pub name: Name, | 98 | pub name: Name, |
93 | pub type_ref: Option<TypeRef>, | 99 | pub type_ref: Option<TypeRef>, |
100 | pub visibility: RawVisibility, | ||
94 | } | 101 | } |
95 | 102 | ||
96 | impl TypeAliasData { | 103 | impl TypeAliasData { |
@@ -98,10 +105,11 @@ impl TypeAliasData { | |||
98 | db: &impl DefDatabase, | 105 | db: &impl DefDatabase, |
99 | typ: TypeAliasId, | 106 | typ: TypeAliasId, |
100 | ) -> Arc<TypeAliasData> { | 107 | ) -> Arc<TypeAliasData> { |
101 | let node = typ.lookup(db).source(db).value; | 108 | let node = typ.lookup(db).source(db); |
102 | let name = node.name().map_or_else(Name::missing, |n| n.as_name()); | 109 | let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); |
103 | let type_ref = node.type_ref().map(TypeRef::from_ast); | 110 | let type_ref = node.value.type_ref().map(TypeRef::from_ast); |
104 | Arc::new(TypeAliasData { name, type_ref }) | 111 | let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); |
112 | Arc::new(TypeAliasData { name, type_ref, visibility }) | ||
105 | } | 113 | } |
106 | } | 114 | } |
107 | 115 | ||
@@ -217,23 +225,28 @@ pub struct ConstData { | |||
217 | /// const _: () = (); | 225 | /// const _: () = (); |
218 | pub name: Option<Name>, | 226 | pub name: Option<Name>, |
219 | pub type_ref: TypeRef, | 227 | pub type_ref: TypeRef, |
228 | pub visibility: RawVisibility, | ||
220 | } | 229 | } |
221 | 230 | ||
222 | impl ConstData { | 231 | impl ConstData { |
223 | pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> { | 232 | pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> { |
224 | let node = konst.lookup(db).source(db).value; | 233 | let node = konst.lookup(db).source(db); |
225 | Arc::new(ConstData::new(&node)) | 234 | Arc::new(ConstData::new(db, node)) |
226 | } | 235 | } |
227 | 236 | ||
228 | pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> { | 237 | pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> { |
229 | let node = konst.lookup(db).source(db).value; | 238 | let node = konst.lookup(db).source(db); |
230 | Arc::new(ConstData::new(&node)) | 239 | Arc::new(ConstData::new(db, node)) |
231 | } | 240 | } |
232 | 241 | ||
233 | fn new<N: NameOwner + TypeAscriptionOwner>(node: &N) -> ConstData { | 242 | fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( |
234 | let name = node.name().map(|n| n.as_name()); | 243 | db: &impl DefDatabase, |
235 | let type_ref = TypeRef::from_ast_opt(node.ascribed_type()); | 244 | node: InFile<N>, |
236 | ConstData { name, type_ref } | 245 | ) -> ConstData { |
246 | let name = node.value.name().map(|n| n.as_name()); | ||
247 | let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type()); | ||
248 | let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); | ||
249 | ConstData { name, type_ref, visibility } | ||
237 | } | 250 | } |
238 | } | 251 | } |
239 | 252 | ||
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 43b9b124a..07ca74ec3 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -176,7 +176,7 @@ fn find_importable_locations( | |||
176 | // directly (only through reexports in direct dependencies). | 176 | // directly (only through reexports in direct dependencies). |
177 | for krate in Some(from.krate) | 177 | for krate in Some(from.krate) |
178 | .into_iter() | 178 | .into_iter() |
179 | .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) | 179 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) |
180 | { | 180 | { |
181 | result.extend( | 181 | result.extend( |
182 | importable_locations_in_crate(db, item, krate) | 182 | importable_locations_in_crate(db, item, krate) |
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs index 5a336ea1f..6de49730e 100644 --- a/crates/ra_hir_def/src/lang_item.rs +++ b/crates/ra_hir_def/src/lang_item.rs | |||
@@ -116,8 +116,9 @@ impl LangItems { | |||
116 | if let Some(target) = start_crate_target { | 116 | if let Some(target) = start_crate_target { |
117 | return Some(*target); | 117 | return Some(*target); |
118 | } | 118 | } |
119 | db.crate_graph() | 119 | db.crate_graph()[start_crate] |
120 | .dependencies(start_crate) | 120 | .dependencies |
121 | .iter() | ||
121 | .find_map(|dep| db.lang_item(dep.crate_id, item.clone())) | 122 | .find_map(|dep| db.lang_item(dep.crate_id, item.clone())) |
122 | } | 123 | } |
123 | 124 | ||
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 628c44c99..81eac52ad 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs | |||
@@ -177,10 +177,10 @@ pub struct ModuleData { | |||
177 | 177 | ||
178 | impl CrateDefMap { | 178 | impl CrateDefMap { |
179 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 179 | pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { |
180 | let _p = profile("crate_def_map_query"); | 180 | let _p = profile("crate_def_map_query") |
181 | .detail(|| db.crate_graph()[krate].display_name.clone().unwrap_or_default()); | ||
181 | let def_map = { | 182 | let def_map = { |
182 | let crate_graph = db.crate_graph(); | 183 | let edition = db.crate_graph()[krate].edition; |
183 | let edition = crate_graph.edition(krate); | ||
184 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); | 184 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); |
185 | let root = modules.alloc(ModuleData::default()); | 185 | let root = modules.alloc(ModuleData::default()); |
186 | CrateDefMap { | 186 | CrateDefMap { |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 51c65a5d7..d0459d9b0 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -34,7 +34,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
34 | let crate_graph = db.crate_graph(); | 34 | let crate_graph = db.crate_graph(); |
35 | 35 | ||
36 | // populate external prelude | 36 | // populate external prelude |
37 | for dep in crate_graph.dependencies(def_map.krate) { | 37 | for dep in &crate_graph[def_map.krate].dependencies { |
38 | let dep_def_map = db.crate_def_map(dep.crate_id); | 38 | let dep_def_map = db.crate_def_map(dep.crate_id); |
39 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); | 39 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); |
40 | def_map.extern_prelude.insert( | 40 | def_map.extern_prelude.insert( |
@@ -51,7 +51,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
51 | } | 51 | } |
52 | } | 52 | } |
53 | 53 | ||
54 | let cfg_options = crate_graph.cfg_options(def_map.krate); | 54 | let cfg_options = &crate_graph[def_map.krate].cfg_options; |
55 | 55 | ||
56 | let mut collector = DefCollector { | 56 | let mut collector = DefCollector { |
57 | db, | 57 | db, |
@@ -128,8 +128,7 @@ where | |||
128 | DB: DefDatabase, | 128 | DB: DefDatabase, |
129 | { | 129 | { |
130 | fn collect(&mut self) { | 130 | fn collect(&mut self) { |
131 | let crate_graph = self.db.crate_graph(); | 131 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; |
132 | let file_id = crate_graph.crate_root(self.def_map.krate); | ||
133 | let raw_items = self.db.raw_items(file_id.into()); | 132 | let raw_items = self.db.raw_items(file_id.into()); |
134 | let module_id = self.def_map.root; | 133 | let module_id = self.def_map.root; |
135 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | 134 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; |
@@ -955,7 +954,7 @@ mod tests { | |||
955 | let krate = db.test_crate(); | 954 | let krate = db.test_crate(); |
956 | 955 | ||
957 | let def_map = { | 956 | let def_map = { |
958 | let edition = db.crate_graph().edition(krate); | 957 | let edition = db.crate_graph()[krate].edition; |
959 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); | 958 | let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); |
960 | let root = modules.alloc(ModuleData::default()); | 959 | let root = modules.alloc(ModuleData::default()); |
961 | CrateDefMap { | 960 | CrateDefMap { |
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 9fc33e4b1..3f60b1cca 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -7,6 +7,7 @@ use crate::{ | |||
7 | 7 | ||
8 | use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; | 8 | use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; |
9 | use either::Either; | 9 | use either::Either; |
10 | use mbe::parse_to_token_tree; | ||
10 | use ra_db::{FileId, RelativePath}; | 11 | use ra_db::{FileId, RelativePath}; |
11 | use ra_parser::FragmentKind; | 12 | use ra_parser::FragmentKind; |
12 | 13 | ||
@@ -142,7 +143,10 @@ fn env_expand( | |||
142 | _tt: &tt::Subtree, | 143 | _tt: &tt::Subtree, |
143 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 144 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
144 | // dummy implementation for type-checking purposes | 145 | // dummy implementation for type-checking purposes |
145 | let expanded = quote! { "" }; | 146 | // we cannot use an empty string here, because for |
147 | // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become | ||
148 | // `include!("foo.rs"), which maybe infinite loop | ||
149 | let expanded = quote! { "__RA_UNIMPLEMENTATED__" }; | ||
146 | 150 | ||
147 | Ok(expanded) | 151 | Ok(expanded) |
148 | } | 152 | } |
@@ -276,7 +280,12 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti | |||
276 | let call_site = call_id.as_file().original_file(db); | 280 | let call_site = call_id.as_file().original_file(db); |
277 | let path = RelativePath::new(&path); | 281 | let path = RelativePath::new(&path); |
278 | 282 | ||
279 | db.resolve_relative_path(call_site, &path) | 283 | let res = db.resolve_relative_path(call_site, &path)?; |
284 | // Prevent include itself | ||
285 | if res == call_site { | ||
286 | return None; | ||
287 | } | ||
288 | Some(res) | ||
280 | } | 289 | } |
281 | 290 | ||
282 | fn include_expand( | 291 | fn include_expand( |
@@ -298,10 +307,9 @@ fn include_expand( | |||
298 | 307 | ||
299 | // FIXME: | 308 | // FIXME: |
300 | // Handle include as expression | 309 | // Handle include as expression |
301 | let node = | 310 | let res = parse_to_token_tree(&db.file_text(file_id.into())) |
302 | db.parse_or_expand(file_id.into()).ok_or_else(|| mbe::ExpandError::ConversionError)?; | 311 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
303 | let res = | 312 | .0; |
304 | mbe::syntax_node_to_token_tree(&node).ok_or_else(|| mbe::ExpandError::ConversionError)?.0; | ||
305 | 313 | ||
306 | Ok((res, FragmentKind::Items)) | 314 | Ok((res, FragmentKind::Items)) |
307 | } | 315 | } |
@@ -394,7 +402,7 @@ mod tests { | |||
394 | "#, | 402 | "#, |
395 | ); | 403 | ); |
396 | 404 | ||
397 | assert_eq!(expanded, "\"\""); | 405 | assert_eq!(expanded, "\"__RA_UNIMPLEMENTATED__\""); |
398 | } | 406 | } |
399 | 407 | ||
400 | #[test] | 408 | #[test] |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index f3a84cacc..29dde3d80 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -72,6 +72,30 @@ pub trait AstDatabase: SourceDatabase { | |||
72 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 72 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
73 | } | 73 | } |
74 | 74 | ||
75 | /// This expands the given macro call, but with different arguments. This is | ||
76 | /// used for completion, where we want to see what 'would happen' if we insert a | ||
77 | /// token. The `token_to_map` mapped down into the expansion, with the mapped | ||
78 | /// token returned. | ||
79 | pub fn expand_hypothetical( | ||
80 | db: &impl AstDatabase, | ||
81 | actual_macro_call: MacroCallId, | ||
82 | hypothetical_args: &ra_syntax::ast::TokenTree, | ||
83 | token_to_map: ra_syntax::SyntaxToken, | ||
84 | ) -> Option<(SyntaxNode, ra_syntax::SyntaxToken)> { | ||
85 | let macro_file = MacroFile { macro_call_id: actual_macro_call }; | ||
86 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()).unwrap(); | ||
87 | let range = | ||
88 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; | ||
89 | let token_id = tmap_1.token_by_range(range)?; | ||
90 | let macro_def = expander(db, actual_macro_call)?; | ||
91 | let (node, tmap_2) = | ||
92 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; | ||
93 | let token_id = macro_def.0.map_id_down(token_id); | ||
94 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | ||
95 | let token = ra_syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; | ||
96 | Some((node.syntax_node(), token)) | ||
97 | } | ||
98 | |||
75 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 99 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
76 | let map = | 100 | let map = |
77 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | 101 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); |
@@ -130,15 +154,42 @@ pub(crate) fn macro_expand( | |||
130 | db: &dyn AstDatabase, | 154 | db: &dyn AstDatabase, |
131 | id: MacroCallId, | 155 | id: MacroCallId, |
132 | ) -> Result<Arc<tt::Subtree>, String> { | 156 | ) -> Result<Arc<tt::Subtree>, String> { |
157 | macro_expand_with_arg(db, id, None) | ||
158 | } | ||
159 | |||
160 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
161 | let lazy_id = match id { | ||
162 | MacroCallId::LazyMacro(id) => id, | ||
163 | MacroCallId::EagerMacro(_id) => { | ||
164 | return None; | ||
165 | } | ||
166 | }; | ||
167 | |||
168 | let loc = db.lookup_intern_macro(lazy_id); | ||
169 | let macro_rules = db.macro_def(loc.def)?; | ||
170 | Some(macro_rules) | ||
171 | } | ||
172 | |||
173 | fn macro_expand_with_arg( | ||
174 | db: &dyn AstDatabase, | ||
175 | id: MacroCallId, | ||
176 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
177 | ) -> Result<Arc<tt::Subtree>, String> { | ||
133 | let lazy_id = match id { | 178 | let lazy_id = match id { |
134 | MacroCallId::LazyMacro(id) => id, | 179 | MacroCallId::LazyMacro(id) => id, |
135 | MacroCallId::EagerMacro(id) => { | 180 | MacroCallId::EagerMacro(id) => { |
136 | return Ok(db.lookup_intern_eager_expansion(id).subtree); | 181 | if arg.is_some() { |
182 | return Err( | ||
183 | "hypothetical macro expansion not implemented for eager macro".to_owned() | ||
184 | ); | ||
185 | } else { | ||
186 | return Ok(db.lookup_intern_eager_expansion(id).subtree); | ||
187 | } | ||
137 | } | 188 | } |
138 | }; | 189 | }; |
139 | 190 | ||
140 | let loc = db.lookup_intern_macro(lazy_id); | 191 | let loc = db.lookup_intern_macro(lazy_id); |
141 | let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; | 192 | let macro_arg = arg.or_else(|| db.macro_arg(id)).ok_or("Fail to args in to tt::TokenTree")?; |
142 | 193 | ||
143 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; | 194 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; |
144 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; | 195 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; |
@@ -163,11 +214,23 @@ pub(crate) fn parse_macro( | |||
163 | db: &dyn AstDatabase, | 214 | db: &dyn AstDatabase, |
164 | macro_file: MacroFile, | 215 | macro_file: MacroFile, |
165 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 216 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
217 | parse_macro_with_arg(db, macro_file, None) | ||
218 | } | ||
219 | |||
220 | pub fn parse_macro_with_arg( | ||
221 | db: &dyn AstDatabase, | ||
222 | macro_file: MacroFile, | ||
223 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
224 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | ||
166 | let _p = profile("parse_macro_query"); | 225 | let _p = profile("parse_macro_query"); |
167 | 226 | ||
168 | let macro_call_id = macro_file.macro_call_id; | 227 | let macro_call_id = macro_file.macro_call_id; |
169 | let tt = db | 228 | let expansion = if let Some(arg) = arg { |
170 | .macro_expand(macro_call_id) | 229 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
230 | } else { | ||
231 | db.macro_expand(macro_call_id) | ||
232 | }; | ||
233 | let tt = expansion | ||
171 | .map_err(|err| { | 234 | .map_err(|err| { |
172 | // Note: | 235 | // Note: |
173 | // The final goal we would like to make all parse_macro success, | 236 | // The final goal we would like to make all parse_macro success, |
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index f2558b579..9962112db 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -26,4 +26,4 @@ chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "177d713 | |||
26 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "177d71340acc7a7204a33115fc63075d86452179" } | 26 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "177d71340acc7a7204a33115fc63075d86452179" } |
27 | 27 | ||
28 | [dev-dependencies] | 28 | [dev-dependencies] |
29 | insta = "0.13.1" | 29 | insta = "0.15.0" |
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 42814941f..ffa78b046 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -484,6 +484,51 @@ fn bar() -> u32 {0} | |||
484 | } | 484 | } |
485 | 485 | ||
486 | #[test] | 486 | #[test] |
487 | fn infer_builtin_macros_include_concat_with_bad_env_should_failed() { | ||
488 | let (db, pos) = TestDB::with_position( | ||
489 | r#" | ||
490 | //- /main.rs | ||
491 | #[rustc_builtin_macro] | ||
492 | macro_rules! include {() => {}} | ||
493 | |||
494 | #[rustc_builtin_macro] | ||
495 | macro_rules! concat {() => {}} | ||
496 | |||
497 | #[rustc_builtin_macro] | ||
498 | macro_rules! env {() => {}} | ||
499 | |||
500 | include!(concat!(env!("OUT_DIR"), "/foo.rs")); | ||
501 | |||
502 | fn main() { | ||
503 | bar()<|>; | ||
504 | } | ||
505 | |||
506 | //- /foo.rs | ||
507 | fn bar() -> u32 {0} | ||
508 | "#, | ||
509 | ); | ||
510 | assert_eq!("{unknown}", type_at_pos(&db, pos)); | ||
511 | } | ||
512 | |||
513 | #[test] | ||
514 | fn infer_builtin_macros_include_itself_should_failed() { | ||
515 | let (db, pos) = TestDB::with_position( | ||
516 | r#" | ||
517 | //- /main.rs | ||
518 | #[rustc_builtin_macro] | ||
519 | macro_rules! include {() => {}} | ||
520 | |||
521 | include!("main.rs"); | ||
522 | |||
523 | fn main() { | ||
524 | 0<|> | ||
525 | } | ||
526 | "#, | ||
527 | ); | ||
528 | assert_eq!("i32", type_at_pos(&db, pos)); | ||
529 | } | ||
530 | |||
531 | #[test] | ||
487 | fn infer_builtin_macros_concat_with_lazy() { | 532 | fn infer_builtin_macros_concat_with_lazy() { |
488 | assert_snapshot!( | 533 | assert_snapshot!( |
489 | infer(r#" | 534 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 8de588790..6e1c8e42a 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs | |||
@@ -47,7 +47,7 @@ pub(crate) fn impls_for_trait_query( | |||
47 | // will only ever get called for a few crates near the root of the tree (the | 47 | // will only ever get called for a few crates near the root of the tree (the |
48 | // ones the user is editing), so this may actually be a waste of memory. I'm | 48 | // ones the user is editing), so this may actually be a waste of memory. I'm |
49 | // doing it like this mainly for simplicity for now. | 49 | // doing it like this mainly for simplicity for now. |
50 | for dep in db.crate_graph().dependencies(krate) { | 50 | for dep in &db.crate_graph()[krate].dependencies { |
51 | impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter()); | 51 | impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter()); |
52 | } | 52 | } |
53 | let crate_impl_defs = db.impls_in_crate(krate); | 53 | let crate_impl_defs = db.impls_in_crate(krate); |
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 410d8de62..7235c944c 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -35,4 +35,4 @@ ra_assists = { path = "../ra_assists" } | |||
35 | hir = { path = "../ra_hir", package = "ra_hir" } | 35 | hir = { path = "../ra_hir", package = "ra_hir" } |
36 | 36 | ||
37 | [dev-dependencies] | 37 | [dev-dependencies] |
38 | insta = "0.13.1" | 38 | insta = "0.15.0" |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 9145aa183..f275305e2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -38,7 +38,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
39 | for receiver in receiver.autoderef(ctx.db) { | 39 | for receiver in receiver.autoderef(ctx.db) { |
40 | for (field, ty) in receiver.fields(ctx.db) { | 40 | for (field, ty) in receiver.fields(ctx.db) { |
41 | if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | 41 | if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { |
42 | // Skip private field. FIXME: If the definition location of the | 42 | // Skip private field. FIXME: If the definition location of the |
43 | // field is editable, we should show the completion | 43 | // field is editable, we should show the completion |
44 | continue; | 44 | continue; |
@@ -53,11 +53,14 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
53 | } | 53 | } |
54 | 54 | ||
55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { | 56 | if let Some(krate) = ctx.krate { |
57 | let mut seen_methods = FxHashSet::default(); | 57 | let mut seen_methods = FxHashSet::default(); |
58 | let traits_in_scope = ctx.scope().traits_in_scope(); | 58 | let traits_in_scope = ctx.scope().traits_in_scope(); |
59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { | 59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { |
60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | 60 | if func.has_self_param(ctx.db) |
61 | && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | ||
62 | && seen_methods.insert(func.name(ctx.db)) | ||
63 | { | ||
61 | acc.add_function(ctx, func); | 64 | acc.add_function(ctx, func); |
62 | } | 65 | } |
63 | None::<()> | 66 | None::<()> |
@@ -308,6 +311,39 @@ mod tests { | |||
308 | } | 311 | } |
309 | 312 | ||
310 | #[test] | 313 | #[test] |
314 | fn test_method_completion_private() { | ||
315 | assert_debug_snapshot!( | ||
316 | do_ref_completion( | ||
317 | r" | ||
318 | struct A {} | ||
319 | mod m { | ||
320 | impl super::A { | ||
321 | fn private_method(&self) {} | ||
322 | pub(super) fn the_method(&self) {} | ||
323 | } | ||
324 | } | ||
325 | fn foo(a: A) { | ||
326 | a.<|> | ||
327 | } | ||
328 | ", | ||
329 | ), | ||
330 | @r###" | ||
331 | [ | ||
332 | CompletionItem { | ||
333 | label: "the_method()", | ||
334 | source_range: [256; 256), | ||
335 | delete: [256; 256), | ||
336 | insert: "the_method()$0", | ||
337 | kind: Method, | ||
338 | lookup: "the_method", | ||
339 | detail: "pub(super) fn the_method(&self)", | ||
340 | }, | ||
341 | ] | ||
342 | "### | ||
343 | ); | ||
344 | } | ||
345 | |||
346 | #[test] | ||
311 | fn test_trait_method_completion() { | 347 | fn test_trait_method_completion() { |
312 | assert_debug_snapshot!( | 348 | assert_debug_snapshot!( |
313 | do_ref_completion( | 349 | do_ref_completion( |
@@ -584,4 +620,102 @@ mod tests { | |||
584 | "### | 620 | "### |
585 | ); | 621 | ); |
586 | } | 622 | } |
623 | |||
624 | #[test] | ||
625 | fn works_in_simple_macro_1() { | ||
626 | assert_debug_snapshot!( | ||
627 | do_ref_completion( | ||
628 | r" | ||
629 | macro_rules! m { ($e:expr) => { $e } } | ||
630 | struct A { the_field: u32 } | ||
631 | fn foo(a: A) { | ||
632 | m!(a.x<|>) | ||
633 | } | ||
634 | ", | ||
635 | ), | ||
636 | @r###" | ||
637 | [ | ||
638 | CompletionItem { | ||
639 | label: "the_field", | ||
640 | source_range: [156; 157), | ||
641 | delete: [156; 157), | ||
642 | insert: "the_field", | ||
643 | kind: Field, | ||
644 | detail: "u32", | ||
645 | }, | ||
646 | ] | ||
647 | "### | ||
648 | ); | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn works_in_simple_macro_recursive() { | ||
653 | assert_debug_snapshot!( | ||
654 | do_ref_completion( | ||
655 | r" | ||
656 | macro_rules! m { ($e:expr) => { $e } } | ||
657 | struct A { the_field: u32 } | ||
658 | fn foo(a: A) { | ||
659 | m!(a.x<|>) | ||
660 | } | ||
661 | ", | ||
662 | ), | ||
663 | @r###" | ||
664 | [ | ||
665 | CompletionItem { | ||
666 | label: "the_field", | ||
667 | source_range: [156; 157), | ||
668 | delete: [156; 157), | ||
669 | insert: "the_field", | ||
670 | kind: Field, | ||
671 | detail: "u32", | ||
672 | }, | ||
673 | ] | ||
674 | "### | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn works_in_simple_macro_2() { | ||
680 | // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery | ||
681 | assert_debug_snapshot!( | ||
682 | do_ref_completion( | ||
683 | r" | ||
684 | macro_rules! m { ($e:expr) => { $e } } | ||
685 | struct A { the_field: u32 } | ||
686 | fn foo(a: A) { | ||
687 | m!(a.<|>) | ||
688 | } | ||
689 | ", | ||
690 | ), | ||
691 | @r###"[]"### | ||
692 | ); | ||
693 | } | ||
694 | |||
695 | #[test] | ||
696 | fn works_in_simple_macro_recursive_1() { | ||
697 | assert_debug_snapshot!( | ||
698 | do_ref_completion( | ||
699 | r" | ||
700 | macro_rules! m { ($e:expr) => { $e } } | ||
701 | struct A { the_field: u32 } | ||
702 | fn foo(a: A) { | ||
703 | m!(m!(m!(a.x<|>))) | ||
704 | } | ||
705 | ", | ||
706 | ), | ||
707 | @r###" | ||
708 | [ | ||
709 | CompletionItem { | ||
710 | label: "the_field", | ||
711 | source_range: [162; 163), | ||
712 | delete: [162; 163), | ||
713 | insert: "the_field", | ||
714 | kind: Field, | ||
715 | detail: "u32", | ||
716 | }, | ||
717 | ] | ||
718 | "### | ||
719 | ); | ||
720 | } | ||
587 | } | 721 | } |
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index eb7cd9ac2..e1c0ffb1f 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -79,6 +79,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
79 | } | 79 | } |
80 | 80 | ||
81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | 81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { |
82 | // FIXME move this to CompletionContext and make it handle macros | ||
82 | for node in leaf.parent().ancestors() { | 83 | for node in leaf.parent().ancestors() { |
83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | 84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { |
84 | break; | 85 | break; |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 1a9699466..3c4a70561 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Completion of paths, including when writing a single name. | 1 | //! Completion of paths, i.e. `some::prefix::<|>`. |
2 | 2 | ||
3 | use hir::{Adt, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
@@ -15,9 +15,10 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
15 | Some(PathResolution::Def(def)) => def, | 15 | Some(PathResolution::Def(def)) => def, |
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
18 | let context_module = ctx.scope().module(); | ||
18 | match def { | 19 | match def { |
19 | hir::ModuleDef::Module(module) => { | 20 | hir::ModuleDef::Module(module) => { |
20 | let module_scope = module.scope(ctx.db); | 21 | let module_scope = module.scope(ctx.db, context_module); |
21 | for (name, def) in module_scope { | 22 | for (name, def) in module_scope { |
22 | if ctx.use_item_syntax.is_some() { | 23 | if ctx.use_item_syntax.is_some() { |
23 | if let ScopeDef::Unknown = def { | 24 | if let ScopeDef::Unknown = def { |
@@ -47,10 +48,13 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
47 | }; | 48 | }; |
48 | // Iterate assoc types separately | 49 | // Iterate assoc types separately |
49 | // FIXME: complete T::AssocType | 50 | // FIXME: complete T::AssocType |
50 | let krate = ctx.module.map(|m| m.krate()); | 51 | let krate = ctx.krate; |
51 | if let Some(krate) = krate { | 52 | if let Some(krate) = krate { |
52 | let traits_in_scope = ctx.scope().traits_in_scope(); | 53 | let traits_in_scope = ctx.scope().traits_in_scope(); |
53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 54 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
55 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
56 | return None; | ||
57 | } | ||
54 | match item { | 58 | match item { |
55 | hir::AssocItem::Function(func) => { | 59 | hir::AssocItem::Function(func) => { |
56 | if !func.has_self_param(ctx.db) { | 60 | if !func.has_self_param(ctx.db) { |
@@ -64,6 +68,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
64 | }); | 68 | }); |
65 | 69 | ||
66 | ty.iterate_impl_items(ctx.db, krate, |item| { | 70 | ty.iterate_impl_items(ctx.db, krate, |item| { |
71 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
72 | return None; | ||
73 | } | ||
67 | match item { | 74 | match item { |
68 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} | 75 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} |
69 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | 76 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), |
@@ -74,6 +81,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
74 | } | 81 | } |
75 | hir::ModuleDef::Trait(t) => { | 82 | hir::ModuleDef::Trait(t) => { |
76 | for item in t.items(ctx.db) { | 83 | for item in t.items(ctx.db) { |
84 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
85 | continue; | ||
86 | } | ||
77 | match item { | 87 | match item { |
78 | hir::AssocItem::Function(func) => { | 88 | hir::AssocItem::Function(func) => { |
79 | if !func.has_self_param(ctx.db) { | 89 | if !func.has_self_param(ctx.db) { |
@@ -170,6 +180,41 @@ mod tests { | |||
170 | } | 180 | } |
171 | 181 | ||
172 | #[test] | 182 | #[test] |
183 | fn path_visibility() { | ||
184 | assert_debug_snapshot!( | ||
185 | do_reference_completion( | ||
186 | r" | ||
187 | use self::my::<|>; | ||
188 | |||
189 | mod my { | ||
190 | struct Bar; | ||
191 | pub struct Foo; | ||
192 | pub use Bar as PublicBar; | ||
193 | } | ||
194 | " | ||
195 | ), | ||
196 | @r###" | ||
197 | [ | ||
198 | CompletionItem { | ||
199 | label: "Foo", | ||
200 | source_range: [31; 31), | ||
201 | delete: [31; 31), | ||
202 | insert: "Foo", | ||
203 | kind: Struct, | ||
204 | }, | ||
205 | CompletionItem { | ||
206 | label: "PublicBar", | ||
207 | source_range: [31; 31), | ||
208 | delete: [31; 31), | ||
209 | insert: "PublicBar", | ||
210 | kind: Struct, | ||
211 | }, | ||
212 | ] | ||
213 | "### | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
173 | fn completes_use_item_starting_with_self() { | 218 | fn completes_use_item_starting_with_self() { |
174 | assert_debug_snapshot!( | 219 | assert_debug_snapshot!( |
175 | do_reference_completion( | 220 | do_reference_completion( |
@@ -177,7 +222,7 @@ mod tests { | |||
177 | use self::m::<|>; | 222 | use self::m::<|>; |
178 | 223 | ||
179 | mod m { | 224 | mod m { |
180 | struct Bar; | 225 | pub struct Bar; |
181 | } | 226 | } |
182 | " | 227 | " |
183 | ), | 228 | ), |
@@ -502,6 +547,60 @@ mod tests { | |||
502 | } | 547 | } |
503 | 548 | ||
504 | #[test] | 549 | #[test] |
550 | fn associated_item_visibility() { | ||
551 | assert_debug_snapshot!( | ||
552 | do_reference_completion( | ||
553 | " | ||
554 | //- /lib.rs | ||
555 | struct S; | ||
556 | |||
557 | mod m { | ||
558 | impl super::S { | ||
559 | pub(super) fn public_method() { } | ||
560 | fn private_method() { } | ||
561 | pub(super) type PublicType = u32; | ||
562 | type PrivateType = u32; | ||
563 | pub(super) const PUBLIC_CONST: u32 = 1; | ||
564 | const PRIVATE_CONST: u32 = 1; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | fn foo() { let _ = S::<|> } | ||
569 | " | ||
570 | ), | ||
571 | @r###" | ||
572 | [ | ||
573 | CompletionItem { | ||
574 | label: "PUBLIC_CONST", | ||
575 | source_range: [302; 302), | ||
576 | delete: [302; 302), | ||
577 | insert: "PUBLIC_CONST", | ||
578 | kind: Const, | ||
579 | detail: "pub(super) const PUBLIC_CONST: u32 = 1;", | ||
580 | }, | ||
581 | CompletionItem { | ||
582 | label: "PublicType", | ||
583 | source_range: [302; 302), | ||
584 | delete: [302; 302), | ||
585 | insert: "PublicType", | ||
586 | kind: TypeAlias, | ||
587 | detail: "pub(super) type PublicType = u32;", | ||
588 | }, | ||
589 | CompletionItem { | ||
590 | label: "public_method()", | ||
591 | source_range: [302; 302), | ||
592 | delete: [302; 302), | ||
593 | insert: "public_method()$0", | ||
594 | kind: Function, | ||
595 | lookup: "public_method", | ||
596 | detail: "pub(super) fn public_method()", | ||
597 | }, | ||
598 | ] | ||
599 | "### | ||
600 | ); | ||
601 | } | ||
602 | |||
603 | #[test] | ||
505 | fn completes_enum_associated_method() { | 604 | fn completes_enum_associated_method() { |
506 | assert_debug_snapshot!( | 605 | assert_debug_snapshot!( |
507 | do_reference_completion( | 606 | do_reference_completion( |
@@ -835,4 +934,37 @@ mod tests { | |||
835 | "### | 934 | "### |
836 | ); | 935 | ); |
837 | } | 936 | } |
937 | |||
938 | #[test] | ||
939 | fn completes_in_simple_macro_call() { | ||
940 | let completions = do_reference_completion( | ||
941 | r#" | ||
942 | macro_rules! m { ($e:expr) => { $e } } | ||
943 | fn main() { m!(self::f<|>); } | ||
944 | fn foo() {} | ||
945 | "#, | ||
946 | ); | ||
947 | assert_debug_snapshot!(completions, @r###" | ||
948 | [ | ||
949 | CompletionItem { | ||
950 | label: "foo()", | ||
951 | source_range: [93; 94), | ||
952 | delete: [93; 94), | ||
953 | insert: "foo()$0", | ||
954 | kind: Function, | ||
955 | lookup: "foo", | ||
956 | detail: "fn foo()", | ||
957 | }, | ||
958 | CompletionItem { | ||
959 | label: "main()", | ||
960 | source_range: [93; 94), | ||
961 | delete: [93; 94), | ||
962 | insert: "main()$0", | ||
963 | kind: Function, | ||
964 | lookup: "main", | ||
965 | detail: "fn main()", | ||
966 | }, | ||
967 | ] | ||
968 | "###); | ||
969 | } | ||
838 | } | 970 | } |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index c2c6ca002..fa8aeceda 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -86,4 +86,22 @@ mod tests { | |||
86 | ] | 86 | ] |
87 | "###); | 87 | "###); |
88 | } | 88 | } |
89 | |||
90 | #[test] | ||
91 | fn completes_in_simple_macro_call() { | ||
92 | // FIXME: doesn't work yet because of missing error recovery in macro expansion | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | enum E { X } | ||
97 | |||
98 | fn foo() { | ||
99 | m!(match E::X { | ||
100 | <|> | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###"[]"###); | ||
106 | } | ||
89 | } | 107 | } |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 8a74f993a..65ecea125 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -67,8 +67,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
67 | 67 | ||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | 68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { |
69 | let edit = { | 69 | let edit = { |
70 | let receiver_range = | 70 | let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); |
71 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | 71 | let receiver_range = ctx.sema.original_range(receiver_syntax).range; |
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | 72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); |
73 | TextEdit::replace(delete_range, snippet.to_string()) | 73 | TextEdit::replace(delete_range, snippet.to_string()) |
74 | }; | 74 | }; |
@@ -279,4 +279,65 @@ mod tests { | |||
279 | "### | 279 | "### |
280 | ); | 280 | ); |
281 | } | 281 | } |
282 | |||
283 | #[test] | ||
284 | fn works_in_simple_macro() { | ||
285 | assert_debug_snapshot!( | ||
286 | do_postfix_completion( | ||
287 | r#" | ||
288 | macro_rules! m { ($e:expr) => { $e } } | ||
289 | fn main() { | ||
290 | let bar: u8 = 12; | ||
291 | m!(bar.b<|>) | ||
292 | } | ||
293 | "#, | ||
294 | ), | ||
295 | @r###" | ||
296 | [ | ||
297 | CompletionItem { | ||
298 | label: "box", | ||
299 | source_range: [149; 150), | ||
300 | delete: [145; 150), | ||
301 | insert: "Box::new(bar)", | ||
302 | detail: "Box::new(expr)", | ||
303 | }, | ||
304 | CompletionItem { | ||
305 | label: "dbg", | ||
306 | source_range: [149; 150), | ||
307 | delete: [145; 150), | ||
308 | insert: "dbg!(bar)", | ||
309 | detail: "dbg!(expr)", | ||
310 | }, | ||
311 | CompletionItem { | ||
312 | label: "match", | ||
313 | source_range: [149; 150), | ||
314 | delete: [145; 150), | ||
315 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
316 | detail: "match expr {}", | ||
317 | }, | ||
318 | CompletionItem { | ||
319 | label: "not", | ||
320 | source_range: [149; 150), | ||
321 | delete: [145; 150), | ||
322 | insert: "!bar", | ||
323 | detail: "!expr", | ||
324 | }, | ||
325 | CompletionItem { | ||
326 | label: "ref", | ||
327 | source_range: [149; 150), | ||
328 | delete: [145; 150), | ||
329 | insert: "&bar", | ||
330 | detail: "&expr", | ||
331 | }, | ||
332 | CompletionItem { | ||
333 | label: "refm", | ||
334 | source_range: [149; 150), | ||
335 | delete: [145; 150), | ||
336 | insert: "&mut bar", | ||
337 | detail: "&mut expr", | ||
338 | }, | ||
339 | ] | ||
340 | "### | ||
341 | ); | ||
342 | } | ||
282 | } | 343 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index f98353d76..be6e4194f 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs | |||
@@ -153,4 +153,29 @@ mod tests { | |||
153 | ] | 153 | ] |
154 | "###); | 154 | "###); |
155 | } | 155 | } |
156 | |||
157 | #[test] | ||
158 | fn test_record_literal_field_in_simple_macro() { | ||
159 | let completions = complete( | ||
160 | r" | ||
161 | macro_rules! m { ($e:expr) => { $e } } | ||
162 | struct A { the_field: u32 } | ||
163 | fn foo() { | ||
164 | m!(A { the<|> }) | ||
165 | } | ||
166 | ", | ||
167 | ); | ||
168 | assert_debug_snapshot!(completions, @r###" | ||
169 | [ | ||
170 | CompletionItem { | ||
171 | label: "the_field", | ||
172 | source_range: [137; 140), | ||
173 | delete: [137; 140), | ||
174 | insert: "the_field", | ||
175 | kind: Field, | ||
176 | detail: "u32", | ||
177 | }, | ||
178 | ] | ||
179 | "###); | ||
180 | } | ||
156 | } | 181 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index 9bdeae49f..687c57d3e 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs | |||
@@ -87,4 +87,32 @@ mod tests { | |||
87 | ] | 87 | ] |
88 | "###); | 88 | "###); |
89 | } | 89 | } |
90 | |||
91 | #[test] | ||
92 | fn test_record_pattern_field_in_simple_macro() { | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | struct S { foo: u32 } | ||
97 | |||
98 | fn process(f: S) { | ||
99 | m!(match f { | ||
100 | S { f<|>: 92 } => (), | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###" | ||
106 | [ | ||
107 | CompletionItem { | ||
108 | label: "foo", | ||
109 | source_range: [171; 172), | ||
110 | delete: [171; 172), | ||
111 | insert: "foo", | ||
112 | kind: Field, | ||
113 | detail: "u32", | ||
114 | }, | ||
115 | ] | ||
116 | "###); | ||
117 | } | ||
90 | } | 118 | } |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 2b9a0e556..eb3c8cf1b 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
@@ -797,4 +797,72 @@ mod tests { | |||
797 | "### | 797 | "### |
798 | ) | 798 | ) |
799 | } | 799 | } |
800 | |||
801 | #[test] | ||
802 | fn completes_in_simple_macro_1() { | ||
803 | assert_debug_snapshot!( | ||
804 | do_reference_completion( | ||
805 | r" | ||
806 | macro_rules! m { ($e:expr) => { $e } } | ||
807 | fn quux(x: i32) { | ||
808 | let y = 92; | ||
809 | m!(<|>); | ||
810 | } | ||
811 | " | ||
812 | ), | ||
813 | @"[]" | ||
814 | ); | ||
815 | } | ||
816 | |||
817 | #[test] | ||
818 | fn completes_in_simple_macro_2() { | ||
819 | assert_debug_snapshot!( | ||
820 | do_reference_completion( | ||
821 | r" | ||
822 | macro_rules! m { ($e:expr) => { $e } } | ||
823 | fn quux(x: i32) { | ||
824 | let y = 92; | ||
825 | m!(x<|>); | ||
826 | } | ||
827 | " | ||
828 | ), | ||
829 | @r###" | ||
830 | [ | ||
831 | CompletionItem { | ||
832 | label: "m!", | ||
833 | source_range: [145; 146), | ||
834 | delete: [145; 146), | ||
835 | insert: "m!($0)", | ||
836 | kind: Macro, | ||
837 | detail: "macro_rules! m", | ||
838 | }, | ||
839 | CompletionItem { | ||
840 | label: "quux(…)", | ||
841 | source_range: [145; 146), | ||
842 | delete: [145; 146), | ||
843 | insert: "quux(${1:x})$0", | ||
844 | kind: Function, | ||
845 | lookup: "quux", | ||
846 | detail: "fn quux(x: i32)", | ||
847 | }, | ||
848 | CompletionItem { | ||
849 | label: "x", | ||
850 | source_range: [145; 146), | ||
851 | delete: [145; 146), | ||
852 | insert: "x", | ||
853 | kind: Binding, | ||
854 | detail: "i32", | ||
855 | }, | ||
856 | CompletionItem { | ||
857 | label: "y", | ||
858 | source_range: [145; 146), | ||
859 | delete: [145; 146), | ||
860 | insert: "y", | ||
861 | kind: Binding, | ||
862 | detail: "i32", | ||
863 | }, | ||
864 | ] | ||
865 | "### | ||
866 | ); | ||
867 | } | ||
800 | } | 868 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 9aa5a705d..40535c09e 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
8 | ast, AstNode, SourceFile, | 8 | ast, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | 10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
11 | }; | 11 | }; |
@@ -20,8 +20,11 @@ pub(crate) struct CompletionContext<'a> { | |||
20 | pub(super) sema: Semantics<'a, RootDatabase>, | 20 | pub(super) sema: Semantics<'a, RootDatabase>, |
21 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
22 | pub(super) offset: TextUnit, | 22 | pub(super) offset: TextUnit, |
23 | /// The token before the cursor, in the original file. | ||
24 | pub(super) original_token: SyntaxToken, | ||
25 | /// The token before the cursor, in the macro-expanded file. | ||
23 | pub(super) token: SyntaxToken, | 26 | pub(super) token: SyntaxToken, |
24 | pub(super) module: Option<hir::Module>, | 27 | pub(super) krate: Option<hir::Crate>, |
25 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 28 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
26 | pub(super) function_syntax: Option<ast::FnDef>, | 29 | pub(super) function_syntax: Option<ast::FnDef>, |
27 | pub(super) use_item_syntax: Option<ast::UseItem>, | 30 | pub(super) use_item_syntax: Option<ast::UseItem>, |
@@ -67,15 +70,20 @@ impl<'a> CompletionContext<'a> { | |||
67 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 70 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); |
68 | parse.reparse(&edit).tree() | 71 | parse.reparse(&edit).tree() |
69 | }; | 72 | }; |
73 | let fake_ident_token = | ||
74 | file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); | ||
70 | 75 | ||
71 | let module = sema.to_module_def(position.file_id); | 76 | let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); |
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | 77 | let original_token = |
78 | original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
79 | let token = sema.descend_into_macros(original_token.clone()); | ||
73 | let mut ctx = CompletionContext { | 80 | let mut ctx = CompletionContext { |
74 | sema, | 81 | sema, |
75 | db, | 82 | db, |
83 | original_token, | ||
76 | token, | 84 | token, |
77 | offset: position.offset, | 85 | offset: position.offset, |
78 | module, | 86 | krate, |
79 | name_ref_syntax: None, | 87 | name_ref_syntax: None, |
80 | function_syntax: None, | 88 | function_syntax: None, |
81 | use_item_syntax: None, | 89 | use_item_syntax: None, |
@@ -95,15 +103,57 @@ impl<'a> CompletionContext<'a> { | |||
95 | has_type_args: false, | 103 | has_type_args: false, |
96 | dot_receiver_is_ambiguous_float_literal: false, | 104 | dot_receiver_is_ambiguous_float_literal: false, |
97 | }; | 105 | }; |
98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); | 106 | |
107 | let mut original_file = original_file.syntax().clone(); | ||
108 | let mut hypothetical_file = file_with_fake_ident.syntax().clone(); | ||
109 | let mut offset = position.offset; | ||
110 | let mut fake_ident_token = fake_ident_token; | ||
111 | |||
112 | // Are we inside a macro call? | ||
113 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( | ||
114 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), | ||
115 | find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), | ||
116 | ) { | ||
117 | if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) | ||
118 | != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) | ||
119 | { | ||
120 | break; | ||
121 | } | ||
122 | let hypothetical_args = match macro_call_with_fake_ident.token_tree() { | ||
123 | Some(tt) => tt, | ||
124 | None => break, | ||
125 | }; | ||
126 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | ||
127 | ctx.sema.expand(&actual_macro_call), | ||
128 | ctx.sema.expand_hypothetical( | ||
129 | &actual_macro_call, | ||
130 | &hypothetical_args, | ||
131 | fake_ident_token, | ||
132 | ), | ||
133 | ) { | ||
134 | let new_offset = hypothetical_expansion.1.text_range().start(); | ||
135 | if new_offset >= actual_expansion.text_range().end() { | ||
136 | break; | ||
137 | } | ||
138 | original_file = actual_expansion; | ||
139 | hypothetical_file = hypothetical_expansion.0; | ||
140 | fake_ident_token = hypothetical_expansion.1; | ||
141 | offset = new_offset; | ||
142 | } else { | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | ctx.fill(&original_file, hypothetical_file, offset); | ||
99 | Some(ctx) | 148 | Some(ctx) |
100 | } | 149 | } |
101 | 150 | ||
102 | // The range of the identifier that is being completed. | 151 | // The range of the identifier that is being completed. |
103 | pub(crate) fn source_range(&self) -> TextRange { | 152 | pub(crate) fn source_range(&self) -> TextRange { |
153 | // check kind of macro-expanded token, but use range of original token | ||
104 | match self.token.kind() { | 154 | match self.token.kind() { |
105 | // workaroud when completion is triggered by trigger characters. | 155 | // workaroud when completion is triggered by trigger characters. |
106 | IDENT => self.token.text_range(), | 156 | IDENT => self.original_token.text_range(), |
107 | _ => TextRange::offset_len(self.offset, 0.into()), | 157 | _ => TextRange::offset_len(self.offset, 0.into()), |
108 | } | 158 | } |
109 | } | 159 | } |
@@ -114,27 +164,24 @@ impl<'a> CompletionContext<'a> { | |||
114 | 164 | ||
115 | fn fill( | 165 | fn fill( |
116 | &mut self, | 166 | &mut self, |
117 | original_file: &ast::SourceFile, | 167 | original_file: &SyntaxNode, |
118 | file_with_fake_ident: ast::SourceFile, | 168 | file_with_fake_ident: SyntaxNode, |
119 | offset: TextUnit, | 169 | offset: TextUnit, |
120 | ) { | 170 | ) { |
121 | // First, let's try to complete a reference to some declaration. | 171 | // First, let's try to complete a reference to some declaration. |
122 | if let Some(name_ref) = | 172 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
123 | find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset) | ||
124 | { | ||
125 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 173 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
126 | // See RFC#1685. | 174 | // See RFC#1685. |
127 | if is_node::<ast::Param>(name_ref.syntax()) { | 175 | if is_node::<ast::Param>(name_ref.syntax()) { |
128 | self.is_param = true; | 176 | self.is_param = true; |
129 | return; | 177 | return; |
130 | } | 178 | } |
131 | self.classify_name_ref(original_file, name_ref); | 179 | self.classify_name_ref(original_file, name_ref, offset); |
132 | } | 180 | } |
133 | 181 | ||
134 | // Otherwise, see if this is a declaration. We can use heuristics to | 182 | // Otherwise, see if this is a declaration. We can use heuristics to |
135 | // suggest declaration names, see `CompletionKind::Magic`. | 183 | // suggest declaration names, see `CompletionKind::Magic`. |
136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) | 184 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { |
137 | { | ||
138 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 185 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
139 | let parent = bind_pat.syntax().parent(); | 186 | let parent = bind_pat.syntax().parent(); |
140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 187 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -148,23 +195,29 @@ impl<'a> CompletionContext<'a> { | |||
148 | return; | 195 | return; |
149 | } | 196 | } |
150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 197 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
151 | self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); | 198 | self.record_lit_pat = |
199 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
152 | } | 200 | } |
153 | } | 201 | } |
154 | } | 202 | } |
155 | 203 | ||
156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { | 204 | fn classify_name_ref( |
205 | &mut self, | ||
206 | original_file: &SyntaxNode, | ||
207 | name_ref: ast::NameRef, | ||
208 | offset: TextUnit, | ||
209 | ) { | ||
157 | self.name_ref_syntax = | 210 | self.name_ref_syntax = |
158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 211 | find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); |
159 | let name_range = name_ref.syntax().text_range(); | 212 | let name_range = name_ref.syntax().text_range(); |
160 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | 213 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { |
161 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 214 | self.record_lit_syntax = |
215 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
162 | } | 216 | } |
163 | 217 | ||
164 | self.impl_def = self | 218 | self.impl_def = self |
165 | .token | 219 | .sema |
166 | .parent() | 220 | .ancestors_with_macros(self.token.parent()) |
167 | .ancestors() | ||
168 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 221 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
169 | .find_map(ast::ImplDef::cast); | 222 | .find_map(ast::ImplDef::cast); |
170 | 223 | ||
@@ -183,12 +236,12 @@ impl<'a> CompletionContext<'a> { | |||
183 | _ => (), | 236 | _ => (), |
184 | } | 237 | } |
185 | 238 | ||
186 | self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); | 239 | self.use_item_syntax = |
240 | self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); | ||
187 | 241 | ||
188 | self.function_syntax = self | 242 | self.function_syntax = self |
189 | .token | 243 | .sema |
190 | .parent() | 244 | .ancestors_with_macros(self.token.parent()) |
191 | .ancestors() | ||
192 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 245 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
193 | .find_map(ast::FnDef::cast); | 246 | .find_map(ast::FnDef::cast); |
194 | 247 | ||
@@ -242,7 +295,7 @@ impl<'a> CompletionContext<'a> { | |||
242 | 295 | ||
243 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | 296 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { |
244 | if let Some(if_expr) = | 297 | if let Some(if_expr) = |
245 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | 298 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) |
246 | { | 299 | { |
247 | if if_expr.syntax().text_range().end() | 300 | if if_expr.syntax().text_range().end() |
248 | < name_ref.syntax().text_range().start() | 301 | < name_ref.syntax().text_range().start() |
@@ -259,7 +312,7 @@ impl<'a> CompletionContext<'a> { | |||
259 | self.dot_receiver = field_expr | 312 | self.dot_receiver = field_expr |
260 | .expr() | 313 | .expr() |
261 | .map(|e| e.syntax().text_range()) | 314 | .map(|e| e.syntax().text_range()) |
262 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 315 | .and_then(|r| find_node_with_range(original_file, r)); |
263 | self.dot_receiver_is_ambiguous_float_literal = | 316 | self.dot_receiver_is_ambiguous_float_literal = |
264 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { | 317 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { |
265 | match l.kind() { | 318 | match l.kind() { |
@@ -275,7 +328,7 @@ impl<'a> CompletionContext<'a> { | |||
275 | self.dot_receiver = method_call_expr | 328 | self.dot_receiver = method_call_expr |
276 | .expr() | 329 | .expr() |
277 | .map(|e| e.syntax().text_range()) | 330 | .map(|e| e.syntax().text_range()) |
278 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 331 | .and_then(|r| find_node_with_range(original_file, r)); |
279 | self.is_call = true; | 332 | self.is_call = true; |
280 | } | 333 | } |
281 | } | 334 | } |
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 1c26a8697..eaeaaa2b4 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs | |||
@@ -68,17 +68,23 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String { | |||
68 | } | 68 | } |
69 | 69 | ||
70 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | 70 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { |
71 | rust_code_markup_with_doc::<_, &str>(val, None) | 71 | rust_code_markup_with_doc::<_, &str>(val, None, None) |
72 | } | 72 | } |
73 | 73 | ||
74 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | 74 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>( |
75 | val: CODE, | ||
76 | doc: Option<DOC>, | ||
77 | mod_path: Option<String>, | ||
78 | ) -> String | ||
75 | where | 79 | where |
76 | CODE: AsRef<str>, | 80 | CODE: AsRef<str>, |
77 | DOC: AsRef<str>, | 81 | DOC: AsRef<str>, |
78 | { | 82 | { |
83 | let mod_path = | ||
84 | mod_path.filter(|path| !path.is_empty()).map(|path| path + "\n").unwrap_or_default(); | ||
79 | if let Some(doc) = doc { | 85 | if let Some(doc) = doc { |
80 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | 86 | format!("```rust\n{}{}\n```\n\n{}", mod_path, val.as_ref(), doc.as_ref()) |
81 | } else { | 87 | } else { |
82 | format!("```rust\n{}\n```", val.as_ref()) | 88 | format!("```rust\n{}{}\n```", mod_path, val.as_ref()) |
83 | } | 89 | } |
84 | } | 90 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index e9c682557..25e038a55 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Adt, HasSource, HirDisplay, Semantics}; | 3 | use hir::{ |
4 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, | ||
5 | ModuleSource, Semantics, | ||
6 | }; | ||
7 | use ra_db::SourceDatabase; | ||
4 | use ra_ide_db::{ | 8 | use ra_ide_db::{ |
5 | defs::{classify_name, classify_name_ref, Definition}, | 9 | defs::{classify_name, classify_name_ref, Definition}, |
6 | RootDatabase, | 10 | RootDatabase, |
@@ -16,6 +20,8 @@ use crate::{ | |||
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 20 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
17 | FilePosition, RangeInfo, | 21 | FilePosition, RangeInfo, |
18 | }; | 22 | }; |
23 | use itertools::Itertools; | ||
24 | use std::iter::once; | ||
19 | 25 | ||
20 | /// Contains the results when hovering over an item | 26 | /// Contains the results when hovering over an item |
21 | #[derive(Debug, Clone)] | 27 | #[derive(Debug, Clone)] |
@@ -83,44 +89,86 @@ impl HoverResult { | |||
83 | } | 89 | } |
84 | } | 90 | } |
85 | 91 | ||
86 | fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> { | 92 | fn hover_text( |
87 | match (desc, docs) { | 93 | docs: Option<String>, |
88 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | 94 | desc: Option<String>, |
89 | (None, Some(docs)) => Some(docs), | 95 | mod_path: Option<String>, |
96 | ) -> Option<String> { | ||
97 | match (desc, docs, mod_path) { | ||
98 | (Some(desc), docs, mod_path) => Some(rust_code_markup_with_doc(desc, docs, mod_path)), | ||
99 | (None, Some(docs), _) => Some(docs), | ||
100 | _ => None, | ||
101 | } | ||
102 | } | ||
103 | |||
104 | fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
105 | match def { | ||
106 | Definition::StructField(f) => Some(f.parent_def(db).name(db)), | ||
107 | Definition::Local(l) => l.parent(db).name(db), | ||
108 | Definition::ModuleDef(md) => match md { | ||
109 | ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { | ||
110 | AssocItemContainer::Trait(t) => Some(t.name(db)), | ||
111 | AssocItemContainer::ImplDef(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
112 | }, | ||
113 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), | ||
114 | _ => None, | ||
115 | }, | ||
116 | Definition::SelfType(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
90 | _ => None, | 117 | _ => None, |
91 | } | 118 | } |
119 | .map(|name| name.to_string()) | ||
120 | } | ||
121 | |||
122 | fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
123 | let mod_path = def.module(db).map(|module| { | ||
124 | once(db.crate_graph()[module.krate().into()].display_name.clone()) | ||
125 | .chain( | ||
126 | module | ||
127 | .path_to_root(db) | ||
128 | .into_iter() | ||
129 | .rev() | ||
130 | .map(|it| it.name(db).map(|name| name.to_string())), | ||
131 | ) | ||
132 | .chain(once(definition_owner_name(db, def))) | ||
133 | .flatten() | ||
134 | .join("::") | ||
135 | }); | ||
136 | mod_path | ||
92 | } | 137 | } |
93 | 138 | ||
94 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { | 139 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { |
140 | let mod_path = determine_mod_path(db, &def); | ||
95 | return match def { | 141 | return match def { |
96 | Definition::Macro(it) => { | 142 | Definition::Macro(it) => { |
97 | let src = it.source(db); | 143 | let src = it.source(db); |
98 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value))) | 144 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) |
99 | } | 145 | } |
100 | Definition::StructField(it) => { | 146 | Definition::StructField(it) => { |
101 | let src = it.source(db); | 147 | let src = it.source(db); |
102 | match src.value { | 148 | match src.value { |
103 | hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()), | 149 | FieldSource::Named(it) => { |
150 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) | ||
151 | } | ||
104 | _ => None, | 152 | _ => None, |
105 | } | 153 | } |
106 | } | 154 | } |
107 | Definition::ModuleDef(it) => match it { | 155 | Definition::ModuleDef(it) => match it { |
108 | hir::ModuleDef::Module(it) => match it.definition_source(db).value { | 156 | ModuleDef::Module(it) => match it.definition_source(db).value { |
109 | hir::ModuleSource::Module(it) => { | 157 | ModuleSource::Module(it) => { |
110 | hover_text(it.doc_comment_text(), it.short_label()) | 158 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) |
111 | } | 159 | } |
112 | _ => None, | 160 | _ => None, |
113 | }, | 161 | }, |
114 | hir::ModuleDef::Function(it) => from_def_source(db, it), | 162 | ModuleDef::Function(it) => from_def_source(db, it, mod_path), |
115 | hir::ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it), | 163 | ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path), |
116 | hir::ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it), | 164 | ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path), |
117 | hir::ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it), | 165 | ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it, mod_path), |
118 | hir::ModuleDef::EnumVariant(it) => from_def_source(db, it), | 166 | ModuleDef::EnumVariant(it) => from_def_source(db, it, mod_path), |
119 | hir::ModuleDef::Const(it) => from_def_source(db, it), | 167 | ModuleDef::Const(it) => from_def_source(db, it, mod_path), |
120 | hir::ModuleDef::Static(it) => from_def_source(db, it), | 168 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), |
121 | hir::ModuleDef::Trait(it) => from_def_source(db, it), | 169 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), |
122 | hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), | 170 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), |
123 | hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), | 171 | ModuleDef::BuiltinType(it) => Some(it.to_string()), |
124 | }, | 172 | }, |
125 | Definition::Local(it) => { | 173 | Definition::Local(it) => { |
126 | Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())) | 174 | Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())) |
@@ -131,13 +179,13 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin | |||
131 | } | 179 | } |
132 | }; | 180 | }; |
133 | 181 | ||
134 | fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String> | 182 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> |
135 | where | 183 | where |
136 | D: HasSource<Ast = A>, | 184 | D: HasSource<Ast = A>, |
137 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, | 185 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, |
138 | { | 186 | { |
139 | let src = def.source(db); | 187 | let src = def.source(db); |
140 | hover_text(src.value.doc_comment_text(), src.value.short_label()) | 188 | hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) |
141 | } | 189 | } |
142 | } | 190 | } |
143 | 191 | ||
@@ -345,7 +393,7 @@ mod tests { | |||
345 | }; | 393 | }; |
346 | } | 394 | } |
347 | "#, | 395 | "#, |
348 | &["field_a: u32"], | 396 | &["Foo\nfield_a: u32"], |
349 | ); | 397 | ); |
350 | 398 | ||
351 | // Hovering over the field in the definition | 399 | // Hovering over the field in the definition |
@@ -362,7 +410,7 @@ mod tests { | |||
362 | }; | 410 | }; |
363 | } | 411 | } |
364 | "#, | 412 | "#, |
365 | &["field_a: u32"], | 413 | &["Foo\nfield_a: u32"], |
366 | ); | 414 | ); |
367 | } | 415 | } |
368 | 416 | ||
@@ -415,7 +463,7 @@ fn main() { | |||
415 | ", | 463 | ", |
416 | ); | 464 | ); |
417 | let hover = analysis.hover(position).unwrap().unwrap(); | 465 | let hover = analysis.hover(position).unwrap().unwrap(); |
418 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Some")); | 466 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); |
419 | 467 | ||
420 | let (analysis, position) = single_file_with_position( | 468 | let (analysis, position) = single_file_with_position( |
421 | " | 469 | " |
@@ -442,6 +490,7 @@ fn main() { | |||
442 | } | 490 | } |
443 | "#, | 491 | "#, |
444 | &[" | 492 | &[" |
493 | Option | ||
445 | None | 494 | None |
446 | ``` | 495 | ``` |
447 | 496 | ||
@@ -462,6 +511,7 @@ The None variant | |||
462 | } | 511 | } |
463 | "#, | 512 | "#, |
464 | &[" | 513 | &[" |
514 | Option | ||
465 | Some | 515 | Some |
466 | ``` | 516 | ``` |
467 | 517 | ||
@@ -528,21 +578,23 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
528 | fn test_hover_infer_associated_method_exact() { | 578 | fn test_hover_infer_associated_method_exact() { |
529 | let (analysis, position) = single_file_with_position( | 579 | let (analysis, position) = single_file_with_position( |
530 | " | 580 | " |
531 | struct Thing { x: u32 } | 581 | mod wrapper { |
582 | struct Thing { x: u32 } | ||
532 | 583 | ||
533 | impl Thing { | 584 | impl Thing { |
534 | fn new() -> Thing { | 585 | fn new() -> Thing { |
535 | Thing { x: 0 } | 586 | Thing { x: 0 } |
587 | } | ||
536 | } | 588 | } |
537 | } | 589 | } |
538 | 590 | ||
539 | fn main() { | 591 | fn main() { |
540 | let foo_test = Thing::new<|>(); | 592 | let foo_test = wrapper::Thing::new<|>(); |
541 | } | 593 | } |
542 | ", | 594 | ", |
543 | ); | 595 | ); |
544 | let hover = analysis.hover(position).unwrap().unwrap(); | 596 | let hover = analysis.hover(position).unwrap().unwrap(); |
545 | assert_eq!(trim_markup_opt(hover.info.first()), Some("fn new() -> Thing")); | 597 | assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); |
546 | assert_eq!(hover.info.is_exact(), true); | 598 | assert_eq!(hover.info.is_exact(), true); |
547 | } | 599 | } |
548 | 600 | ||
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 69098a630..cf0cbdbd0 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -119,6 +119,12 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ | |||
119 | return true; | 119 | return true; |
120 | } | 120 | } |
121 | 121 | ||
122 | if let Some(Adt::Struct(s)) = pat_ty.as_adt() { | ||
123 | if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.syntax().to_string() { | ||
124 | return true; | ||
125 | } | ||
126 | } | ||
127 | |||
122 | for node in bind_pat.syntax().ancestors() { | 128 | for node in bind_pat.syntax().ancestors() { |
123 | match_ast! { | 129 | match_ast! { |
124 | match node { | 130 | match node { |
@@ -943,4 +949,30 @@ fn main() { | |||
943 | "### | 949 | "### |
944 | ); | 950 | ); |
945 | } | 951 | } |
952 | |||
953 | #[test] | ||
954 | fn unit_structs_have_no_type_hints() { | ||
955 | let (analysis, file_id) = single_file( | ||
956 | r#" | ||
957 | enum CustomResult<T, E> { | ||
958 | Ok(T), | ||
959 | Err(E), | ||
960 | } | ||
961 | use CustomResult::*; | ||
962 | |||
963 | struct SyntheticSyntax; | ||
964 | |||
965 | fn main() { | ||
966 | match Ok(()) { | ||
967 | Ok(_) => (), | ||
968 | Err(SyntheticSyntax) => (), | ||
969 | } | ||
970 | }"#, | ||
971 | ); | ||
972 | |||
973 | assert_debug_snapshot!(analysis.inlay_hints(file_id, Some(8)).unwrap(), @r###" | ||
974 | [] | ||
975 | "### | ||
976 | ); | ||
977 | } | ||
946 | } | 978 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 4dfe0553e..c60e86aea 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -211,7 +211,13 @@ impl Analysis { | |||
211 | // Default to enable test for single file. | 211 | // Default to enable test for single file. |
212 | let mut cfg_options = CfgOptions::default(); | 212 | let mut cfg_options = CfgOptions::default(); |
213 | cfg_options.insert_atom("test".into()); | 213 | cfg_options.insert_atom("test".into()); |
214 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options, Env::default()); | 214 | crate_graph.add_crate_root( |
215 | file_id, | ||
216 | Edition::Edition2018, | ||
217 | None, | ||
218 | cfg_options, | ||
219 | Env::default(), | ||
220 | ); | ||
215 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 221 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
216 | change.set_crate_graph(crate_graph); | 222 | change.set_crate_graph(crate_graph); |
217 | host.apply_change(change); | 223 | host.apply_change(change); |
@@ -415,12 +421,12 @@ impl Analysis { | |||
415 | 421 | ||
416 | /// Returns the edition of the given crate. | 422 | /// Returns the edition of the given crate. |
417 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { | 423 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { |
418 | self.with_db(|db| db.crate_graph().edition(crate_id)) | 424 | self.with_db(|db| db.crate_graph()[crate_id].edition) |
419 | } | 425 | } |
420 | 426 | ||
421 | /// Returns the root file of the given crate. | 427 | /// Returns the root file of the given crate. |
422 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 428 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { |
423 | self.with_db(|db| db.crate_graph().crate_root(crate_id)) | 429 | self.with_db(|db| db.crate_graph()[crate_id].root_file_id) |
424 | } | 430 | } |
425 | 431 | ||
426 | /// Returns the set of possible targets to run for the current file. | 432 | /// Returns the set of possible targets to run for the current file. |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index f4cd6deb7..90f84b052 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -99,13 +99,19 @@ impl MockAnalysis { | |||
99 | root_crate = Some(crate_graph.add_crate_root( | 99 | root_crate = Some(crate_graph.add_crate_root( |
100 | file_id, | 100 | file_id, |
101 | Edition2018, | 101 | Edition2018, |
102 | None, | ||
102 | cfg_options, | 103 | cfg_options, |
103 | Env::default(), | 104 | Env::default(), |
104 | )); | 105 | )); |
105 | } else if path.ends_with("/lib.rs") { | 106 | } else if path.ends_with("/lib.rs") { |
106 | let other_crate = | ||
107 | crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); | ||
108 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 107 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
108 | let other_crate = crate_graph.add_crate_root( | ||
109 | file_id, | ||
110 | Edition2018, | ||
111 | Some(crate_name.to_owned()), | ||
112 | cfg_options, | ||
113 | Env::default(), | ||
114 | ); | ||
109 | if let Some(root_crate) = root_crate { | 115 | if let Some(root_crate) = root_crate { |
110 | crate_graph | 116 | crate_graph |
111 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) | 117 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 2c4bdb039..b73cefd97 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -133,6 +133,7 @@ mod tests { | |||
133 | let crate_id = crate_graph.add_crate_root( | 133 | let crate_id = crate_graph.add_crate_root( |
134 | root_file, | 134 | root_file, |
135 | Edition2018, | 135 | Edition2018, |
136 | None, | ||
136 | CfgOptions::default(), | 137 | CfgOptions::default(), |
137 | Env::default(), | 138 | Env::default(), |
138 | ); | 139 | ); |
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index 52f0f23df..de4f5bce0 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml | |||
@@ -13,7 +13,7 @@ wasm = [] | |||
13 | [dependencies] | 13 | [dependencies] |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rayon = "1.3.0" | 15 | rayon = "1.3.0" |
16 | fst = { version = "0.3.5", default-features = false } | 16 | fst = { version = "0.4", default-features = false } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | superslice = "1.0.0" | 18 | superslice = "1.0.0" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 8b5be9d21..628cf6416 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs | |||
@@ -5,7 +5,7 @@ use std::{fmt, sync::Arc, time}; | |||
5 | 5 | ||
6 | use ra_db::{ | 6 | use ra_db::{ |
7 | salsa::{Database, Durability, SweepStrategy}, | 7 | salsa::{Database, Durability, SweepStrategy}, |
8 | CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, | 8 | CrateGraph, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, |
9 | SourceRootId, | 9 | SourceRootId, |
10 | }; | 10 | }; |
11 | use ra_prof::{memory_usage, profile, Bytes}; | 11 | use ra_prof::{memory_usage, profile, Bytes}; |
@@ -88,10 +88,6 @@ impl AnalysisChange { | |||
88 | self.crate_graph = Some(graph); | 88 | self.crate_graph = Some(graph); |
89 | } | 89 | } |
90 | 90 | ||
91 | pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) { | ||
92 | self.debug_data.crate_names.insert(crate_id, name); | ||
93 | } | ||
94 | |||
95 | pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { | 91 | pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { |
96 | self.debug_data.root_paths.insert(source_root_id, path); | 92 | self.debug_data.root_paths.insert(source_root_id, path); |
97 | } | 93 | } |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 79f48c9e3..a105c7556 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -131,12 +131,10 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> { | |||
131 | #[derive(Debug, Default, Clone)] | 131 | #[derive(Debug, Default, Clone)] |
132 | pub(crate) struct DebugData { | 132 | pub(crate) struct DebugData { |
133 | pub(crate) root_paths: FxHashMap<SourceRootId, String>, | 133 | pub(crate) root_paths: FxHashMap<SourceRootId, String>, |
134 | pub(crate) crate_names: FxHashMap<CrateId, String>, | ||
135 | } | 134 | } |
136 | 135 | ||
137 | impl DebugData { | 136 | impl DebugData { |
138 | pub(crate) fn merge(&mut self, other: DebugData) { | 137 | pub(crate) fn merge(&mut self, other: DebugData) { |
139 | self.root_paths.extend(other.root_paths.into_iter()); | 138 | self.root_paths.extend(other.root_paths.into_iter()); |
140 | self.crate_names.extend(other.crate_names.into_iter()); | ||
141 | } | 139 | } |
142 | } | 140 | } |
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index e6b3126b6..884359ee3 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs | |||
@@ -163,7 +163,7 @@ pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymb | |||
163 | #[derive(Default)] | 163 | #[derive(Default)] |
164 | pub struct SymbolIndex { | 164 | pub struct SymbolIndex { |
165 | symbols: Vec<FileSymbol>, | 165 | symbols: Vec<FileSymbol>, |
166 | map: fst::Map, | 166 | map: fst::Map<Vec<u8>>, |
167 | } | 167 | } |
168 | 168 | ||
169 | impl fmt::Debug for SymbolIndex { | 169 | impl fmt::Debug for SymbolIndex { |
@@ -221,7 +221,7 @@ impl SymbolIndex { | |||
221 | builder.insert(key, value).unwrap(); | 221 | builder.insert(key, value).unwrap(); |
222 | } | 222 | } |
223 | 223 | ||
224 | let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap(); | 224 | let map = fst::Map::new(builder.into_inner().unwrap()).unwrap(); |
225 | SymbolIndex { symbols, map } | 225 | SymbolIndex { symbols, map } |
226 | } | 226 | } |
227 | 227 | ||
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 2c6ae5658..43afe24cc 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -31,7 +31,8 @@ pub enum ExpandError { | |||
31 | } | 31 | } |
32 | 32 | ||
33 | pub use crate::syntax_bridge::{ | 33 | pub use crate::syntax_bridge::{ |
34 | ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, TokenMap, | 34 | ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, |
35 | TokenMap, | ||
35 | }; | 36 | }; |
36 | 37 | ||
37 | /// This struct contains AST for a single `macro_rules` definition. What might | 38 | /// This struct contains AST for a single `macro_rules` definition. What might |
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index ffba03898..49c53183a 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs | |||
@@ -247,6 +247,7 @@ impl<'a> TtIter<'a> { | |||
247 | ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); | 247 | ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); |
248 | 248 | ||
249 | if !sink.cursor.is_root() || sink.error { | 249 | if !sink.cursor.is_root() || sink.error { |
250 | // FIXME better recovery in this case would help completion inside macros immensely | ||
250 | return Err(()); | 251 | return Err(()); |
251 | } | 252 | } |
252 | 253 | ||
@@ -375,7 +376,8 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> Result<Option<Fragment>, Ex | |||
375 | return Ok(Some(Fragment::Tokens(tt))); | 376 | return Ok(Some(Fragment::Tokens(tt))); |
376 | } | 377 | } |
377 | }; | 378 | }; |
378 | let tt = input.expect_fragment(fragment).map_err(|()| err!())?; | 379 | let tt = |
380 | input.expect_fragment(fragment).map_err(|()| err!("fragment did not parse as {}", kind))?; | ||
379 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; | 381 | let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; |
380 | Ok(Some(fragment)) | 382 | Ok(Some(fragment)) |
381 | } | 383 | } |
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index fb9fa5314..fcb73fbc7 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs | |||
@@ -2,8 +2,10 @@ | |||
2 | 2 | ||
3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; | 3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, | 5 | ast::{self, make::tokens::doc_comment}, |
6 | SyntaxTreeBuilder, TextRange, TextUnit, T, | 6 | tokenize, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, |
7 | SyntaxKind::*, | ||
8 | SyntaxNode, SyntaxTreeBuilder, TextRange, TextUnit, Token, T, | ||
7 | }; | 9 | }; |
8 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
9 | use std::iter::successors; | 11 | use std::iter::successors; |
@@ -48,9 +50,11 @@ pub fn ast_to_token_tree(ast: &impl ast::AstNode) -> Option<(tt::Subtree, TokenM | |||
48 | /// will consume). | 50 | /// will consume). |
49 | pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> { | 51 | pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> { |
50 | let global_offset = node.text_range().start(); | 52 | let global_offset = node.text_range().start(); |
51 | let mut c = Convertor { map: TokenMap::default(), global_offset, next_id: 0 }; | 53 | let mut c = Convertor { |
54 | id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } }, | ||
55 | }; | ||
52 | let subtree = c.go(node)?; | 56 | let subtree = c.go(node)?; |
53 | Some((subtree, c.map)) | 57 | Some((subtree, c.id_alloc.map)) |
54 | } | 58 | } |
55 | 59 | ||
56 | // The following items are what `rustc` macro can be parsed into : | 60 | // The following items are what `rustc` macro can be parsed into : |
@@ -89,6 +93,28 @@ pub fn token_tree_to_syntax_node( | |||
89 | Ok((parse, range_map)) | 93 | Ok((parse, range_map)) |
90 | } | 94 | } |
91 | 95 | ||
96 | /// Convert a string to a `TokenTree` | ||
97 | pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> { | ||
98 | let (tokens, errors) = tokenize(text); | ||
99 | if !errors.is_empty() { | ||
100 | return None; | ||
101 | } | ||
102 | |||
103 | let mut conv = RawConvertor { | ||
104 | text, | ||
105 | offset: TextUnit::default(), | ||
106 | inner: tokens.iter(), | ||
107 | id_alloc: TokenIdAlloc { | ||
108 | map: Default::default(), | ||
109 | global_offset: TextUnit::default(), | ||
110 | next_id: 0, | ||
111 | }, | ||
112 | }; | ||
113 | |||
114 | let subtree = conv.go()?; | ||
115 | Some((subtree, conv.id_alloc.map)) | ||
116 | } | ||
117 | |||
92 | impl TokenMap { | 118 | impl TokenMap { |
93 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { | 119 | pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { |
94 | let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { | 120 | let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { |
@@ -118,6 +144,14 @@ impl TokenMap { | |||
118 | self.entries | 144 | self.entries |
119 | .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); | 145 | .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); |
120 | } | 146 | } |
147 | |||
148 | fn update_close_delim(&mut self, token_id: tt::TokenId, close_relative_range: TextRange) { | ||
149 | if let Some(entry) = self.entries.iter_mut().find(|(tid, _)| *tid == token_id) { | ||
150 | if let TokenTextRange::Delimiter(dim, _) = entry.1 { | ||
151 | entry.1 = TokenTextRange::Delimiter(dim, close_relative_range); | ||
152 | } | ||
153 | } | ||
154 | } | ||
121 | } | 155 | } |
122 | 156 | ||
123 | /// Returns the textual content of a doc comment block as a quoted string | 157 | /// Returns the textual content of a doc comment block as a quoted string |
@@ -188,12 +222,161 @@ fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTr | |||
188 | } | 222 | } |
189 | } | 223 | } |
190 | 224 | ||
191 | struct Convertor { | 225 | struct TokenIdAlloc { |
192 | map: TokenMap, | 226 | map: TokenMap, |
193 | global_offset: TextUnit, | 227 | global_offset: TextUnit, |
194 | next_id: u32, | 228 | next_id: u32, |
195 | } | 229 | } |
196 | 230 | ||
231 | impl TokenIdAlloc { | ||
232 | fn alloc(&mut self, absolute_range: TextRange) -> tt::TokenId { | ||
233 | let relative_range = absolute_range - self.global_offset; | ||
234 | let token_id = tt::TokenId(self.next_id); | ||
235 | self.next_id += 1; | ||
236 | self.map.insert(token_id, relative_range); | ||
237 | token_id | ||
238 | } | ||
239 | |||
240 | fn delim(&mut self, open_abs_range: TextRange, close_abs_range: TextRange) -> tt::TokenId { | ||
241 | let open_relative_range = open_abs_range - self.global_offset; | ||
242 | let close_relative_range = close_abs_range - self.global_offset; | ||
243 | let token_id = tt::TokenId(self.next_id); | ||
244 | self.next_id += 1; | ||
245 | |||
246 | self.map.insert_delim(token_id, open_relative_range, close_relative_range); | ||
247 | token_id | ||
248 | } | ||
249 | |||
250 | fn open_delim(&mut self, open_abs_range: TextRange) -> tt::TokenId { | ||
251 | let token_id = tt::TokenId(self.next_id); | ||
252 | self.next_id += 1; | ||
253 | self.map.insert_delim(token_id, open_abs_range, open_abs_range); | ||
254 | token_id | ||
255 | } | ||
256 | |||
257 | fn close_delim(&mut self, id: tt::TokenId, close_abs_range: TextRange) { | ||
258 | self.map.update_close_delim(id, close_abs_range); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /// A Raw Token (straightly from lexer) convertor | ||
263 | struct RawConvertor<'a> { | ||
264 | text: &'a str, | ||
265 | offset: TextUnit, | ||
266 | id_alloc: TokenIdAlloc, | ||
267 | inner: std::slice::Iter<'a, Token>, | ||
268 | } | ||
269 | |||
270 | impl RawConvertor<'_> { | ||
271 | fn go(&mut self) -> Option<tt::Subtree> { | ||
272 | let mut subtree = tt::Subtree::default(); | ||
273 | subtree.delimiter = None; | ||
274 | while self.peek().is_some() { | ||
275 | self.collect_leaf(&mut subtree.token_trees); | ||
276 | } | ||
277 | if subtree.token_trees.is_empty() { | ||
278 | return None; | ||
279 | } | ||
280 | if subtree.token_trees.len() == 1 { | ||
281 | if let tt::TokenTree::Subtree(first) = &subtree.token_trees[0] { | ||
282 | return Some(first.clone()); | ||
283 | } | ||
284 | } | ||
285 | Some(subtree) | ||
286 | } | ||
287 | |||
288 | fn bump(&mut self) -> Option<(Token, TextRange)> { | ||
289 | let token = self.inner.next()?; | ||
290 | let range = TextRange::offset_len(self.offset, token.len); | ||
291 | self.offset += token.len; | ||
292 | Some((*token, range)) | ||
293 | } | ||
294 | |||
295 | fn peek(&self) -> Option<Token> { | ||
296 | self.inner.as_slice().get(0).cloned() | ||
297 | } | ||
298 | |||
299 | fn collect_leaf(&mut self, result: &mut Vec<tt::TokenTree>) { | ||
300 | let (token, range) = match self.bump() { | ||
301 | None => return, | ||
302 | Some(it) => it, | ||
303 | }; | ||
304 | |||
305 | let k: SyntaxKind = token.kind; | ||
306 | if k == COMMENT { | ||
307 | let node = doc_comment(&self.text[range]); | ||
308 | if let Some(tokens) = convert_doc_comment(&node) { | ||
309 | result.extend(tokens); | ||
310 | } | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | result.push(if k.is_punct() { | ||
315 | let delim = match k { | ||
316 | T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), | ||
317 | T!['{'] => Some((tt::DelimiterKind::Brace, T!['}'])), | ||
318 | T!['['] => Some((tt::DelimiterKind::Bracket, T![']'])), | ||
319 | _ => None, | ||
320 | }; | ||
321 | |||
322 | if let Some((kind, closed)) = delim { | ||
323 | let mut subtree = tt::Subtree::default(); | ||
324 | let id = self.id_alloc.open_delim(range); | ||
325 | subtree.delimiter = Some(tt::Delimiter { kind, id }); | ||
326 | |||
327 | while self.peek().map(|it| it.kind != closed).unwrap_or(false) { | ||
328 | self.collect_leaf(&mut subtree.token_trees); | ||
329 | } | ||
330 | let last_range = match self.bump() { | ||
331 | None => return, | ||
332 | Some(it) => it.1, | ||
333 | }; | ||
334 | self.id_alloc.close_delim(id, last_range); | ||
335 | subtree.into() | ||
336 | } else { | ||
337 | let spacing = match self.peek() { | ||
338 | Some(next) | ||
339 | if next.kind.is_trivia() | ||
340 | || next.kind == T!['['] | ||
341 | || next.kind == T!['{'] | ||
342 | || next.kind == T!['('] => | ||
343 | { | ||
344 | tt::Spacing::Alone | ||
345 | } | ||
346 | Some(next) if next.kind.is_punct() => tt::Spacing::Joint, | ||
347 | _ => tt::Spacing::Alone, | ||
348 | }; | ||
349 | let char = | ||
350 | self.text[range].chars().next().expect("Token from lexer must be single char"); | ||
351 | |||
352 | tt::Leaf::from(tt::Punct { char, spacing, id: self.id_alloc.alloc(range) }).into() | ||
353 | } | ||
354 | } else { | ||
355 | macro_rules! make_leaf { | ||
356 | ($i:ident) => { | ||
357 | tt::$i { id: self.id_alloc.alloc(range), text: self.text[range].into() }.into() | ||
358 | }; | ||
359 | } | ||
360 | let leaf: tt::Leaf = match k { | ||
361 | T![true] | T![false] => make_leaf!(Literal), | ||
362 | IDENT | LIFETIME => make_leaf!(Ident), | ||
363 | k if k.is_keyword() => make_leaf!(Ident), | ||
364 | k if k.is_literal() => make_leaf!(Literal), | ||
365 | _ => return, | ||
366 | }; | ||
367 | |||
368 | leaf.into() | ||
369 | }); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | // FIXME: There are some duplicate logic between RawConvertor and Convertor | ||
374 | // It would be nice to refactor to converting SyntaxNode to ra_parser::Token and thus | ||
375 | // use RawConvertor directly. But performance-wise it may not be a good idea ? | ||
376 | struct Convertor { | ||
377 | id_alloc: TokenIdAlloc, | ||
378 | } | ||
379 | |||
197 | impl Convertor { | 380 | impl Convertor { |
198 | fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { | 381 | fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { |
199 | // This tree is empty | 382 | // This tree is empty |
@@ -236,7 +419,7 @@ impl Convertor { | |||
236 | }; | 419 | }; |
237 | let delimiter = delimiter_kind.map(|kind| tt::Delimiter { | 420 | let delimiter = delimiter_kind.map(|kind| tt::Delimiter { |
238 | kind, | 421 | kind, |
239 | id: self.alloc_delim(first_child.text_range(), last_child.text_range()), | 422 | id: self.id_alloc.delim(first_child.text_range(), last_child.text_range()), |
240 | }); | 423 | }); |
241 | 424 | ||
242 | let mut token_trees = Vec::new(); | 425 | let mut token_trees = Vec::new(); |
@@ -273,7 +456,7 @@ impl Convertor { | |||
273 | tt::Leaf::from(tt::Punct { | 456 | tt::Leaf::from(tt::Punct { |
274 | char, | 457 | char, |
275 | spacing, | 458 | spacing, |
276 | id: self.alloc(token.text_range()), | 459 | id: self.id_alloc.alloc(token.text_range()), |
277 | }) | 460 | }) |
278 | .into(), | 461 | .into(), |
279 | ); | 462 | ); |
@@ -282,7 +465,7 @@ impl Convertor { | |||
282 | macro_rules! make_leaf { | 465 | macro_rules! make_leaf { |
283 | ($i:ident) => { | 466 | ($i:ident) => { |
284 | tt::$i { | 467 | tt::$i { |
285 | id: self.alloc(token.text_range()), | 468 | id: self.id_alloc.alloc(token.text_range()), |
286 | text: token.text().clone(), | 469 | text: token.text().clone(), |
287 | } | 470 | } |
288 | .into() | 471 | .into() |
@@ -313,28 +496,6 @@ impl Convertor { | |||
313 | let res = tt::Subtree { delimiter, token_trees }; | 496 | let res = tt::Subtree { delimiter, token_trees }; |
314 | Some(res) | 497 | Some(res) |
315 | } | 498 | } |
316 | |||
317 | fn alloc(&mut self, absolute_range: TextRange) -> tt::TokenId { | ||
318 | let relative_range = absolute_range - self.global_offset; | ||
319 | let token_id = tt::TokenId(self.next_id); | ||
320 | self.next_id += 1; | ||
321 | self.map.insert(token_id, relative_range); | ||
322 | token_id | ||
323 | } | ||
324 | |||
325 | fn alloc_delim( | ||
326 | &mut self, | ||
327 | open_abs_range: TextRange, | ||
328 | close_abs_range: TextRange, | ||
329 | ) -> tt::TokenId { | ||
330 | let open_relative_range = open_abs_range - self.global_offset; | ||
331 | let close_relative_range = close_abs_range - self.global_offset; | ||
332 | let token_id = tt::TokenId(self.next_id); | ||
333 | self.next_id += 1; | ||
334 | |||
335 | self.map.insert_delim(token_id, open_relative_range, close_relative_range); | ||
336 | token_id | ||
337 | } | ||
338 | } | 499 | } |
339 | 500 | ||
340 | struct TtTreeSink<'a> { | 501 | struct TtTreeSink<'a> { |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 066ce150b..6d5d1e9e6 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1499,12 +1499,20 @@ impl MacroFixture { | |||
1499 | } | 1499 | } |
1500 | } | 1500 | } |
1501 | 1501 | ||
1502 | pub(crate) fn parse_macro(macro_definition: &str) -> MacroFixture { | 1502 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { |
1503 | let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap(); | 1503 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1504 | let macro_definition = | 1504 | let macro_definition = |
1505 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 1505 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
1506 | 1506 | ||
1507 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | 1507 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); |
1508 | |||
1509 | let parsed = parse_to_token_tree( | ||
1510 | &ra_fixture[macro_definition.token_tree().unwrap().syntax().text_range()], | ||
1511 | ) | ||
1512 | .unwrap() | ||
1513 | .0; | ||
1514 | assert_eq!(definition_tt, parsed); | ||
1515 | |||
1508 | let rules = MacroRules::parse(&definition_tt).unwrap(); | 1516 | let rules = MacroRules::parse(&definition_tt).unwrap(); |
1509 | MacroFixture { rules } | 1517 | MacroFixture { rules } |
1510 | } | 1518 | } |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index bcf12460d..37845ca56 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -14,7 +14,7 @@ use std::{ | |||
14 | 14 | ||
15 | use anyhow::{bail, Context, Result}; | 15 | use anyhow::{bail, Context, Result}; |
16 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateName, Edition, Env, FileId}; |
18 | use rustc_hash::FxHashMap; | 18 | use rustc_hash::FxHashMap; |
19 | use serde_json::from_reader; | 19 | use serde_json::from_reader; |
20 | 20 | ||
@@ -163,9 +163,8 @@ impl ProjectWorkspace { | |||
163 | &self, | 163 | &self, |
164 | default_cfg_options: &CfgOptions, | 164 | default_cfg_options: &CfgOptions, |
165 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 165 | load: &mut dyn FnMut(&Path) -> Option<FileId>, |
166 | ) -> (CrateGraph, FxHashMap<CrateId, String>) { | 166 | ) -> CrateGraph { |
167 | let mut crate_graph = CrateGraph::default(); | 167 | let mut crate_graph = CrateGraph::default(); |
168 | let mut names = FxHashMap::default(); | ||
169 | match self { | 168 | match self { |
170 | ProjectWorkspace::Json { project } => { | 169 | ProjectWorkspace::Json { project } => { |
171 | let mut crates = FxHashMap::default(); | 170 | let mut crates = FxHashMap::default(); |
@@ -191,6 +190,8 @@ impl ProjectWorkspace { | |||
191 | crate_graph.add_crate_root( | 190 | crate_graph.add_crate_root( |
192 | file_id, | 191 | file_id, |
193 | edition, | 192 | edition, |
193 | // FIXME json definitions can store the crate name | ||
194 | None, | ||
194 | cfg_options, | 195 | cfg_options, |
195 | Env::default(), | 196 | Env::default(), |
196 | ), | 197 | ), |
@@ -233,11 +234,11 @@ impl ProjectWorkspace { | |||
233 | let crate_id = crate_graph.add_crate_root( | 234 | let crate_id = crate_graph.add_crate_root( |
234 | file_id, | 235 | file_id, |
235 | Edition::Edition2018, | 236 | Edition::Edition2018, |
237 | Some(krate.name(&sysroot).to_string()), | ||
236 | cfg_options, | 238 | cfg_options, |
237 | Env::default(), | 239 | Env::default(), |
238 | ); | 240 | ); |
239 | sysroot_crates.insert(krate, crate_id); | 241 | sysroot_crates.insert(krate, crate_id); |
240 | names.insert(crate_id, krate.name(&sysroot).to_string()); | ||
241 | } | 242 | } |
242 | } | 243 | } |
243 | for from in sysroot.crates() { | 244 | for from in sysroot.crates() { |
@@ -277,10 +278,10 @@ impl ProjectWorkspace { | |||
277 | let crate_id = crate_graph.add_crate_root( | 278 | let crate_id = crate_graph.add_crate_root( |
278 | file_id, | 279 | file_id, |
279 | edition, | 280 | edition, |
281 | Some(pkg.name(&cargo).to_string()), | ||
280 | cfg_options, | 282 | cfg_options, |
281 | Env::default(), | 283 | Env::default(), |
282 | ); | 284 | ); |
283 | names.insert(crate_id, pkg.name(&cargo).to_string()); | ||
284 | if tgt.kind(&cargo) == TargetKind::Lib { | 285 | if tgt.kind(&cargo) == TargetKind::Lib { |
285 | lib_tgt = Some(crate_id); | 286 | lib_tgt = Some(crate_id); |
286 | pkg_to_lib_crate.insert(pkg, crate_id); | 287 | pkg_to_lib_crate.insert(pkg, crate_id); |
@@ -381,7 +382,7 @@ impl ProjectWorkspace { | |||
381 | } | 382 | } |
382 | } | 383 | } |
383 | } | 384 | } |
384 | (crate_graph, names) | 385 | crate_graph |
385 | } | 386 | } |
386 | 387 | ||
387 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { | 388 | pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 53d6fa562..ae8829807 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -267,6 +267,12 @@ pub mod tokens { | |||
267 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() | 267 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() |
268 | } | 268 | } |
269 | 269 | ||
270 | pub fn doc_comment(text: &str) -> SyntaxToken { | ||
271 | assert!(!text.trim().is_empty()); | ||
272 | let sf = SourceFile::parse(text).ok().unwrap(); | ||
273 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() | ||
274 | } | ||
275 | |||
270 | pub fn literal(text: &str) -> SyntaxToken { | 276 | pub fn literal(text: &str) -> SyntaxToken { |
271 | assert_eq!(text.trim(), text); | 277 | assert_eq!(text.trim(), text); |
272 | let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text)); | 278 | let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text)); |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index b14ebb268..d44f0ef1d 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -20,7 +20,7 @@ globset = "0.4.4" | |||
20 | itertools = "0.8.2" | 20 | itertools = "0.8.2" |
21 | jod-thread = "0.1.0" | 21 | jod-thread = "0.1.0" |
22 | log = "0.4.8" | 22 | log = "0.4.8" |
23 | lsp-types = { version = "0.72.0", features = ["proposed"] } | 23 | lsp-types = { version = "0.73.0", features = ["proposed"] } |
24 | parking_lot = "0.10.0" | 24 | parking_lot = "0.10.0" |
25 | pico-args = "0.3.1" | 25 | pico-args = "0.3.1" |
26 | rand = { version = "0.7.3", features = ["small_rng"] } | 26 | rand = { version = "0.7.3", features = ["small_rng"] } |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 8cd08ecb6..4be987860 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -52,12 +52,11 @@ pub(crate) fn load_cargo( | |||
52 | opts | 52 | opts |
53 | }; | 53 | }; |
54 | 54 | ||
55 | let (crate_graph, _crate_names) = | 55 | let crate_graph = ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { |
56 | ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { | 56 | let vfs_file = vfs.load(path); |
57 | let vfs_file = vfs.load(path); | 57 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); |
58 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | 58 | vfs_file.map(vfs_file_to_id) |
59 | vfs_file.map(vfs_file_to_id) | 59 | }); |
60 | }); | ||
61 | log::debug!("crate graph: {:?}", crate_graph); | 60 | log::debug!("crate graph: {:?}", crate_graph); |
62 | 61 | ||
63 | let source_roots = roots | 62 | let source_roots = roots |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 580ad1f2c..221f464b6 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -44,6 +44,8 @@ pub struct LspError { | |||
44 | } | 44 | } |
45 | 45 | ||
46 | impl LspError { | 46 | impl LspError { |
47 | pub const UNKNOWN_FILE: i32 = -32900; | ||
48 | |||
47 | pub fn new(code: i32, message: String) -> LspError { | 49 | pub fn new(code: i32, message: String) -> LspError { |
48 | LspError { code, message } | 50 | LspError { code, message } |
49 | } | 51 | } |
@@ -631,6 +633,9 @@ fn on_notification( | |||
631 | } | 633 | } |
632 | Err(not) => not, | 634 | Err(not) => not, |
633 | }; | 635 | }; |
636 | if not.method.starts_with("$/") { | ||
637 | return Ok(()); | ||
638 | } | ||
634 | log::error!("unhandled notification: {:?}", not); | 639 | log::error!("unhandled notification: {:?}", not); |
635 | Ok(()) | 640 | Ok(()) |
636 | } | 641 | } |
@@ -802,7 +807,14 @@ where | |||
802 | let response = match result { | 807 | let response = match result { |
803 | Ok(resp) => Response::new_ok(id, &resp), | 808 | Ok(resp) => Response::new_ok(id, &resp), |
804 | Err(e) => match e.downcast::<LspError>() { | 809 | Err(e) => match e.downcast::<LspError>() { |
805 | Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), | 810 | Ok(lsp_error) => { |
811 | if lsp_error.code == LspError::UNKNOWN_FILE { | ||
812 | // Work-around for https://github.com/rust-analyzer/rust-analyzer/issues/1521 | ||
813 | Response::new_ok(id, ()) | ||
814 | } else { | ||
815 | Response::new_err(id, lsp_error.code, lsp_error.message) | ||
816 | } | ||
817 | } | ||
806 | Err(e) => { | 818 | Err(e) => { |
807 | if is_canceled(&e) { | 819 | if is_canceled(&e) { |
808 | Response::new_err( | 820 | Response::new_err( |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index b498c90c9..8dc6e8dc0 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -85,11 +85,11 @@ pub fn handle_expand_macro( | |||
85 | pub fn handle_selection_range( | 85 | pub fn handle_selection_range( |
86 | world: WorldSnapshot, | 86 | world: WorldSnapshot, |
87 | params: req::SelectionRangeParams, | 87 | params: req::SelectionRangeParams, |
88 | ) -> Result<Vec<req::SelectionRange>> { | 88 | ) -> Result<Option<Vec<req::SelectionRange>>> { |
89 | let _p = profile("handle_selection_range"); | 89 | let _p = profile("handle_selection_range"); |
90 | let file_id = params.text_document.try_conv_with(&world)?; | 90 | let file_id = params.text_document.try_conv_with(&world)?; |
91 | let line_index = world.analysis().file_line_index(file_id)?; | 91 | let line_index = world.analysis().file_line_index(file_id)?; |
92 | params | 92 | let res: Result<Vec<req::SelectionRange>> = params |
93 | .positions | 93 | .positions |
94 | .into_iter() | 94 | .into_iter() |
95 | .map_conv_with(&line_index) | 95 | .map_conv_with(&line_index) |
@@ -120,7 +120,9 @@ pub fn handle_selection_range( | |||
120 | } | 120 | } |
121 | Ok(range) | 121 | Ok(range) |
122 | }) | 122 | }) |
123 | .collect() | 123 | .collect(); |
124 | |||
125 | Ok(Some(res?)) | ||
124 | } | 126 | } |
125 | 127 | ||
126 | pub fn handle_find_matching_brace( | 128 | pub fn handle_find_matching_brace( |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 96efab844..6f394055a 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -9,7 +9,6 @@ use std::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crossbeam_channel::{unbounded, Receiver}; | 11 | use crossbeam_channel::{unbounded, Receiver}; |
12 | use lsp_server::ErrorCode; | ||
13 | use lsp_types::Url; | 12 | use lsp_types::Url; |
14 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
15 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; | 14 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; |
@@ -123,13 +122,12 @@ impl WorldState { | |||
123 | let vfs_file = vfs.load(path); | 122 | let vfs_file = vfs.load(path); |
124 | vfs_file.map(|f| FileId(f.0)) | 123 | vfs_file.map(|f| FileId(f.0)) |
125 | }; | 124 | }; |
126 | for ws in workspaces.iter() { | 125 | |
127 | let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load); | 126 | workspaces.iter().map(|ws| ws.to_crate_graph(&default_cfg_options, &mut load)).for_each( |
128 | let shift = crate_graph.extend(graph); | 127 | |graph| { |
129 | for (crate_id, name) in crate_names { | 128 | crate_graph.extend(graph); |
130 | change.set_debug_crate_name(crate_id.shift(shift), name) | 129 | }, |
131 | } | 130 | ); |
132 | } | ||
133 | change.set_crate_graph(crate_graph); | 131 | change.set_crate_graph(crate_graph); |
134 | 132 | ||
135 | // FIXME: Figure out the multi-workspace situation | 133 | // FIXME: Figure out the multi-workspace situation |
@@ -252,8 +250,9 @@ impl WorldSnapshot { | |||
252 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 250 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
253 | let file = self.vfs.read().path2file(&path).ok_or_else(|| { | 251 | let file = self.vfs.read().path2file(&path).ok_or_else(|| { |
254 | // Show warning as this file is outside current workspace | 252 | // Show warning as this file is outside current workspace |
253 | // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`. | ||
255 | LspError { | 254 | LspError { |
256 | code: ErrorCode::InvalidRequest as i32, | 255 | code: LspError::UNKNOWN_FILE, |
257 | message: "Rust file outside current workspace is not supported yet.".to_string(), | 256 | message: "Rust file outside current workspace is not supported yet.".to_string(), |
258 | } | 257 | } |
259 | })?; | 258 | })?; |
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index f1386a8f9..4e99dd0a6 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc | |||
@@ -2,6 +2,13 @@ | |||
2 | :toc: preamble | 2 | :toc: preamble |
3 | :sectanchors: | 3 | :sectanchors: |
4 | :page-layout: post | 4 | :page-layout: post |
5 | // https://gist.github.com/dcode/0cfbf2699a1fe9b46ff04c41721dda74#admonitions | ||
6 | :tip-caption: :bulb: | ||
7 | :note-caption: :information_source: | ||
8 | :important-caption: :heavy_exclamation_mark: | ||
9 | :caution-caption: :fire: | ||
10 | :warning-caption: :warning: | ||
11 | |||
5 | 12 | ||
6 | 13 | ||
7 | // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository | 14 | // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository |
@@ -30,7 +37,7 @@ $ rustup component add rust-src | |||
30 | 37 | ||
31 | === VS Code | 38 | === VS Code |
32 | 39 | ||
33 | This the best supported editor at the moment. | 40 | This is the best supported editor at the moment. |
34 | rust-analyzer plugin for VS Code is maintained | 41 | rust-analyzer plugin for VS Code is maintained |
35 | https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree]. | 42 | https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree]. |
36 | 43 | ||
@@ -40,6 +47,16 @@ By default, the plugin will prompt you to download the matching version of the s | |||
40 | 47 | ||
41 | image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[] | 48 | image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[] |
42 | 49 | ||
50 | [NOTE] | ||
51 | ==== | ||
52 | To disable this notification put the following to `settings.json` | ||
53 | |||
54 | [source,json] | ||
55 | ---- | ||
56 | { "rust-analyzer.updates.askBeforeDownload": false } | ||
57 | ---- | ||
58 | ==== | ||
59 | |||
43 | The server binary is stored in `~/.config/Code/User/globalStorage/matklad.rust-analyzer`. | 60 | The server binary is stored in `~/.config/Code/User/globalStorage/matklad.rust-analyzer`. |
44 | 61 | ||
45 | Note that we only support the latest version of VS Code. | 62 | Note that we only support the latest version of VS Code. |
@@ -124,7 +141,7 @@ let g:LanguageClient_serverCommands = { | |||
124 | 141 | ||
125 | NeoVim 0.5 (not yet released) has built-in language server support. | 142 | NeoVim 0.5 (not yet released) has built-in language server support. |
126 | For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lsp#rust_analyzer[neovim/nvim-lsp]. | 143 | For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lsp#rust_analyzer[neovim/nvim-lsp]. |
127 | Once `neovim/nvim-lsp` is installed, use `lua require'nvim_lsp'.rust_analyzer.setup({})` in your `init.vim`. | 144 | Once `neovim/nvim-lsp` is installed, use `+lua require'nvim_lsp'.rust_analyzer.setup({})+` in your `init.vim`. |
128 | 145 | ||
129 | === Sublime Text 3 | 146 | === Sublime Text 3 |
130 | 147 | ||
diff --git a/editors/code/package.json b/editors/code/package.json index 225739328..7a4a93e30 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -191,7 +191,7 @@ | |||
191 | "properties": { | 191 | "properties": { |
192 | "lsp.diagnostics": { | 192 | "lsp.diagnostics": { |
193 | "type": "boolean", | 193 | "type": "boolean", |
194 | "description": "Whether to show diagnostics from `cargo check`" | 194 | "markdownDescription": "Whether to show diagnostics from `cargo check`" |
195 | }, | 195 | }, |
196 | "completion.insertion.add-call-parenthesis": { | 196 | "completion.insertion.add-call-parenthesis": { |
197 | "type": "boolean", | 197 | "type": "boolean", |
@@ -203,7 +203,7 @@ | |||
203 | }, | 203 | }, |
204 | "completion.enable-postfix": { | 204 | "completion.enable-postfix": { |
205 | "type": "boolean", | 205 | "type": "boolean", |
206 | "description": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." | 206 | "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." |
207 | }, | 207 | }, |
208 | "call-info.full": { | 208 | "call-info.full": { |
209 | "type": "boolean", | 209 | "type": "boolean", |
@@ -211,14 +211,19 @@ | |||
211 | }, | 211 | }, |
212 | "notifications.workspace-loaded": { | 212 | "notifications.workspace-loaded": { |
213 | "type": "boolean", | 213 | "type": "boolean", |
214 | "description": "Whether to show `workspace loaded` message" | 214 | "markdownDescription": "Whether to show `workspace loaded` message" |
215 | }, | 215 | }, |
216 | "notifications.cargo-toml-not-found": { | 216 | "notifications.cargo-toml-not-found": { |
217 | "type": "boolean", | 217 | "type": "boolean", |
218 | "description": "Whether to show `can't find Cargo.toml` error message" | 218 | "markdownDescription": "Whether to show `can't find Cargo.toml` error message" |
219 | } | 219 | } |
220 | } | 220 | } |
221 | }, | 221 | }, |
222 | "rust-analyzer.updates.askBeforeDownload": { | ||
223 | "type": "boolean", | ||
224 | "default": true, | ||
225 | "description": "Whether to ask for permission before downloading any files from the Internet" | ||
226 | }, | ||
222 | "rust-analyzer.serverPath": { | 227 | "rust-analyzer.serverPath": { |
223 | "type": [ | 228 | "type": [ |
224 | "null", | 229 | "null", |
@@ -251,24 +256,24 @@ | |||
251 | "rust-analyzer.cargo-watch.enable": { | 256 | "rust-analyzer.cargo-watch.enable": { |
252 | "type": "boolean", | 257 | "type": "boolean", |
253 | "default": true, | 258 | "default": true, |
254 | "description": "Run `cargo check` for diagnostics on save" | 259 | "markdownDescription": "Run `cargo check` for diagnostics on save" |
255 | }, | 260 | }, |
256 | "rust-analyzer.cargo-watch.arguments": { | 261 | "rust-analyzer.cargo-watch.arguments": { |
257 | "type": "array", | 262 | "type": "array", |
258 | "items": { | 263 | "items": { |
259 | "type": "string" | 264 | "type": "string" |
260 | }, | 265 | }, |
261 | "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", | 266 | "markdownDescription": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", |
262 | "default": [] | 267 | "default": [] |
263 | }, | 268 | }, |
264 | "rust-analyzer.cargo-watch.command": { | 269 | "rust-analyzer.cargo-watch.command": { |
265 | "type": "string", | 270 | "type": "string", |
266 | "description": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", | 271 | "markdownDescription": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", |
267 | "default": "check" | 272 | "default": "check" |
268 | }, | 273 | }, |
269 | "rust-analyzer.cargo-watch.allTargets": { | 274 | "rust-analyzer.cargo-watch.allTargets": { |
270 | "type": "boolean", | 275 | "type": "boolean", |
271 | "description": "Check all targets and tests (will be passed as `--all-targets`)", | 276 | "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)", |
272 | "default": true | 277 | "default": true |
273 | }, | 278 | }, |
274 | "rust-analyzer.trace.server": { | 279 | "rust-analyzer.trace.server": { |
@@ -320,7 +325,7 @@ | |||
320 | "rust-analyzer.cargoFeatures.noDefaultFeatures": { | 325 | "rust-analyzer.cargoFeatures.noDefaultFeatures": { |
321 | "type": "boolean", | 326 | "type": "boolean", |
322 | "default": false, | 327 | "default": false, |
323 | "description": "Do not activate the `default` feature" | 328 | "markdownDescription": "Do not activate the `default` feature" |
324 | }, | 329 | }, |
325 | "rust-analyzer.cargoFeatures.allFeatures": { | 330 | "rust-analyzer.cargoFeatures.allFeatures": { |
326 | "type": "boolean", | 331 | "type": "boolean", |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index bf915102c..b72206d3c 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as os from "os"; | 1 | import * as os from "os"; |
2 | import * as vscode from 'vscode'; | 2 | import * as vscode from 'vscode'; |
3 | import { BinarySource } from "./installation/interfaces"; | 3 | import { ArtifactSource } from "./installation/interfaces"; |
4 | import { log } from "./util"; | 4 | import { log } from "./util"; |
5 | 5 | ||
6 | const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; | 6 | const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; |
@@ -114,12 +114,12 @@ export class Config { | |||
114 | } | 114 | } |
115 | } | 115 | } |
116 | 116 | ||
117 | get serverSource(): null | BinarySource { | 117 | get serverSource(): null | ArtifactSource { |
118 | const serverPath = RA_LSP_DEBUG ?? this.cfg.get<null | string>("serverPath"); | 118 | const serverPath = RA_LSP_DEBUG ?? this.cfg.get<null | string>("serverPath"); |
119 | 119 | ||
120 | if (serverPath) { | 120 | if (serverPath) { |
121 | return { | 121 | return { |
122 | type: BinarySource.Type.ExplicitPath, | 122 | type: ArtifactSource.Type.ExplicitPath, |
123 | path: Config.replaceTildeWithHomeDir(serverPath) | 123 | path: Config.replaceTildeWithHomeDir(serverPath) |
124 | }; | 124 | }; |
125 | } | 125 | } |
@@ -129,11 +129,12 @@ export class Config { | |||
129 | if (!prebuiltBinaryName) return null; | 129 | if (!prebuiltBinaryName) return null; |
130 | 130 | ||
131 | return { | 131 | return { |
132 | type: BinarySource.Type.GithubRelease, | 132 | type: ArtifactSource.Type.GithubRelease, |
133 | dir: this.ctx.globalStoragePath, | 133 | dir: this.ctx.globalStoragePath, |
134 | file: prebuiltBinaryName, | 134 | file: prebuiltBinaryName, |
135 | storage: this.ctx.globalState, | 135 | storage: this.ctx.globalState, |
136 | version: Config.extensionVersion, | 136 | tag: Config.extensionVersion, |
137 | askBeforeDownload: this.cfg.get("updates.askBeforeDownload") as boolean, | ||
137 | repo: { | 138 | repo: { |
138 | name: "rust-analyzer", | 139 | name: "rust-analyzer", |
139 | owner: "rust-analyzer", | 140 | owner: "rust-analyzer", |
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index b4e983a0c..25ef38aed 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -3,7 +3,7 @@ import * as lc from 'vscode-languageclient'; | |||
3 | 3 | ||
4 | import { Config } from './config'; | 4 | import { Config } from './config'; |
5 | import { createClient } from './client'; | 5 | import { createClient } from './client'; |
6 | import { isRustDocument } from './util'; | 6 | import { isRustEditor, RustEditor } from './util'; |
7 | 7 | ||
8 | export class Ctx { | 8 | export class Ctx { |
9 | private constructor( | 9 | private constructor( |
@@ -22,17 +22,15 @@ export class Ctx { | |||
22 | return res; | 22 | return res; |
23 | } | 23 | } |
24 | 24 | ||
25 | get activeRustEditor(): vscode.TextEditor | undefined { | 25 | get activeRustEditor(): RustEditor | undefined { |
26 | const editor = vscode.window.activeTextEditor; | 26 | const editor = vscode.window.activeTextEditor; |
27 | return editor && isRustDocument(editor.document) | 27 | return editor && isRustEditor(editor) |
28 | ? editor | 28 | ? editor |
29 | : undefined; | 29 | : undefined; |
30 | } | 30 | } |
31 | 31 | ||
32 | get visibleRustEditors(): vscode.TextEditor[] { | 32 | get visibleRustEditors(): RustEditor[] { |
33 | return vscode.window.visibleTextEditors.filter( | 33 | return vscode.window.visibleTextEditors.filter(isRustEditor); |
34 | editor => isRustDocument(editor.document), | ||
35 | ); | ||
36 | } | 34 | } |
37 | 35 | ||
38 | registerCommand(name: string, factory: (ctx: Ctx) => Cmd) { | 36 | registerCommand(name: string, factory: (ctx: Ctx) => Cmd) { |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 08d3a64a7..e1a82e03e 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -1,156 +1,214 @@ | |||
1 | import * as lc from "vscode-languageclient"; | ||
1 | import * as vscode from 'vscode'; | 2 | import * as vscode from 'vscode'; |
2 | import * as ra from './rust-analyzer-api'; | 3 | import * as ra from './rust-analyzer-api'; |
3 | 4 | ||
4 | import { Ctx } from './ctx'; | 5 | import { Ctx, Disposable } from './ctx'; |
5 | import { log, sendRequestWithRetry, isRustDocument } from './util'; | 6 | import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor } from './util'; |
6 | 7 | ||
7 | export function activateInlayHints(ctx: Ctx) { | ||
8 | const hintsUpdater = new HintsUpdater(ctx); | ||
9 | vscode.window.onDidChangeVisibleTextEditors( | ||
10 | async _ => hintsUpdater.refresh(), | ||
11 | null, | ||
12 | ctx.subscriptions | ||
13 | ); | ||
14 | 8 | ||
15 | vscode.workspace.onDidChangeTextDocument( | 9 | export function activateInlayHints(ctx: Ctx) { |
16 | async event => { | 10 | const maybeUpdater = { |
17 | if (event.contentChanges.length === 0) return; | 11 | updater: null as null | HintsUpdater, |
18 | if (!isRustDocument(event.document)) return; | 12 | onConfigChange() { |
19 | await hintsUpdater.refresh(); | 13 | if (!ctx.config.displayInlayHints) { |
14 | return this.dispose(); | ||
15 | } | ||
16 | if (!this.updater) this.updater = new HintsUpdater(ctx); | ||
20 | }, | 17 | }, |
21 | null, | 18 | dispose() { |
22 | ctx.subscriptions | 19 | this.updater?.dispose(); |
23 | ); | 20 | this.updater = null; |
21 | } | ||
22 | }; | ||
23 | |||
24 | ctx.pushCleanup(maybeUpdater); | ||
24 | 25 | ||
25 | vscode.workspace.onDidChangeConfiguration( | 26 | vscode.workspace.onDidChangeConfiguration( |
26 | async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints), | 27 | maybeUpdater.onConfigChange, maybeUpdater, ctx.subscriptions |
27 | null, | ||
28 | ctx.subscriptions | ||
29 | ); | 28 | ); |
30 | 29 | ||
31 | ctx.pushCleanup({ | 30 | maybeUpdater.onConfigChange(); |
32 | dispose() { | 31 | } |
33 | hintsUpdater.clear(); | 32 | |
33 | |||
34 | const typeHints = { | ||
35 | decorationType: vscode.window.createTextEditorDecorationType({ | ||
36 | after: { | ||
37 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | ||
38 | fontStyle: "normal", | ||
34 | } | 39 | } |
35 | }); | 40 | }), |
36 | 41 | ||
37 | // XXX: we don't await this, thus Promise rejections won't be handled, but | 42 | toDecoration(hint: ra.InlayHint.TypeHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions { |
38 | // this should never throw in fact... | 43 | return { |
39 | void hintsUpdater.setEnabled(ctx.config.displayInlayHints); | 44 | range: conv.asRange(hint.range), |
40 | } | 45 | renderOptions: { after: { contentText: `: ${hint.label}` } } |
46 | }; | ||
47 | } | ||
48 | }; | ||
49 | |||
50 | const paramHints = { | ||
51 | decorationType: vscode.window.createTextEditorDecorationType({ | ||
52 | before: { | ||
53 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | ||
54 | fontStyle: "normal", | ||
55 | } | ||
56 | }), | ||
41 | 57 | ||
42 | const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ | 58 | toDecoration(hint: ra.InlayHint.ParamHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions { |
43 | after: { | 59 | return { |
44 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | 60 | range: conv.asRange(hint.range), |
45 | fontStyle: "normal", | 61 | renderOptions: { before: { contentText: `${hint.label}: ` } } |
46 | }, | 62 | }; |
47 | }); | ||
48 | |||
49 | const parameterHintDecorationType = vscode.window.createTextEditorDecorationType({ | ||
50 | before: { | ||
51 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | ||
52 | fontStyle: "normal", | ||
53 | }, | ||
54 | }); | ||
55 | |||
56 | class HintsUpdater { | ||
57 | private pending = new Map<string, vscode.CancellationTokenSource>(); | ||
58 | private ctx: Ctx; | ||
59 | private enabled: boolean; | ||
60 | |||
61 | constructor(ctx: Ctx) { | ||
62 | this.ctx = ctx; | ||
63 | this.enabled = false; | ||
64 | } | 63 | } |
64 | }; | ||
65 | 65 | ||
66 | async setEnabled(enabled: boolean): Promise<void> { | 66 | class HintsUpdater implements Disposable { |
67 | log.debug({ enabled, prev: this.enabled }); | 67 | private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile |
68 | private readonly disposables: Disposable[] = []; | ||
68 | 69 | ||
69 | if (this.enabled === enabled) return; | 70 | constructor(private readonly ctx: Ctx) { |
70 | this.enabled = enabled; | 71 | vscode.window.onDidChangeVisibleTextEditors( |
72 | this.onDidChangeVisibleTextEditors, | ||
73 | this, | ||
74 | this.disposables | ||
75 | ); | ||
71 | 76 | ||
72 | if (this.enabled) { | 77 | vscode.workspace.onDidChangeTextDocument( |
73 | return await this.refresh(); | 78 | this.onDidChangeTextDocument, |
74 | } else { | 79 | this, |
75 | return this.clear(); | 80 | this.disposables |
76 | } | 81 | ); |
82 | |||
83 | // Set up initial cache shape | ||
84 | ctx.visibleRustEditors.forEach(editor => this.sourceFiles.set( | ||
85 | editor.document.uri.toString(), | ||
86 | { | ||
87 | document: editor.document, | ||
88 | inlaysRequest: null, | ||
89 | cachedDecorations: null | ||
90 | } | ||
91 | )); | ||
92 | |||
93 | this.syncCacheAndRenderHints(); | ||
77 | } | 94 | } |
78 | 95 | ||
79 | clear() { | 96 | dispose() { |
80 | this.ctx.visibleRustEditors.forEach(it => { | 97 | this.sourceFiles.forEach(file => file.inlaysRequest?.cancel()); |
81 | this.setTypeDecorations(it, []); | 98 | this.ctx.visibleRustEditors.forEach(editor => this.renderDecorations(editor, { param: [], type: [] })); |
82 | this.setParameterDecorations(it, []); | 99 | this.disposables.forEach(d => d.dispose()); |
83 | }); | 100 | } |
101 | |||
102 | onDidChangeTextDocument({ contentChanges, document }: vscode.TextDocumentChangeEvent) { | ||
103 | if (contentChanges.length === 0 || !isRustDocument(document)) return; | ||
104 | this.syncCacheAndRenderHints(); | ||
84 | } | 105 | } |
85 | 106 | ||
86 | async refresh() { | 107 | private syncCacheAndRenderHints() { |
87 | if (!this.enabled) return; | 108 | // FIXME: make inlayHints request pass an array of files? |
88 | await Promise.all(this.ctx.visibleRustEditors.map(it => this.refreshEditor(it))); | 109 | this.sourceFiles.forEach((file, uri) => this.fetchHints(file).then(hints => { |
110 | if (!hints) return; | ||
111 | |||
112 | file.cachedDecorations = this.hintsToDecorations(hints); | ||
113 | |||
114 | for (const editor of this.ctx.visibleRustEditors) { | ||
115 | if (editor.document.uri.toString() === uri) { | ||
116 | this.renderDecorations(editor, file.cachedDecorations); | ||
117 | } | ||
118 | } | ||
119 | })); | ||
89 | } | 120 | } |
90 | 121 | ||
91 | private async refreshEditor(editor: vscode.TextEditor): Promise<void> { | 122 | onDidChangeVisibleTextEditors() { |
92 | const newHints = await this.queryHints(editor.document.uri.toString()); | 123 | const newSourceFiles = new Map<string, RustSourceFile>(); |
93 | if (newHints == null) return; | 124 | |
94 | 125 | // Rerendering all, even up-to-date editors for simplicity | |
95 | const newTypeDecorations = newHints | 126 | this.ctx.visibleRustEditors.forEach(async editor => { |
96 | .filter(hint => hint.kind === ra.InlayKind.TypeHint) | 127 | const uri = editor.document.uri.toString(); |
97 | .map(hint => ({ | 128 | const file = this.sourceFiles.get(uri) ?? { |
98 | range: this.ctx.client.protocol2CodeConverter.asRange(hint.range), | 129 | document: editor.document, |
99 | renderOptions: { | 130 | inlaysRequest: null, |
100 | after: { | 131 | cachedDecorations: null |
101 | contentText: `: ${hint.label}`, | 132 | }; |
102 | }, | 133 | newSourceFiles.set(uri, file); |
103 | }, | 134 | |
104 | })); | 135 | // No text documents changed, so we may try to use the cache |
105 | this.setTypeDecorations(editor, newTypeDecorations); | 136 | if (!file.cachedDecorations) { |
106 | 137 | file.inlaysRequest?.cancel(); | |
107 | const newParameterDecorations = newHints | 138 | |
108 | .filter(hint => hint.kind === ra.InlayKind.ParameterHint) | 139 | const hints = await this.fetchHints(file); |
109 | .map(hint => ({ | 140 | if (!hints) return; |
110 | range: this.ctx.client.protocol2CodeConverter.asRange(hint.range), | 141 | |
111 | renderOptions: { | 142 | file.cachedDecorations = this.hintsToDecorations(hints); |
112 | before: { | 143 | } |
113 | contentText: `${hint.label}: `, | 144 | |
114 | }, | 145 | this.renderDecorations(editor, file.cachedDecorations); |
115 | }, | 146 | }); |
116 | })); | 147 | |
117 | this.setParameterDecorations(editor, newParameterDecorations); | 148 | // Cancel requests for no longer visible (disposed) source files |
149 | this.sourceFiles.forEach((file, uri) => { | ||
150 | if (!newSourceFiles.has(uri)) file.inlaysRequest?.cancel(); | ||
151 | }); | ||
152 | |||
153 | this.sourceFiles = newSourceFiles; | ||
118 | } | 154 | } |
119 | 155 | ||
120 | private setTypeDecorations( | 156 | private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) { |
121 | editor: vscode.TextEditor, | 157 | editor.setDecorations(typeHints.decorationType, decorations.type); |
122 | decorations: vscode.DecorationOptions[], | 158 | editor.setDecorations(paramHints.decorationType, decorations.param); |
123 | ) { | ||
124 | editor.setDecorations( | ||
125 | typeHintDecorationType, | ||
126 | this.enabled ? decorations : [], | ||
127 | ); | ||
128 | } | 159 | } |
129 | 160 | ||
130 | private setParameterDecorations( | 161 | private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations { |
131 | editor: vscode.TextEditor, | 162 | const decorations: InlaysDecorations = { type: [], param: [] }; |
132 | decorations: vscode.DecorationOptions[], | 163 | const conv = this.ctx.client.protocol2CodeConverter; |
133 | ) { | 164 | |
134 | editor.setDecorations( | 165 | for (const hint of hints) { |
135 | parameterHintDecorationType, | 166 | switch (hint.kind) { |
136 | this.enabled ? decorations : [], | 167 | case ra.InlayHint.Kind.TypeHint: { |
137 | ); | 168 | decorations.type.push(typeHints.toDecoration(hint, conv)); |
169 | continue; | ||
170 | } | ||
171 | case ra.InlayHint.Kind.ParamHint: { | ||
172 | decorations.param.push(paramHints.toDecoration(hint, conv)); | ||
173 | continue; | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | return decorations; | ||
138 | } | 178 | } |
139 | 179 | ||
140 | private async queryHints(documentUri: string): Promise<ra.InlayHint[] | null> { | 180 | private async fetchHints(file: RustSourceFile): Promise<null | ra.InlayHint[]> { |
141 | this.pending.get(documentUri)?.cancel(); | 181 | file.inlaysRequest?.cancel(); |
142 | 182 | ||
143 | const tokenSource = new vscode.CancellationTokenSource(); | 183 | const tokenSource = new vscode.CancellationTokenSource(); |
144 | this.pending.set(documentUri, tokenSource); | 184 | file.inlaysRequest = tokenSource; |
145 | 185 | ||
146 | const request = { textDocument: { uri: documentUri } }; | 186 | const request = { textDocument: { uri: file.document.uri.toString() } }; |
147 | 187 | ||
148 | return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token) | 188 | return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token) |
149 | .catch(_ => null) | 189 | .catch(_ => null) |
150 | .finally(() => { | 190 | .finally(() => { |
151 | if (!tokenSource.token.isCancellationRequested) { | 191 | if (file.inlaysRequest === tokenSource) { |
152 | this.pending.delete(documentUri); | 192 | file.inlaysRequest = null; |
153 | } | 193 | } |
154 | }); | 194 | }); |
155 | } | 195 | } |
156 | } | 196 | } |
197 | |||
198 | interface InlaysDecorations { | ||
199 | type: vscode.DecorationOptions[]; | ||
200 | param: vscode.DecorationOptions[]; | ||
201 | } | ||
202 | |||
203 | interface RustSourceFile { | ||
204 | /* | ||
205 | * Source of the token to cancel in-flight inlay hints request if any. | ||
206 | */ | ||
207 | inlaysRequest: null | vscode.CancellationTokenSource; | ||
208 | /** | ||
209 | * Last applied decorations. | ||
210 | */ | ||
211 | cachedDecorations: null | InlaysDecorations; | ||
212 | |||
213 | document: RustDocument; | ||
214 | } | ||
diff --git a/editors/code/src/installation/interfaces.ts b/editors/code/src/installation/interfaces.ts index e40839e4b..50b635921 100644 --- a/editors/code/src/installation/interfaces.ts +++ b/editors/code/src/installation/interfaces.ts | |||
@@ -14,14 +14,14 @@ export interface ArtifactReleaseInfo { | |||
14 | } | 14 | } |
15 | 15 | ||
16 | /** | 16 | /** |
17 | * Represents the source of a binary artifact which is either specified by the user | 17 | * Represents the source of a an artifact which is either specified by the user |
18 | * explicitly, or bundled by this extension from GitHub releases. | 18 | * explicitly, or bundled by this extension from GitHub releases. |
19 | */ | 19 | */ |
20 | export type BinarySource = BinarySource.ExplicitPath | BinarySource.GithubRelease; | 20 | export type ArtifactSource = ArtifactSource.ExplicitPath | ArtifactSource.GithubRelease; |
21 | 21 | ||
22 | export namespace BinarySource { | 22 | export namespace ArtifactSource { |
23 | /** | 23 | /** |
24 | * Type tag for `BinarySource` discriminated union. | 24 | * Type tag for `ArtifactSource` discriminated union. |
25 | */ | 25 | */ |
26 | export const enum Type { ExplicitPath, GithubRelease } | 26 | export const enum Type { ExplicitPath, GithubRelease } |
27 | 27 | ||
@@ -56,13 +56,18 @@ export namespace BinarySource { | |||
56 | /** | 56 | /** |
57 | * Tag of github release that denotes a version required by this extension. | 57 | * Tag of github release that denotes a version required by this extension. |
58 | */ | 58 | */ |
59 | version: string; | 59 | tag: string; |
60 | 60 | ||
61 | /** | 61 | /** |
62 | * Object that provides `get()/update()` operations to store metadata | 62 | * Object that provides `get()/update()` operations to store metadata |
63 | * about the actual binary, e.g. its actual version. | 63 | * about the actual binary, e.g. its actual version. |
64 | */ | 64 | */ |
65 | storage: vscode.Memento; | 65 | storage: vscode.Memento; |
66 | |||
67 | /** | ||
68 | * Ask for the user permission before downloading the artifact. | ||
69 | */ | ||
70 | askBeforeDownload: boolean; | ||
66 | } | 71 | } |
67 | 72 | ||
68 | } | 73 | } |
diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts index 6a6cf4f8c..ef1c45ff6 100644 --- a/editors/code/src/installation/server.ts +++ b/editors/code/src/installation/server.ts | |||
@@ -3,12 +3,12 @@ import * as path from "path"; | |||
3 | import { promises as dns } from "dns"; | 3 | import { promises as dns } from "dns"; |
4 | import { spawnSync } from "child_process"; | 4 | import { spawnSync } from "child_process"; |
5 | 5 | ||
6 | import { BinarySource } from "./interfaces"; | 6 | import { ArtifactSource } from "./interfaces"; |
7 | import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info"; | 7 | import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info"; |
8 | import { downloadArtifact } from "./download_artifact"; | 8 | import { downloadArtifact } from "./download_artifact"; |
9 | import { log, assert } from "../util"; | 9 | import { log, assert } from "../util"; |
10 | 10 | ||
11 | export async function ensureServerBinary(source: null | BinarySource): Promise<null | string> { | 11 | export async function ensureServerBinary(source: null | ArtifactSource): Promise<null | string> { |
12 | if (!source) { | 12 | if (!source) { |
13 | vscode.window.showErrorMessage( | 13 | vscode.window.showErrorMessage( |
14 | "Unfortunately we don't ship binaries for your platform yet. " + | 14 | "Unfortunately we don't ship binaries for your platform yet. " + |
@@ -22,7 +22,7 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n | |||
22 | } | 22 | } |
23 | 23 | ||
24 | switch (source.type) { | 24 | switch (source.type) { |
25 | case BinarySource.Type.ExplicitPath: { | 25 | case ArtifactSource.Type.ExplicitPath: { |
26 | if (isBinaryAvailable(source.path)) { | 26 | if (isBinaryAvailable(source.path)) { |
27 | return source.path; | 27 | return source.path; |
28 | } | 28 | } |
@@ -34,11 +34,11 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n | |||
34 | ); | 34 | ); |
35 | return null; | 35 | return null; |
36 | } | 36 | } |
37 | case BinarySource.Type.GithubRelease: { | 37 | case ArtifactSource.Type.GithubRelease: { |
38 | const prebuiltBinaryPath = path.join(source.dir, source.file); | 38 | const prebuiltBinaryPath = path.join(source.dir, source.file); |
39 | 39 | ||
40 | const installedVersion: null | string = getServerVersion(source.storage); | 40 | const installedVersion: null | string = getServerVersion(source.storage); |
41 | const requiredVersion: string = source.version; | 41 | const requiredVersion: string = source.tag; |
42 | 42 | ||
43 | log.debug("Installed version:", installedVersion, "required:", requiredVersion); | 43 | log.debug("Installed version:", installedVersion, "required:", requiredVersion); |
44 | 44 | ||
@@ -46,12 +46,14 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n | |||
46 | return prebuiltBinaryPath; | 46 | return prebuiltBinaryPath; |
47 | } | 47 | } |
48 | 48 | ||
49 | const userResponse = await vscode.window.showInformationMessage( | 49 | if (source.askBeforeDownload) { |
50 | `Language server version ${source.version} for rust-analyzer is not installed. ` + | 50 | const userResponse = await vscode.window.showInformationMessage( |
51 | "Do you want to download it now?", | 51 | `Language server version ${source.tag} for rust-analyzer is not installed. ` + |
52 | "Download now", "Cancel" | 52 | "Do you want to download it now?", |
53 | ); | 53 | "Download now", "Cancel" |
54 | if (userResponse !== "Download now") return null; | 54 | ); |
55 | if (userResponse !== "Download now") return null; | ||
56 | } | ||
55 | 57 | ||
56 | if (!await downloadServer(source)) return null; | 58 | if (!await downloadServer(source)) return null; |
57 | 59 | ||
@@ -60,9 +62,9 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n | |||
60 | } | 62 | } |
61 | } | 63 | } |
62 | 64 | ||
63 | async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> { | 65 | async function downloadServer(source: ArtifactSource.GithubRelease): Promise<boolean> { |
64 | try { | 66 | try { |
65 | const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.version); | 67 | const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.tag); |
66 | 68 | ||
67 | await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); | 69 | await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); |
68 | await setServerVersion(source.storage, releaseInfo.releaseName); | 70 | await setServerVersion(source.storage, releaseInfo.releaseName); |
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts index c5a010e94..bd6e3ada0 100644 --- a/editors/code/src/rust-analyzer-api.ts +++ b/editors/code/src/rust-analyzer-api.ts | |||
@@ -86,14 +86,20 @@ export interface Runnable { | |||
86 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); | 86 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); |
87 | 87 | ||
88 | 88 | ||
89 | export const enum InlayKind { | 89 | |
90 | TypeHint = "TypeHint", | 90 | export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint; |
91 | ParameterHint = "ParameterHint", | 91 | |
92 | } | 92 | export namespace InlayHint { |
93 | export interface InlayHint { | 93 | export const enum Kind { |
94 | range: lc.Range; | 94 | TypeHint = "TypeHint", |
95 | kind: InlayKind; | 95 | ParamHint = "ParameterHint", |
96 | label: string; | 96 | } |
97 | interface Common { | ||
98 | range: lc.Range; | ||
99 | label: string; | ||
100 | } | ||
101 | export type TypeHint = Common & { kind: Kind.TypeHint }; | ||
102 | export type ParamHint = Common & { kind: Kind.ParamHint }; | ||
97 | } | 103 | } |
98 | export interface InlayHintsParams { | 104 | export interface InlayHintsParams { |
99 | textDocument: lc.TextDocumentIdentifier; | 105 | textDocument: lc.TextDocumentIdentifier; |
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 7c95769bb..95a5f1227 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import * as lc from "vscode-languageclient"; | 1 | import * as lc from "vscode-languageclient"; |
2 | import * as vscode from "vscode"; | 2 | import * as vscode from "vscode"; |
3 | import { strict as nativeAssert } from "assert"; | 3 | import { strict as nativeAssert } from "assert"; |
4 | import { TextDocument } from "vscode"; | ||
5 | 4 | ||
6 | export function assert(condition: boolean, explanation: string): asserts condition { | 5 | export function assert(condition: boolean, explanation: string): asserts condition { |
7 | try { | 6 | try { |
@@ -67,9 +66,16 @@ function sleep(ms: number) { | |||
67 | return new Promise(resolve => setTimeout(resolve, ms)); | 66 | return new Promise(resolve => setTimeout(resolve, ms)); |
68 | } | 67 | } |
69 | 68 | ||
70 | export function isRustDocument(document: TextDocument) { | 69 | export type RustDocument = vscode.TextDocument & { languageId: "rust" }; |
70 | export type RustEditor = vscode.TextEditor & { document: RustDocument; id: string }; | ||
71 | |||
72 | export function isRustDocument(document: vscode.TextDocument): document is RustDocument { | ||
71 | return document.languageId === 'rust' | 73 | return document.languageId === 'rust' |
72 | // SCM diff views have the same URI as the on-disk document but not the same content | 74 | // SCM diff views have the same URI as the on-disk document but not the same content |
73 | && document.uri.scheme !== 'git' | 75 | && document.uri.scheme !== 'git' |
74 | && document.uri.scheme !== 'svn'; | 76 | && document.uri.scheme !== 'svn'; |
75 | } \ No newline at end of file | 77 | } |
78 | |||
79 | export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { | ||
80 | return isRustDocument(editor.document); | ||
81 | } | ||
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index 12bad820f..bb3593b11 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs | |||
@@ -27,7 +27,13 @@ fn dist_client(nightly: bool) -> Result<()> { | |||
27 | let _restore = | 27 | let _restore = |
28 | Restore { path: package_json_path.clone(), contents: original_package_json.clone() }; | 28 | Restore { path: package_json_path.clone(), contents: original_package_json.clone() }; |
29 | 29 | ||
30 | let mut package_json = original_package_json.replace(r#""enableProposedApi": true,"#, r#""#); | 30 | let date = run!("date --utc +%Y%m%d")?; |
31 | let version_suffix = if nightly { "-nightly" } else { "" }; | ||
32 | |||
33 | let mut package_json = original_package_json.replace( | ||
34 | r#""version": "0.2.20200211-dev""#, | ||
35 | &format!(r#""version": "0.1.{}{}""#, date, version_suffix), | ||
36 | ); | ||
31 | 37 | ||
32 | if nightly { | 38 | if nightly { |
33 | package_json = package_json.replace( | 39 | package_json = package_json.replace( |
@@ -35,7 +41,7 @@ fn dist_client(nightly: bool) -> Result<()> { | |||
35 | r#""displayName": "rust-analyzer nightly""#, | 41 | r#""displayName": "rust-analyzer nightly""#, |
36 | ); | 42 | ); |
37 | } else { | 43 | } else { |
38 | package_json = original_package_json.replace(r#""enableProposedApi": true,"#, r#""#); | 44 | package_json = package_json.replace(r#""enableProposedApi": true,"#, r#""#); |
39 | } | 45 | } |
40 | fs2::write(package_json_path, package_json)?; | 46 | fs2::write(package_json_path, package_json)?; |
41 | 47 | ||
@@ -47,10 +53,16 @@ fn dist_client(nightly: bool) -> Result<()> { | |||
47 | fn dist_server() -> Result<()> { | 53 | fn dist_server() -> Result<()> { |
48 | if cfg!(target_os = "linux") { | 54 | if cfg!(target_os = "linux") { |
49 | std::env::set_var("CC", "clang"); | 55 | std::env::set_var("CC", "clang"); |
50 | run!("cargo build --package rust-analyzer --bin rust-analyzer --release --target x86_64-unknown-linux-musl")?; | 56 | run!( |
57 | "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release | ||
58 | --target x86_64-unknown-linux-musl | ||
59 | " | ||
60 | // We'd want to add, but that requires setting the right linker somehow | ||
61 | // --features=jemalloc | ||
62 | )?; | ||
51 | run!("strip ./target/x86_64-unknown-linux-musl/release/rust-analyzer")?; | 63 | run!("strip ./target/x86_64-unknown-linux-musl/release/rust-analyzer")?; |
52 | } else { | 64 | } else { |
53 | run!("cargo build --package rust-analyzer --bin rust-analyzer --release")?; | 65 | run!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release")?; |
54 | } | 66 | } |
55 | 67 | ||
56 | let (src, dst) = if cfg!(target_os = "linux") { | 68 | let (src, dst) = if cfg!(target_os = "linux") { |