aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yaml9
-rw-r--r--.vscode/launch.json339
-rw-r--r--.vscode/tasks.json56
-rw-r--r--Cargo.lock21
-rw-r--r--crates/ra_assists/src/assists/add_explicit_type.rs18
-rw-r--r--crates/ra_cargo_watch/Cargo.toml17
-rw-r--r--crates/ra_cargo_watch/src/conv.rs359
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap85
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap46
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap61
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap46
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap46
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap70
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap65
-rw-r--r--crates/ra_cargo_watch/src/conv/test.rs929
-rw-r--r--crates/ra_cargo_watch/src/lib.rs394
-rw-r--r--crates/ra_hir/src/code_model.rs19
-rw-r--r--crates/ra_hir/src/lib.rs4
-rw-r--r--crates/ra_hir/src/source_binder.rs26
-rw-r--r--crates/ra_hir_def/src/adt.rs44
-rw-r--r--crates/ra_hir_def/src/body.rs6
-rw-r--r--crates/ra_hir_def/src/body/lower.rs25
-rw-r--r--crates/ra_hir_def/src/item_scope.rs33
-rw-r--r--crates/ra_hir_def/src/lang_item.rs44
-rw-r--r--crates/ra_hir_def/src/lib.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs109
-rw-r--r--crates/ra_hir_def/src/nameres/path_resolution.rs72
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs35
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs6
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs97
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs4
-rw-r--r--crates/ra_hir_def/src/path.rs4
-rw-r--r--crates/ra_hir_def/src/per_ns.rs50
-rw-r--r--crates/ra_hir_def/src/resolver.rs24
-rw-r--r--crates/ra_hir_def/src/visibility.rs120
-rw-r--r--crates/ra_hir_ty/src/infer.rs23
-rw-r--r--crates/ra_hir_ty/src/tests.rs80
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs36
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs5
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs57
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html6
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html6
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs27
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/caps.rs4
-rw-r--r--crates/ra_lsp_server/src/config.rs9
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs59
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs28
-rw-r--r--crates/ra_lsp_server/src/req.rs8
-rw-r--r--crates/ra_lsp_server/src/world.rs72
-rw-r--r--crates/ra_syntax/src/ast.rs4
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs29
-rw-r--r--crates/ra_syntax/src/ast/generated.rs3
-rw-r--r--crates/ra_syntax/src/grammar.ron6
-rw-r--r--docs/user/README.md8
-rw-r--r--editors/code/.prettierignore2
-rw-r--r--editors/code/.vscode/launch.json31
-rw-r--r--editors/code/.vscode/settings.json11
-rw-r--r--editors/code/.vscode/tasks.json20
-rw-r--r--editors/code/.vscodeignore13
-rw-r--r--editors/code/package-lock.json1127
-rw-r--r--editors/code/package.json317
-rw-r--r--editors/code/rollup.config.js29
-rw-r--r--editors/code/src/client.ts90
-rw-r--r--editors/code/src/color_theme.ts123
-rw-r--r--editors/code/src/commands/analyzer_status.ts74
-rw-r--r--editors/code/src/commands/cargo_watch.ts264
-rw-r--r--editors/code/src/commands/expand_macro.ts103
-rw-r--r--editors/code/src/commands/index.ts66
-rw-r--r--editors/code/src/commands/inlay_hints.ts115
-rw-r--r--editors/code/src/commands/join_lines.ts46
-rw-r--r--editors/code/src/commands/line_buffer.ts16
-rw-r--r--editors/code/src/commands/matching_brace.ts58
-rw-r--r--editors/code/src/commands/on_enter.ts53
-rw-r--r--editors/code/src/commands/parent_module.ts55
-rw-r--r--editors/code/src/commands/runnables.ts214
-rw-r--r--editors/code/src/commands/syntaxTree.ts76
-rw-r--r--editors/code/src/commands/syntax_tree.ts104
-rw-r--r--editors/code/src/commands/watch_status.ts63
-rw-r--r--editors/code/src/config.ts87
-rw-r--r--editors/code/src/ctx.ts112
-rw-r--r--editors/code/src/events/change_active_text_editor.ts32
-rw-r--r--editors/code/src/events/change_text_document.ts24
-rw-r--r--editors/code/src/events/index.ts4
-rw-r--r--editors/code/src/extension.ts218
-rw-r--r--editors/code/src/highlighting.ts209
-rw-r--r--editors/code/src/inlay_hints.ts120
-rw-r--r--editors/code/src/main.ts51
-rw-r--r--editors/code/src/notifications/index.ts3
-rw-r--r--editors/code/src/notifications/publish_decorations.ts24
-rw-r--r--editors/code/src/server.ts109
-rw-r--r--editors/code/src/source_change.ts (renamed from editors/code/src/commands/apply_source_change.ts)13
-rw-r--r--editors/code/src/status_display.ts115
-rw-r--r--editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json110
-rw-r--r--editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json42
-rw-r--r--editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json114
-rw-r--r--editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json261
-rw-r--r--editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json33
-rw-r--r--editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json72
-rw-r--r--editors/code/src/test/runTest.ts22
-rw-r--r--editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts134
-rw-r--r--editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts127
-rw-r--r--editors/code/src/test/utils/diagnotics/rust.test.ts236
-rw-r--r--editors/code/src/test/utils/diagnotics/vscode.test.ts98
-rw-r--r--editors/code/src/test/utils/index.ts49
-rw-r--r--editors/code/src/utils/diagnostics/SuggestedFix.ts67
-rw-r--r--editors/code/src/utils/diagnostics/SuggestedFixCollection.ts77
-rw-r--r--editors/code/src/utils/diagnostics/rust.ts299
-rw-r--r--editors/code/src/utils/diagnostics/vscode.ts14
-rw-r--r--editors/code/src/utils/processes.ts51
-rw-r--r--editors/code/src/utils/terminateProcess.sh12
-rw-r--r--editors/code/tsconfig.json11
-rw-r--r--editors/code/tslint.json15
-rw-r--r--xtask/tests/tidy-tests/cli.rs2
115 files changed, 4550 insertions, 5247 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index cb397ae14..1d24295ec 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -67,14 +67,7 @@ jobs:
67 with: 67 with:
68 node-version: 12.x 68 node-version: 12.x
69 69
70 - name: Install xvfb
71 run: sudo apt-get install xvfb
72 - run: npm ci 70 - run: npm ci
73 working-directory: ./editors/code 71 working-directory: ./editors/code
74 - run: npm run vscode:prepublish 72 - run: npm run package --scripts-prepend-node-path
75 working-directory: ./editors/code 73 working-directory: ./editors/code
76 - run: xvfb-run --auto-servernum npm run travis
77 working-directory: ./editors/code
78
79 - name: Cleanup xvfb
80 uses: bcomnes/cleanup-xvfb@v1
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 0cf3984a9..55a2f10f2 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,19 +5,33 @@
5 "version": "0.2.0", 5 "version": "0.2.0",
6 "configurations": [ 6 "configurations": [
7 { 7 {
8 "name": "Debug Extension", 8 "name": "Run Extension",
9 "type": "extensionHost", 9 "type": "extensionHost",
10 "request": "launch", 10 "request": "launch",
11 "runtimeExecutable": "${execPath}", 11 "runtimeExecutable": "${execPath}",
12 "args": [ 12 "args": [
13 "--extensionDevelopmentPath=${workspaceFolder}/editors/code", 13 "--extensionDevelopmentPath=${workspaceFolder}/editors/code"
14 "--disable-extensions"
15 ], 14 ],
15 "outFiles": [
16 "${workspaceFolder}/editors/code/out/**/*.js"
17 ],
18 "preLaunchTask": "Build Extension"
19 },
20 {
21 "name": "Run Extension (Dev Server)",
22 "type": "extensionHost",
23 "request": "launch",
24 "runtimeExecutable": "${execPath}",
25 "args": [
26 "--extensionDevelopmentPath=${workspaceFolder}/editors/code"
27 ],
28 "outFiles": [
29 "${workspaceFolder}/editors/code/out/**/*.js"
30 ],
31 "preLaunchTask": "Build Extension",
16 "env": { 32 "env": {
17 "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server" 33 "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server"
18 }, 34 }
19 "outFiles": ["${workspaceFolder}/editors/code/bundle/**/*.js"],
20 "preLaunchTask": "Build All"
21 }, 35 },
22 { 36 {
23 "name": "Debug Lsp Server", 37 "name": "Debug Lsp Server",
@@ -25,316 +39,9 @@
25 "request": "attach", 39 "request": "attach",
26 "program": "${workspaceFolder}/target/debug/ra_lsp_server", 40 "program": "${workspaceFolder}/target/debug/ra_lsp_server",
27 "pid": "${command:pickMyProcess}", 41 "pid": "${command:pickMyProcess}",
28 "sourceLanguages": ["rust"] 42 "sourceLanguages": [
29 }, 43 "rust"
30 { 44 ]
31 "type": "lldb",
32 "request": "launch",
33 "name": "Debug unit tests in library 'gen_lsp_server'",
34 "cargo": {
35 "args": ["test", "--no-run", "--lib", "--package=gen_lsp_server"],
36 "filter": {
37 "kind": "lib"
38 }
39 },
40 "args": [],
41 "cwd": "${workspaceFolder}"
42 },
43 {
44 "type": "lldb",
45 "request": "launch",
46 "name": "Debug unit tests in library 'ra_analysis'",
47 "cargo": {
48 "args": ["test", "--no-run", "--lib", "--package=ra_analysis"],
49 "filter": {
50 "kind": "lib"
51 }
52 },
53 "args": [],
54 "cwd": "${workspaceFolder}"
55 },
56 {
57 "type": "lldb",
58 "request": "launch",
59 "name": "Debug test 'tests'",
60 "cargo": {
61 "args": ["build", "--test=tests", "--package=ra_analysis"],
62 "filter": {
63 "kind": "bin"
64 }
65 },
66 "args": [],
67 "cwd": "${workspaceFolder}"
68 },
69 {
70 "type": "lldb",
71 "request": "launch",
72 "name": "Debug unit tests in test 'tests'",
73 "cargo": {
74 "args": ["test", "--no-run", "--test=tests", "--package=ra_analysis"],
75 "filter": {
76 "kind": "bin"
77 }
78 },
79 "args": [],
80 "cwd": "${workspaceFolder}"
81 },
82 {
83 "type": "lldb",
84 "request": "launch",
85 "name": "Debug unit tests in library 'ra_hir'",
86 "cargo": {
87 "args": ["test", "--no-run", "--lib", "--package=ra_hir"],
88 "filter": {
89 "kind": "lib"
90 }
91 },
92 "args": [],
93 "cwd": "${workspaceFolder}"
94 },
95 {
96 "type": "lldb",
97 "request": "launch",
98 "name": "Debug unit tests in library 'ra_db'",
99 "cargo": {
100 "args": ["test", "--no-run", "--lib", "--package=ra_db"],
101 "filter": {
102 "kind": "lib"
103 }
104 },
105 "args": [],
106 "cwd": "${workspaceFolder}"
107 },
108 {
109 "type": "lldb",
110 "request": "launch",
111 "name": "Debug unit tests in library 'ra_editor'",
112 "cargo": {
113 "args": ["test", "--no-run", "--lib", "--package=ra_editor"],
114 "filter": {
115 "kind": "lib"
116 }
117 },
118 "args": [],
119 "cwd": "${workspaceFolder}"
120 },
121 {
122 "type": "lldb",
123 "request": "launch",
124 "name": "Debug unit tests in library 'ra_syntax'",
125 "cargo": {
126 "args": ["test", "--no-run", "--lib", "--package=ra_syntax"],
127 "filter": {
128 "kind": "lib"
129 }
130 },
131 "args": [],
132 "cwd": "${workspaceFolder}"
133 },
134 {
135 "type": "lldb",
136 "request": "launch",
137 "name": "Debug test 'test'",
138 "cargo": {
139 "args": ["build", "--test=test", "--package=ra_syntax"],
140 "filter": {
141 "kind": "bin"
142 }
143 },
144 "args": [],
145 "cwd": "${workspaceFolder}"
146 },
147 {
148 "type": "lldb",
149 "request": "launch",
150 "name": "Debug unit tests in test 'test'",
151 "cargo": {
152 "args": ["test", "--no-run", "--test=test", "--package=ra_syntax"],
153 "filter": {
154 "kind": "bin"
155 }
156 },
157 "args": [],
158 "cwd": "${workspaceFolder}"
159 },
160 {
161 "type": "lldb",
162 "request": "launch",
163 "name": "Debug unit tests in library 'test_utils'",
164 "cargo": {
165 "args": ["test", "--no-run", "--lib", "--package=test_utils"],
166 "filter": {
167 "kind": "lib"
168 }
169 },
170 "args": [],
171 "cwd": "${workspaceFolder}"
172 },
173 {
174 "type": "lldb",
175 "request": "launch",
176 "name": "Debug executable 'ra_cli'",
177 "cargo": {
178 "args": ["build", "--bin=ra_cli", "--package=ra_cli"],
179 "filter": {
180 "kind": "bin"
181 }
182 },
183 "args": [],
184 "cwd": "${workspaceFolder}"
185 },
186 {
187 "type": "lldb",
188 "request": "launch",
189 "name": "Debug unit tests in executable 'ra_cli'",
190 "cargo": {
191 "args": ["test", "--no-run", "--bin=ra_cli", "--package=ra_cli"],
192 "filter": {
193 "kind": "bin"
194 }
195 },
196 "args": [],
197 "cwd": "${workspaceFolder}"
198 },
199 {
200 "type": "lldb",
201 "request": "launch",
202 "name": "Debug unit tests in library 'tools'",
203 "cargo": {
204 "args": ["test", "--no-run", "--lib", "--package=tools"],
205 "filter": {
206 "kind": "lib"
207 }
208 },
209 "args": [],
210 "cwd": "${workspaceFolder}"
211 },
212 {
213 "type": "lldb",
214 "request": "launch",
215 "name": "Debug executable 'tools'",
216 "cargo": {
217 "args": ["build", "--bin=tools", "--package=tools"],
218 "filter": {
219 "kind": "bin"
220 }
221 },
222 "args": [],
223 "cwd": "${workspaceFolder}"
224 },
225 {
226 "type": "lldb",
227 "request": "launch",
228 "name": "Debug unit tests in executable 'tools'",
229 "cargo": {
230 "args": ["test", "--no-run", "--bin=tools", "--package=tools"],
231 "filter": {
232 "kind": "bin"
233 }
234 },
235 "args": [],
236 "cwd": "${workspaceFolder}"
237 },
238 {
239 "type": "lldb",
240 "request": "launch",
241 "name": "Debug test 'cli'",
242 "cargo": {
243 "args": ["build", "--test=cli", "--package=tools"],
244 "filter": {
245 "kind": "bin"
246 }
247 },
248 "args": [],
249 "cwd": "${workspaceFolder}"
250 }, 45 },
251 {
252 "type": "lldb",
253 "request": "launch",
254 "name": "Debug unit tests in test 'cli'",
255 "cargo": {
256 "args": ["test", "--no-run", "--test=cli", "--package=tools"],
257 "filter": {
258 "kind": "bin"
259 }
260 },
261 "args": [],
262 "cwd": "${workspaceFolder}"
263 },
264 {
265 "type": "lldb",
266 "request": "launch",
267 "name": "Debug unit tests in library 'ra_lsp_server'",
268 "cargo": {
269 "args": ["test", "--no-run", "--lib", "--package=ra_lsp_server"],
270 "filter": {
271 "kind": "lib"
272 }
273 },
274 "args": [],
275 "cwd": "${workspaceFolder}"
276 },
277 {
278 "type": "lldb",
279 "request": "launch",
280 "name": "Debug executable 'ra_lsp_server'",
281 "cargo": {
282 "args": ["build", "--bin=ra_lsp_server", "--package=ra_lsp_server"],
283 "filter": {
284 "kind": "bin"
285 }
286 },
287 "args": [],
288 "cwd": "${workspaceFolder}"
289 },
290 {
291 "type": "lldb",
292 "request": "launch",
293 "name": "Debug unit tests in executable 'ra_lsp_server'",
294 "cargo": {
295 "args": [
296 "test",
297 "--no-run",
298 "--bin=ra_lsp_server",
299 "--package=ra_lsp_server"
300 ],
301 "filter": {
302 "kind": "bin"
303 }
304 },
305 "args": [],
306 "cwd": "${workspaceFolder}"
307 },
308 {
309 "type": "lldb",
310 "request": "launch",
311 "name": "Debug test 'heavy_tests'",
312 "cargo": {
313 "args": ["build", "--test=heavy_tests", "--package=ra_lsp_server"],
314 "filter": {
315 "kind": "bin"
316 }
317 },
318 "args": [],
319 "cwd": "${workspaceFolder}"
320 },
321 {
322 "type": "lldb",
323 "request": "launch",
324 "name": "Debug unit tests in test 'heavy_tests'",
325 "cargo": {
326 "args": [
327 "test",
328 "--no-run",
329 "--test=heavy_tests",
330 "--package=ra_lsp_server"
331 ],
332 "filter": {
333 "kind": "bin"
334 }
335 },
336 "args": [],
337 "cwd": "${workspaceFolder}"
338 }
339 ] 46 ]
340} 47}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 063cbd174..fc9a8593b 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -4,57 +4,23 @@
4 "version": "2.0.0", 4 "version": "2.0.0",
5 "tasks": [ 5 "tasks": [
6 { 6 {
7 "type": "npm",
8 "script": "compile",
9 "label": "Build Extension", 7 "label": "Build Extension",
8 "group": "build",
9 "type": "npm",
10 "script": "watch",
11 "path": "editors/code/",
10 "problemMatcher": { 12 "problemMatcher": {
11 "owner": "typescript", 13 "base": "$tsc-watch",
12 "pattern": "$tsc", 14 "fileLocation": ["relative", "${workspaceFolder}/editors/code/"]
13 "fileLocation": [
14 "relative",
15 "${workspaceRoot}/editors/code"
16 ]
17 }, 15 },
18 "path": "editors/code/"
19 },
20 {
21 "label": "Build Lsp",
22 "type": "shell",
23 "command": "cargo build",
24 "problemMatcher": "$rustc"
25 },
26 {
27 "label": "Build All",
28 "group": "build",
29 "dependsOn": [
30 "Build Extension",
31 "Build Lsp"
32 ],
33 "problemMatcher": []
34 },
35 {
36 "label": "cargo watch",
37 "group": "build",
38 "isBackground": true, 16 "isBackground": true,
39 "type": "shell",
40 "command": "cargo",
41 "args": [
42 "watch"
43 ],
44 "problemMatcher": "$rustc-watch"
45 }, 17 },
46 { 18 {
47 "label": "cargo watch tests", 19 "label": "Build Server",
48 "group": "build", 20 "group": "build",
49 "isBackground": true,
50 "type": "shell", 21 "type": "shell",
51 "command": "cargo", 22 "command": "cargo build --package ra_lsp_server",
52 "args": [ 23 "problemMatcher": "$rustc"
53 "watch", 24 },
54 "-x",
55 "check --tests"
56 ],
57 "problemMatcher": "$rustc-watch"
58 }
59 ] 25 ]
60} \ No newline at end of file 26}
diff --git a/Cargo.lock b/Cargo.lock
index 3314ef70f..c21b74340 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -615,7 +615,7 @@ dependencies = [
615 615
616[[package]] 616[[package]]
617name = "lsp-server" 617name = "lsp-server"
618version = "0.3.0" 618version = "0.3.1"
619source = "registry+https://github.com/rust-lang/crates.io-index" 619source = "registry+https://github.com/rust-lang/crates.io-index"
620dependencies = [ 620dependencies = [
621 "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 621 "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -899,6 +899,20 @@ dependencies = [
899] 899]
900 900
901[[package]] 901[[package]]
902name = "ra_cargo_watch"
903version = "0.1.0"
904dependencies = [
905 "cargo_metadata 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
906 "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
907 "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
908 "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
909 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
910 "lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)",
911 "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
912 "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
913]
914
915[[package]]
902name = "ra_cfg" 916name = "ra_cfg"
903version = "0.1.0" 917version = "0.1.0"
904dependencies = [ 918dependencies = [
@@ -1056,9 +1070,10 @@ dependencies = [
1056 "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 1070 "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
1057 "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1071 "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1058 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 1072 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
1059 "lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1073 "lsp-server 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
1060 "lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)", 1074 "lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)",
1061 "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 1075 "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
1076 "ra_cargo_watch 0.1.0",
1062 "ra_ide 0.1.0", 1077 "ra_ide 0.1.0",
1063 "ra_prof 0.1.0", 1078 "ra_prof 0.1.0",
1064 "ra_project_model 0.1.0", 1079 "ra_project_model 0.1.0",
@@ -1849,7 +1864,7 @@ dependencies = [
1849"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" 1864"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
1850"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" 1865"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586"
1851"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 1866"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
1852"checksum lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba36405bd742139ab79c246ca5adb7fde2fe1a0f495e2c8e2f607b607dedb12" 1867"checksum lsp-server 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5383e043329615624bbf45e1ba27bd75c176762b2592855c659bc28ac580a06b"
1853"checksum lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aea9639ebf210bd5de66931cbdb2d4a8bcc1fa1e5b2dece7daa6b387ab42e803" 1868"checksum lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aea9639ebf210bd5de66931cbdb2d4a8bcc1fa1e5b2dece7daa6b387ab42e803"
1854"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 1869"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
1855"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" 1870"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs
index eeb4ff39f..2c602a79e 100644
--- a/crates/ra_assists/src/assists/add_explicit_type.rs
+++ b/crates/ra_assists/src/assists/add_explicit_type.rs
@@ -74,6 +74,24 @@ mod tests {
74 } 74 }
75 75
76 #[test] 76 #[test]
77 fn add_explicit_type_works_for_macro_call() {
78 check_assist(
79 add_explicit_type,
80 "macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
81 "macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
82 );
83 }
84
85 #[test]
86 fn add_explicit_type_works_for_macro_call_recursive() {
87 check_assist(
88 add_explicit_type,
89 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
90 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
91 );
92 }
93
94 #[test]
77 fn add_explicit_type_not_applicable_if_ty_not_inferred() { 95 fn add_explicit_type_not_applicable_if_ty_not_inferred() {
78 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); 96 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
79 } 97 }
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml
new file mode 100644
index 000000000..bcc4648ff
--- /dev/null
+++ b/crates/ra_cargo_watch/Cargo.toml
@@ -0,0 +1,17 @@
1[package]
2edition = "2018"
3name = "ra_cargo_watch"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[dependencies]
8crossbeam-channel = "0.4"
9lsp-types = { version = "0.67.0", features = ["proposed"] }
10log = "0.4.3"
11cargo_metadata = "0.9.1"
12jod-thread = "0.1.0"
13parking_lot = "0.10.0"
14
15[dev-dependencies]
16insta = "0.12.0"
17serde_json = "1.0" \ No newline at end of file
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs
new file mode 100644
index 000000000..ac0f1d28a
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv.rs
@@ -0,0 +1,359 @@
1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format.
3use cargo_metadata::diagnostic::{
4 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
5 DiagnosticSpanMacroExpansion,
6};
7use lsp_types::{
8 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
9 NumberOrString, Position, Range, Url,
10};
11use std::{
12 fmt::Write,
13 path::{Component, Path, PathBuf, Prefix},
14 str::FromStr,
15};
16
17#[cfg(test)]
18mod test;
19
20/// Converts a Rust level string to a LSP severity
21fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
22 match val {
23 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error),
24 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error),
25 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning),
26 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information),
27 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint),
28 DiagnosticLevel::Unknown => None,
29 }
30}
31
32/// Check whether a file name is from macro invocation
33fn is_from_macro(file_name: &str) -> bool {
34 file_name.starts_with('<') && file_name.ends_with('>')
35}
36
37/// Converts a Rust macro span to a LSP location recursively
38fn map_macro_span_to_location(
39 span_macro: &DiagnosticSpanMacroExpansion,
40 workspace_root: &PathBuf,
41) -> Option<Location> {
42 if !is_from_macro(&span_macro.span.file_name) {
43 return Some(map_span_to_location(&span_macro.span, workspace_root));
44 }
45
46 if let Some(expansion) = &span_macro.span.expansion {
47 return map_macro_span_to_location(&expansion, workspace_root);
48 }
49
50 None
51}
52
53/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
54fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
55 if span.expansion.is_some() {
56 let expansion = span.expansion.as_ref().unwrap();
57 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
58 return macro_range;
59 }
60 }
61
62 map_span_to_location_naive(span, workspace_root)
63}
64
65/// Converts a Rust span to a LSP location
66fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
67 let mut file_name = workspace_root.clone();
68 file_name.push(&span.file_name);
69 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
70
71 let range = Range::new(
72 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
73 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
74 );
75
76 Location { uri, range }
77}
78
79/// Converts a secondary Rust span to a LSP related information
80///
81/// If the span is unlabelled this will return `None`.
82fn map_secondary_span_to_related(
83 span: &DiagnosticSpan,
84 workspace_root: &PathBuf,
85) -> Option<DiagnosticRelatedInformation> {
86 if let Some(label) = &span.label {
87 let location = map_span_to_location(span, workspace_root);
88 Some(DiagnosticRelatedInformation { location, message: label.clone() })
89 } else {
90 // Nothing to label this with
91 None
92 }
93}
94
95/// Determines if diagnostic is related to unused code
96fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool {
97 if let Some(code) = &rd.code {
98 match code.code.as_str() {
99 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
100 | "unused_imports" | "unused_macros" | "unused_variables" => true,
101 _ => false,
102 }
103 } else {
104 false
105 }
106}
107
108/// Determines if diagnostic is related to deprecated code
109fn is_deprecated(rd: &RustDiagnostic) -> bool {
110 if let Some(code) = &rd.code {
111 match code.code.as_str() {
112 "deprecated" => true,
113 _ => false,
114 }
115 } else {
116 false
117 }
118}
119
120#[derive(Debug)]
121pub struct SuggestedFix {
122 pub title: String,
123 pub location: Location,
124 pub replacement: String,
125 pub applicability: Applicability,
126 pub diagnostics: Vec<Diagnostic>,
127}
128
129impl std::cmp::PartialEq<SuggestedFix> for SuggestedFix {
130 fn eq(&self, other: &SuggestedFix) -> bool {
131 if self.title == other.title
132 && self.location == other.location
133 && self.replacement == other.replacement
134 {
135 // Applicability doesn't impl PartialEq...
136 match (&self.applicability, &other.applicability) {
137 (Applicability::MachineApplicable, Applicability::MachineApplicable) => true,
138 (Applicability::HasPlaceholders, Applicability::HasPlaceholders) => true,
139 (Applicability::MaybeIncorrect, Applicability::MaybeIncorrect) => true,
140 (Applicability::Unspecified, Applicability::Unspecified) => true,
141 _ => false,
142 }
143 } else {
144 false
145 }
146 }
147}
148
149enum MappedRustChildDiagnostic {
150 Related(DiagnosticRelatedInformation),
151 SuggestedFix(SuggestedFix),
152 MessageLine(String),
153}
154
155fn map_rust_child_diagnostic(
156 rd: &RustDiagnostic,
157 workspace_root: &PathBuf,
158) -> MappedRustChildDiagnostic {
159 let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) {
160 Some(span) => span,
161 None => {
162 // `rustc` uses these spanless children as a way to print multi-line
163 // messages
164 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
165 }
166 };
167
168 // If we have a primary span use its location, otherwise use the parent
169 let location = map_span_to_location(&span, workspace_root);
170
171 if let Some(suggested_replacement) = &span.suggested_replacement {
172 // Include our replacement in the title unless it's empty
173 let title = if !suggested_replacement.is_empty() {
174 format!("{}: '{}'", rd.message, suggested_replacement)
175 } else {
176 rd.message.clone()
177 };
178
179 MappedRustChildDiagnostic::SuggestedFix(SuggestedFix {
180 title,
181 location,
182 replacement: suggested_replacement.clone(),
183 applicability: span.suggestion_applicability.clone().unwrap_or(Applicability::Unknown),
184 diagnostics: vec![],
185 })
186 } else {
187 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
188 location,
189 message: rd.message.clone(),
190 })
191 }
192}
193
194#[derive(Debug)]
195pub(crate) struct MappedRustDiagnostic {
196 pub location: Location,
197 pub diagnostic: Diagnostic,
198 pub suggested_fixes: Vec<SuggestedFix>,
199}
200
201/// Converts a Rust root diagnostic to LSP form
202///
203/// This flattens the Rust diagnostic by:
204///
205/// 1. Creating a LSP diagnostic with the root message and primary span.
206/// 2. Adding any labelled secondary spans to `relatedInformation`
207/// 3. Categorising child diagnostics as either `SuggestedFix`es,
208/// `relatedInformation` or additional message lines.
209///
210/// If the diagnostic has no primary span this will return `None`
211pub(crate) fn map_rust_diagnostic_to_lsp(
212 rd: &RustDiagnostic,
213 workspace_root: &PathBuf,
214) -> Option<MappedRustDiagnostic> {
215 let primary_span = rd.spans.iter().find(|s| s.is_primary)?;
216
217 let location = map_span_to_location(&primary_span, workspace_root);
218
219 let severity = map_level_to_severity(rd.level);
220 let mut primary_span_label = primary_span.label.as_ref();
221
222 let mut source = String::from("rustc");
223 let mut code = rd.code.as_ref().map(|c| c.code.clone());
224 if let Some(code_val) = &code {
225 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
226 let scoped_code: Vec<&str> = code_val.split("::").collect();
227 if scoped_code.len() == 2 {
228 source = String::from(scoped_code[0]);
229 code = Some(String::from(scoped_code[1]));
230 }
231 }
232
233 let mut related_information = vec![];
234 let mut tags = vec![];
235
236 // If error occurs from macro expansion, add related info pointing to
237 // where the error originated
238 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
239 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
240 related_information.push(DiagnosticRelatedInformation {
241 location: def_loc,
242 message: "Error originated from macro here".to_string(),
243 });
244 }
245
246 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
247 let related = map_secondary_span_to_related(secondary_span, workspace_root);
248 if let Some(related) = related {
249 related_information.push(related);
250 }
251 }
252
253 let mut suggested_fixes = vec![];
254 let mut message = rd.message.clone();
255 for child in &rd.children {
256 let child = map_rust_child_diagnostic(&child, workspace_root);
257 match child {
258 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
259 MappedRustChildDiagnostic::SuggestedFix(suggested_fix) => {
260 suggested_fixes.push(suggested_fix)
261 }
262 MappedRustChildDiagnostic::MessageLine(message_line) => {
263 write!(&mut message, "\n{}", message_line).unwrap();
264
265 // These secondary messages usually duplicate the content of the
266 // primary span label.
267 primary_span_label = None;
268 }
269 }
270 }
271
272 if let Some(primary_span_label) = primary_span_label {
273 write!(&mut message, "\n{}", primary_span_label).unwrap();
274 }
275
276 if is_unused_or_unnecessary(rd) {
277 tags.push(DiagnosticTag::Unnecessary);
278 }
279
280 if is_deprecated(rd) {
281 tags.push(DiagnosticTag::Deprecated);
282 }
283
284 let diagnostic = Diagnostic {
285 range: location.range,
286 severity,
287 code: code.map(NumberOrString::String),
288 source: Some(source),
289 message,
290 related_information: if !related_information.is_empty() {
291 Some(related_information)
292 } else {
293 None
294 },
295 tags: if !tags.is_empty() { Some(tags) } else { None },
296 };
297
298 Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes })
299}
300
301/// Returns a `Url` object from a given path, will lowercase drive letters if present.
302/// This will only happen when processing windows paths.
303///
304/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
305pub fn url_from_path_with_drive_lowercasing(
306 path: impl AsRef<Path>,
307) -> Result<Url, Box<dyn std::error::Error + Send + Sync>> {
308 let component_has_windows_drive = path.as_ref().components().any(|comp| {
309 if let Component::Prefix(c) = comp {
310 match c.kind() {
311 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
312 _ => return false,
313 }
314 }
315 false
316 });
317
318 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
319 if component_has_windows_drive {
320 let url_original = Url::from_file_path(&path)
321 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
322
323 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
324
325 // There is a drive partition, but we never found a colon.
326 // This should not happen, but in this case we just pass it through.
327 if drive_partition.len() == 1 {
328 return Ok(url_original);
329 }
330
331 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
332 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
333
334 Ok(url)
335 } else {
336 Ok(Url::from_file_path(&path)
337 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
338 }
339}
340
341// `Url` is not able to parse windows paths on unix machines.
342#[cfg(target_os = "windows")]
343#[cfg(test)]
344mod path_conversion_windows_tests {
345 use super::url_from_path_with_drive_lowercasing;
346 #[test]
347 fn test_lowercase_drive_letter_with_drive() {
348 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
349
350 assert_eq!(url.to_string(), "file:///c:/Test");
351 }
352
353 #[test]
354 fn test_drive_without_colon_passthrough() {
355 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
356
357 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
358 }
359}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap
new file mode 100644
index 000000000..cb0920914
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap
@@ -0,0 +1,85 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/mir/tagset.rs",
8 range: Range {
9 start: Position {
10 line: 41,
11 character: 23,
12 },
13 end: Position {
14 line: 41,
15 character: 28,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 41,
23 character: 23,
24 },
25 end: Position {
26 line: 41,
27 character: 28,
28 },
29 },
30 severity: Some(
31 Warning,
32 ),
33 code: Some(
34 String(
35 "trivially_copy_pass_by_ref",
36 ),
37 ),
38 source: Some(
39 "clippy",
40 ),
41 message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
42 related_information: Some(
43 [
44 DiagnosticRelatedInformation {
45 location: Location {
46 uri: "file:///test/compiler/lib.rs",
47 range: Range {
48 start: Position {
49 line: 0,
50 character: 8,
51 },
52 end: Position {
53 line: 0,
54 character: 19,
55 },
56 },
57 },
58 message: "lint level defined here",
59 },
60 ],
61 ),
62 tags: None,
63 },
64 suggested_fixes: [
65 SuggestedFix {
66 title: "consider passing by value instead: \'self\'",
67 location: Location {
68 uri: "file:///test/compiler/mir/tagset.rs",
69 range: Range {
70 start: Position {
71 line: 41,
72 character: 23,
73 },
74 end: Position {
75 line: 41,
76 character: 28,
77 },
78 },
79 },
80 replacement: "self",
81 applicability: Unspecified,
82 diagnostics: [],
83 },
84 ],
85}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap
new file mode 100644
index 000000000..19510ecc1
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap
@@ -0,0 +1,46 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/src/main.rs",
8 range: Range {
9 start: Position {
10 line: 1,
11 character: 4,
12 },
13 end: Position {
14 line: 1,
15 character: 26,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 1,
23 character: 4,
24 },
25 end: Position {
26 line: 1,
27 character: 26,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0277",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap
new file mode 100644
index 000000000..92f7eec05
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap
@@ -0,0 +1,61 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/crates/ra_hir_def/src/data.rs",
8 range: Range {
9 start: Position {
10 line: 79,
11 character: 15,
12 },
13 end: Position {
14 line: 79,
15 character: 41,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 79,
23 character: 15,
24 },
25 end: Position {
26 line: 79,
27 character: 41,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: None,
34 source: Some(
35 "rustc",
36 ),
37 message: "Please register your known path in the path module",
38 related_information: Some(
39 [
40 DiagnosticRelatedInformation {
41 location: Location {
42 uri: "file:///test/crates/ra_hir_def/src/path.rs",
43 range: Range {
44 start: Position {
45 line: 264,
46 character: 8,
47 },
48 end: Position {
49 line: 264,
50 character: 76,
51 },
52 },
53 },
54 message: "Error originated from macro here",
55 },
56 ],
57 ),
58 tags: None,
59 },
60 suggested_fixes: [],
61}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
new file mode 100644
index 000000000..cf683e4b6
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
@@ -0,0 +1,46 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/ty/list_iter.rs",
8 range: Range {
9 start: Position {
10 line: 51,
11 character: 4,
12 },
13 end: Position {
14 line: 51,
15 character: 47,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 51,
23 character: 4,
24 },
25 end: Position {
26 line: 51,
27 character: 47,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0053",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap
new file mode 100644
index 000000000..8c1483c74
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap
@@ -0,0 +1,46 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/runtime/compiler_support.rs",
8 range: Range {
9 start: Position {
10 line: 47,
11 character: 64,
12 },
13 end: Position {
14 line: 47,
15 character: 69,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 47,
23 character: 64,
24 },
25 end: Position {
26 line: 47,
27 character: 69,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0308",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "mismatched types\nexpected usize, found u32",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap
new file mode 100644
index 000000000..eb5a2247b
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap
@@ -0,0 +1,70 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/driver/subcommand/repl.rs",
8 range: Range {
9 start: Position {
10 line: 290,
11 character: 8,
12 },
13 end: Position {
14 line: 290,
15 character: 11,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 290,
23 character: 8,
24 },
25 end: Position {
26 line: 290,
27 character: 11,
28 },
29 },
30 severity: Some(
31 Warning,
32 ),
33 code: Some(
34 String(
35 "unused_variables",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
42 related_information: None,
43 tags: Some(
44 [
45 Unnecessary,
46 ],
47 ),
48 },
49 suggested_fixes: [
50 SuggestedFix {
51 title: "consider prefixing with an underscore: \'_foo\'",
52 location: Location {
53 uri: "file:///test/driver/subcommand/repl.rs",
54 range: Range {
55 start: Position {
56 line: 290,
57 character: 8,
58 },
59 end: Position {
60 line: 290,
61 character: 11,
62 },
63 },
64 },
65 replacement: "_foo",
66 applicability: MachineApplicable,
67 diagnostics: [],
68 },
69 ],
70}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
new file mode 100644
index 000000000..2f4518931
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
@@ -0,0 +1,65 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/ty/select.rs",
8 range: Range {
9 start: Position {
10 line: 103,
11 character: 17,
12 },
13 end: Position {
14 line: 103,
15 character: 29,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 103,
23 character: 17,
24 },
25 end: Position {
26 line: 103,
27 character: 29,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0061",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters",
42 related_information: Some(
43 [
44 DiagnosticRelatedInformation {
45 location: Location {
46 uri: "file:///test/compiler/ty/select.rs",
47 range: Range {
48 start: Position {
49 line: 218,
50 character: 4,
51 },
52 end: Position {
53 line: 230,
54 character: 5,
55 },
56 },
57 },
58 message: "defined here",
59 },
60 ],
61 ),
62 tags: None,
63 },
64 suggested_fixes: [],
65}
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_cargo_watch/src/conv/test.rs
new file mode 100644
index 000000000..381992388
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/test.rs
@@ -0,0 +1,929 @@
1//! This module contains the large and verbose snapshot tests for the
2//! conversions between `cargo check` json and LSP diagnostics.
3use crate::*;
4
5fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic {
6 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap()
7}
8
9#[test]
10fn snap_rustc_incompatible_type_for_trait() {
11 let diag = parse_diagnostic(
12 r##"{
13 "message": "method `next` has an incompatible type for trait",
14 "code": {
15 "code": "E0053",
16 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
17 },
18 "level": "error",
19 "spans": [
20 {
21 "file_name": "compiler/ty/list_iter.rs",
22 "byte_start": 1307,
23 "byte_end": 1350,
24 "line_start": 52,
25 "line_end": 52,
26 "column_start": 5,
27 "column_end": 48,
28 "is_primary": true,
29 "text": [
30 {
31 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
32 "highlight_start": 5,
33 "highlight_end": 48
34 }
35 ],
36 "label": "types differ in mutability",
37 "suggested_replacement": null,
38 "suggestion_applicability": null,
39 "expansion": null
40 }
41 ],
42 "children": [
43 {
44 "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`",
45 "code": null,
46 "level": "note",
47 "spans": [],
48 "children": [],
49 "rendered": null
50 }
51 ],
52 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
53 }
54 "##,
55 );
56
57 let workspace_root = PathBuf::from("/test/");
58 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
59 insta::assert_debug_snapshot!(diag);
60}
61
62#[test]
63fn snap_rustc_unused_variable() {
64 let diag = parse_diagnostic(
65 r##"{
66"message": "unused variable: `foo`",
67"code": {
68 "code": "unused_variables",
69 "explanation": null
70},
71"level": "warning",
72"spans": [
73 {
74 "file_name": "driver/subcommand/repl.rs",
75 "byte_start": 9228,
76 "byte_end": 9231,
77 "line_start": 291,
78 "line_end": 291,
79 "column_start": 9,
80 "column_end": 12,
81 "is_primary": true,
82 "text": [
83 {
84 "text": " let foo = 42;",
85 "highlight_start": 9,
86 "highlight_end": 12
87 }
88 ],
89 "label": null,
90 "suggested_replacement": null,
91 "suggestion_applicability": null,
92 "expansion": null
93 }
94],
95"children": [
96 {
97 "message": "#[warn(unused_variables)] on by default",
98 "code": null,
99 "level": "note",
100 "spans": [],
101 "children": [],
102 "rendered": null
103 },
104 {
105 "message": "consider prefixing with an underscore",
106 "code": null,
107 "level": "help",
108 "spans": [
109 {
110 "file_name": "driver/subcommand/repl.rs",
111 "byte_start": 9228,
112 "byte_end": 9231,
113 "line_start": 291,
114 "line_end": 291,
115 "column_start": 9,
116 "column_end": 12,
117 "is_primary": true,
118 "text": [
119 {
120 "text": " let foo = 42;",
121 "highlight_start": 9,
122 "highlight_end": 12
123 }
124 ],
125 "label": null,
126 "suggested_replacement": "_foo",
127 "suggestion_applicability": "MachineApplicable",
128 "expansion": null
129 }
130 ],
131 "children": [],
132 "rendered": null
133 }
134],
135"rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
136}"##,
137 );
138
139 let workspace_root = PathBuf::from("/test/");
140 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
141 insta::assert_debug_snapshot!(diag);
142}
143
144#[test]
145fn snap_rustc_wrong_number_of_parameters() {
146 let diag = parse_diagnostic(
147 r##"{
148"message": "this function takes 2 parameters but 3 parameters were supplied",
149"code": {
150 "code": "E0061",
151 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
152},
153"level": "error",
154"spans": [
155 {
156 "file_name": "compiler/ty/select.rs",
157 "byte_start": 8787,
158 "byte_end": 9241,
159 "line_start": 219,
160 "line_end": 231,
161 "column_start": 5,
162 "column_end": 6,
163 "is_primary": false,
164 "text": [
165 {
166 "text": " pub fn add_evidence(",
167 "highlight_start": 5,
168 "highlight_end": 25
169 },
170 {
171 "text": " &mut self,",
172 "highlight_start": 1,
173 "highlight_end": 19
174 },
175 {
176 "text": " target_poly: &ty::Ref<ty::Poly>,",
177 "highlight_start": 1,
178 "highlight_end": 41
179 },
180 {
181 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
182 "highlight_start": 1,
183 "highlight_end": 43
184 },
185 {
186 "text": " ) {",
187 "highlight_start": 1,
188 "highlight_end": 8
189 },
190 {
191 "text": " match target_poly {",
192 "highlight_start": 1,
193 "highlight_end": 28
194 },
195 {
196 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
197 "highlight_start": 1,
198 "highlight_end": 81
199 },
200 {
201 "text": " ty::Ref::Fixed(target_ty) => {",
202 "highlight_start": 1,
203 "highlight_end": 43
204 },
205 {
206 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
207 "highlight_start": 1,
208 "highlight_end": 65
209 },
210 {
211 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
212 "highlight_start": 1,
213 "highlight_end": 76
214 },
215 {
216 "text": " }",
217 "highlight_start": 1,
218 "highlight_end": 14
219 },
220 {
221 "text": " }",
222 "highlight_start": 1,
223 "highlight_end": 10
224 },
225 {
226 "text": " }",
227 "highlight_start": 1,
228 "highlight_end": 6
229 }
230 ],
231 "label": "defined here",
232 "suggested_replacement": null,
233 "suggestion_applicability": null,
234 "expansion": null
235 },
236 {
237 "file_name": "compiler/ty/select.rs",
238 "byte_start": 4045,
239 "byte_end": 4057,
240 "line_start": 104,
241 "line_end": 104,
242 "column_start": 18,
243 "column_end": 30,
244 "is_primary": true,
245 "text": [
246 {
247 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
248 "highlight_start": 18,
249 "highlight_end": 30
250 }
251 ],
252 "label": "expected 2 parameters",
253 "suggested_replacement": null,
254 "suggestion_applicability": null,
255 "expansion": null
256 }
257],
258"children": [],
259"rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
260}"##,
261 );
262
263 let workspace_root = PathBuf::from("/test/");
264 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
265 insta::assert_debug_snapshot!(diag);
266}
267
268#[test]
269fn snap_clippy_pass_by_ref() {
270 let diag = parse_diagnostic(
271 r##"{
272"message": "this argument is passed by reference, but would be more efficient if passed by value",
273"code": {
274 "code": "clippy::trivially_copy_pass_by_ref",
275 "explanation": null
276},
277"level": "warning",
278"spans": [
279 {
280 "file_name": "compiler/mir/tagset.rs",
281 "byte_start": 941,
282 "byte_end": 946,
283 "line_start": 42,
284 "line_end": 42,
285 "column_start": 24,
286 "column_end": 29,
287 "is_primary": true,
288 "text": [
289 {
290 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
291 "highlight_start": 24,
292 "highlight_end": 29
293 }
294 ],
295 "label": null,
296 "suggested_replacement": null,
297 "suggestion_applicability": null,
298 "expansion": null
299 }
300],
301"children": [
302 {
303 "message": "lint level defined here",
304 "code": null,
305 "level": "note",
306 "spans": [
307 {
308 "file_name": "compiler/lib.rs",
309 "byte_start": 8,
310 "byte_end": 19,
311 "line_start": 1,
312 "line_end": 1,
313 "column_start": 9,
314 "column_end": 20,
315 "is_primary": true,
316 "text": [
317 {
318 "text": "#![warn(clippy::all)]",
319 "highlight_start": 9,
320 "highlight_end": 20
321 }
322 ],
323 "label": null,
324 "suggested_replacement": null,
325 "suggestion_applicability": null,
326 "expansion": null
327 }
328 ],
329 "children": [],
330 "rendered": null
331 },
332 {
333 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
334 "code": null,
335 "level": "note",
336 "spans": [],
337 "children": [],
338 "rendered": null
339 },
340 {
341 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
342 "code": null,
343 "level": "help",
344 "spans": [],
345 "children": [],
346 "rendered": null
347 },
348 {
349 "message": "consider passing by value instead",
350 "code": null,
351 "level": "help",
352 "spans": [
353 {
354 "file_name": "compiler/mir/tagset.rs",
355 "byte_start": 941,
356 "byte_end": 946,
357 "line_start": 42,
358 "line_end": 42,
359 "column_start": 24,
360 "column_end": 29,
361 "is_primary": true,
362 "text": [
363 {
364 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
365 "highlight_start": 24,
366 "highlight_end": 29
367 }
368 ],
369 "label": null,
370 "suggested_replacement": "self",
371 "suggestion_applicability": "Unspecified",
372 "expansion": null
373 }
374 ],
375 "children": [],
376 "rendered": null
377 }
378],
379"rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
380}"##,
381 );
382
383 let workspace_root = PathBuf::from("/test/");
384 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
385 insta::assert_debug_snapshot!(diag);
386}
387
388#[test]
389fn snap_rustc_mismatched_type() {
390 let diag = parse_diagnostic(
391 r##"{
392"message": "mismatched types",
393"code": {
394 "code": "E0308",
395 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
396},
397"level": "error",
398"spans": [
399 {
400 "file_name": "runtime/compiler_support.rs",
401 "byte_start": 1589,
402 "byte_end": 1594,
403 "line_start": 48,
404 "line_end": 48,
405 "column_start": 65,
406 "column_end": 70,
407 "is_primary": true,
408 "text": [
409 {
410 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
411 "highlight_start": 65,
412 "highlight_end": 70
413 }
414 ],
415 "label": "expected usize, found u32",
416 "suggested_replacement": null,
417 "suggestion_applicability": null,
418 "expansion": null
419 }
420],
421"children": [],
422"rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
423}"##,
424 );
425
426 let workspace_root = PathBuf::from("/test/");
427 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
428 insta::assert_debug_snapshot!(diag);
429}
430
431#[test]
432fn snap_handles_macro_location() {
433 let diag = parse_diagnostic(
434 r##"{
435"rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
436"children": [
437 {
438 "children": [],
439 "code": null,
440 "level": "help",
441 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
442 "rendered": null,
443 "spans": []
444 }
445],
446"code": {
447 "code": "E0277",
448 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
449},
450"level": "error",
451"message": "can't compare `{integer}` with `&str`",
452"spans": [
453 {
454 "byte_end": 155,
455 "byte_start": 153,
456 "column_end": 33,
457 "column_start": 31,
458 "expansion": {
459 "def_site_span": {
460 "byte_end": 940,
461 "byte_start": 0,
462 "column_end": 6,
463 "column_start": 1,
464 "expansion": null,
465 "file_name": "<::core::macros::assert_eq macros>",
466 "is_primary": false,
467 "label": null,
468 "line_end": 36,
469 "line_start": 1,
470 "suggested_replacement": null,
471 "suggestion_applicability": null,
472 "text": [
473 {
474 "highlight_end": 35,
475 "highlight_start": 1,
476 "text": "($ left : expr, $ right : expr) =>"
477 },
478 {
479 "highlight_end": 3,
480 "highlight_start": 1,
481 "text": "({"
482 },
483 {
484 "highlight_end": 33,
485 "highlight_start": 1,
486 "text": " match (& $ left, & $ right)"
487 },
488 {
489 "highlight_end": 7,
490 "highlight_start": 1,
491 "text": " {"
492 },
493 {
494 "highlight_end": 34,
495 "highlight_start": 1,
496 "text": " (left_val, right_val) =>"
497 },
498 {
499 "highlight_end": 11,
500 "highlight_start": 1,
501 "text": " {"
502 },
503 {
504 "highlight_end": 46,
505 "highlight_start": 1,
506 "text": " if ! (* left_val == * right_val)"
507 },
508 {
509 "highlight_end": 15,
510 "highlight_start": 1,
511 "text": " {"
512 },
513 {
514 "highlight_end": 25,
515 "highlight_start": 1,
516 "text": " panic !"
517 },
518 {
519 "highlight_end": 57,
520 "highlight_start": 1,
521 "text": " (r#\"assertion failed: `(left == right)`"
522 },
523 {
524 "highlight_end": 16,
525 "highlight_start": 1,
526 "text": " left: `{:?}`,"
527 },
528 {
529 "highlight_end": 18,
530 "highlight_start": 1,
531 "text": " right: `{:?}`\"#,"
532 },
533 {
534 "highlight_end": 47,
535 "highlight_start": 1,
536 "text": " & * left_val, & * right_val)"
537 },
538 {
539 "highlight_end": 15,
540 "highlight_start": 1,
541 "text": " }"
542 },
543 {
544 "highlight_end": 11,
545 "highlight_start": 1,
546 "text": " }"
547 },
548 {
549 "highlight_end": 7,
550 "highlight_start": 1,
551 "text": " }"
552 },
553 {
554 "highlight_end": 42,
555 "highlight_start": 1,
556 "text": " }) ; ($ left : expr, $ right : expr,) =>"
557 },
558 {
559 "highlight_end": 49,
560 "highlight_start": 1,
561 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
562 },
563 {
564 "highlight_end": 53,
565 "highlight_start": 1,
566 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
567 },
568 {
569 "highlight_end": 3,
570 "highlight_start": 1,
571 "text": "({"
572 },
573 {
574 "highlight_end": 37,
575 "highlight_start": 1,
576 "text": " match (& ($ left), & ($ right))"
577 },
578 {
579 "highlight_end": 7,
580 "highlight_start": 1,
581 "text": " {"
582 },
583 {
584 "highlight_end": 34,
585 "highlight_start": 1,
586 "text": " (left_val, right_val) =>"
587 },
588 {
589 "highlight_end": 11,
590 "highlight_start": 1,
591 "text": " {"
592 },
593 {
594 "highlight_end": 46,
595 "highlight_start": 1,
596 "text": " if ! (* left_val == * right_val)"
597 },
598 {
599 "highlight_end": 15,
600 "highlight_start": 1,
601 "text": " {"
602 },
603 {
604 "highlight_end": 25,
605 "highlight_start": 1,
606 "text": " panic !"
607 },
608 {
609 "highlight_end": 57,
610 "highlight_start": 1,
611 "text": " (r#\"assertion failed: `(left == right)`"
612 },
613 {
614 "highlight_end": 16,
615 "highlight_start": 1,
616 "text": " left: `{:?}`,"
617 },
618 {
619 "highlight_end": 22,
620 "highlight_start": 1,
621 "text": " right: `{:?}`: {}\"#,"
622 },
623 {
624 "highlight_end": 72,
625 "highlight_start": 1,
626 "text": " & * left_val, & * right_val, $ crate :: format_args !"
627 },
628 {
629 "highlight_end": 33,
630 "highlight_start": 1,
631 "text": " ($ ($ arg) +))"
632 },
633 {
634 "highlight_end": 15,
635 "highlight_start": 1,
636 "text": " }"
637 },
638 {
639 "highlight_end": 11,
640 "highlight_start": 1,
641 "text": " }"
642 },
643 {
644 "highlight_end": 7,
645 "highlight_start": 1,
646 "text": " }"
647 },
648 {
649 "highlight_end": 6,
650 "highlight_start": 1,
651 "text": " }) ;"
652 }
653 ]
654 },
655 "macro_decl_name": "assert_eq!",
656 "span": {
657 "byte_end": 38,
658 "byte_start": 16,
659 "column_end": 27,
660 "column_start": 5,
661 "expansion": null,
662 "file_name": "src/main.rs",
663 "is_primary": false,
664 "label": null,
665 "line_end": 2,
666 "line_start": 2,
667 "suggested_replacement": null,
668 "suggestion_applicability": null,
669 "text": [
670 {
671 "highlight_end": 27,
672 "highlight_start": 5,
673 "text": " assert_eq!(1, \"love\");"
674 }
675 ]
676 }
677 },
678 "file_name": "<::core::macros::assert_eq macros>",
679 "is_primary": true,
680 "label": "no implementation for `{integer} == &str`",
681 "line_end": 7,
682 "line_start": 7,
683 "suggested_replacement": null,
684 "suggestion_applicability": null,
685 "text": [
686 {
687 "highlight_end": 33,
688 "highlight_start": 31,
689 "text": " if ! (* left_val == * right_val)"
690 }
691 ]
692 }
693]
694}"##,
695 );
696
697 let workspace_root = PathBuf::from("/test/");
698 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
699 insta::assert_debug_snapshot!(diag);
700}
701
702#[test]
703fn snap_macro_compiler_error() {
704 let diag = parse_diagnostic(
705 r##"{
706 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
707 "children": [],
708 "code": null,
709 "level": "error",
710 "message": "Please register your known path in the path module",
711 "spans": [
712 {
713 "byte_end": 8285,
714 "byte_start": 8217,
715 "column_end": 77,
716 "column_start": 9,
717 "expansion": {
718 "def_site_span": {
719 "byte_end": 8294,
720 "byte_start": 7858,
721 "column_end": 2,
722 "column_start": 1,
723 "expansion": null,
724 "file_name": "crates/ra_hir_def/src/path.rs",
725 "is_primary": false,
726 "label": null,
727 "line_end": 267,
728 "line_start": 254,
729 "suggested_replacement": null,
730 "suggestion_applicability": null,
731 "text": [
732 {
733 "highlight_end": 28,
734 "highlight_start": 1,
735 "text": "macro_rules! __known_path {"
736 },
737 {
738 "highlight_end": 37,
739 "highlight_start": 1,
740 "text": " (std::iter::IntoIterator) => {};"
741 },
742 {
743 "highlight_end": 33,
744 "highlight_start": 1,
745 "text": " (std::result::Result) => {};"
746 },
747 {
748 "highlight_end": 29,
749 "highlight_start": 1,
750 "text": " (std::ops::Range) => {};"
751 },
752 {
753 "highlight_end": 33,
754 "highlight_start": 1,
755 "text": " (std::ops::RangeFrom) => {};"
756 },
757 {
758 "highlight_end": 33,
759 "highlight_start": 1,
760 "text": " (std::ops::RangeFull) => {};"
761 },
762 {
763 "highlight_end": 31,
764 "highlight_start": 1,
765 "text": " (std::ops::RangeTo) => {};"
766 },
767 {
768 "highlight_end": 40,
769 "highlight_start": 1,
770 "text": " (std::ops::RangeToInclusive) => {};"
771 },
772 {
773 "highlight_end": 38,
774 "highlight_start": 1,
775 "text": " (std::ops::RangeInclusive) => {};"
776 },
777 {
778 "highlight_end": 27,
779 "highlight_start": 1,
780 "text": " (std::ops::Try) => {};"
781 },
782 {
783 "highlight_end": 22,
784 "highlight_start": 1,
785 "text": " ($path:path) => {"
786 },
787 {
788 "highlight_end": 77,
789 "highlight_start": 1,
790 "text": " compile_error!(\"Please register your known path in the path module\")"
791 },
792 {
793 "highlight_end": 7,
794 "highlight_start": 1,
795 "text": " };"
796 },
797 {
798 "highlight_end": 2,
799 "highlight_start": 1,
800 "text": "}"
801 }
802 ]
803 },
804 "macro_decl_name": "$crate::__known_path!",
805 "span": {
806 "byte_end": 8427,
807 "byte_start": 8385,
808 "column_end": 51,
809 "column_start": 9,
810 "expansion": {
811 "def_site_span": {
812 "byte_end": 8611,
813 "byte_start": 8312,
814 "column_end": 2,
815 "column_start": 1,
816 "expansion": null,
817 "file_name": "crates/ra_hir_def/src/path.rs",
818 "is_primary": false,
819 "label": null,
820 "line_end": 277,
821 "line_start": 270,
822 "suggested_replacement": null,
823 "suggestion_applicability": null,
824 "text": [
825 {
826 "highlight_end": 22,
827 "highlight_start": 1,
828 "text": "macro_rules! __path {"
829 },
830 {
831 "highlight_end": 43,
832 "highlight_start": 1,
833 "text": " ($start:ident $(:: $seg:ident)*) => ({"
834 },
835 {
836 "highlight_end": 51,
837 "highlight_start": 1,
838 "text": " $crate::__known_path!($start $(:: $seg)*);"
839 },
840 {
841 "highlight_end": 87,
842 "highlight_start": 1,
843 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
844 },
845 {
846 "highlight_end": 76,
847 "highlight_start": 1,
848 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
849 },
850 {
851 "highlight_end": 11,
852 "highlight_start": 1,
853 "text": " ])"
854 },
855 {
856 "highlight_end": 8,
857 "highlight_start": 1,
858 "text": " });"
859 },
860 {
861 "highlight_end": 2,
862 "highlight_start": 1,
863 "text": "}"
864 }
865 ]
866 },
867 "macro_decl_name": "path!",
868 "span": {
869 "byte_end": 2966,
870 "byte_start": 2940,
871 "column_end": 42,
872 "column_start": 16,
873 "expansion": null,
874 "file_name": "crates/ra_hir_def/src/data.rs",
875 "is_primary": false,
876 "label": null,
877 "line_end": 80,
878 "line_start": 80,
879 "suggested_replacement": null,
880 "suggestion_applicability": null,
881 "text": [
882 {
883 "highlight_end": 42,
884 "highlight_start": 16,
885 "text": " let path = path![std::future::Future];"
886 }
887 ]
888 }
889 },
890 "file_name": "crates/ra_hir_def/src/path.rs",
891 "is_primary": false,
892 "label": null,
893 "line_end": 272,
894 "line_start": 272,
895 "suggested_replacement": null,
896 "suggestion_applicability": null,
897 "text": [
898 {
899 "highlight_end": 51,
900 "highlight_start": 9,
901 "text": " $crate::__known_path!($start $(:: $seg)*);"
902 }
903 ]
904 }
905 },
906 "file_name": "crates/ra_hir_def/src/path.rs",
907 "is_primary": true,
908 "label": null,
909 "line_end": 265,
910 "line_start": 265,
911 "suggested_replacement": null,
912 "suggestion_applicability": null,
913 "text": [
914 {
915 "highlight_end": 77,
916 "highlight_start": 9,
917 "text": " compile_error!(\"Please register your known path in the path module\")"
918 }
919 ]
920 }
921 ]
922}
923 "##,
924 );
925
926 let workspace_root = PathBuf::from("/test/");
927 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
928 insta::assert_debug_snapshot!(diag);
929}
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
new file mode 100644
index 000000000..9bc0fd405
--- /dev/null
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -0,0 +1,394 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4use cargo_metadata::Message;
5use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
6use lsp_types::{
7 Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd,
8 WorkDoneProgressReport,
9};
10use parking_lot::RwLock;
11use std::{
12 collections::HashMap,
13 path::PathBuf,
14 process::{Command, Stdio},
15 sync::Arc,
16 thread::JoinHandle,
17 time::Instant,
18};
19
20mod conv;
21
22use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix};
23
24pub use crate::conv::url_from_path_with_drive_lowercasing;
25
26#[derive(Clone, Debug)]
27pub struct CheckOptions {
28 pub enable: bool,
29 pub args: Vec<String>,
30 pub command: String,
31 pub all_targets: bool,
32}
33
34/// CheckWatcher wraps the shared state and communication machinery used for
35/// running `cargo check` (or other compatible command) and providing
36/// diagnostics based on the output.
37/// The spawned thread is shut down when this struct is dropped.
38#[derive(Debug)]
39pub struct CheckWatcher {
40 pub task_recv: Receiver<CheckTask>,
41 pub shared: Arc<RwLock<CheckWatcherSharedState>>,
42 cmd_send: Option<Sender<CheckCommand>>,
43 handle: Option<JoinHandle<()>>,
44}
45
46impl CheckWatcher {
47 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
48 let options = options.clone();
49 let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new()));
50
51 let (task_send, task_recv) = unbounded::<CheckTask>();
52 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
53 let shared_ = shared.clone();
54 let handle = std::thread::spawn(move || {
55 let mut check = CheckWatcherState::new(options, workspace_root, shared_);
56 check.run(&task_send, &cmd_recv);
57 });
58 CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared }
59 }
60
61 /// Schedule a re-start of the cargo check worker.
62 pub fn update(&self) {
63 if let Some(cmd_send) = &self.cmd_send {
64 cmd_send.send(CheckCommand::Update).unwrap();
65 }
66 }
67}
68
69impl std::ops::Drop for CheckWatcher {
70 fn drop(&mut self) {
71 if let Some(handle) = self.handle.take() {
72 // Take the sender out of the option
73 let recv = self.cmd_send.take();
74
75 // Dropping the sender finishes the thread loop
76 drop(recv);
77
78 // Join the thread, it should finish shortly. We don't really care
79 // whether it panicked, so it is safe to ignore the result
80 let _ = handle.join();
81 }
82 }
83}
84
85#[derive(Debug)]
86pub struct CheckWatcherSharedState {
87 diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
88 suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
89}
90
91impl CheckWatcherSharedState {
92 fn new() -> CheckWatcherSharedState {
93 CheckWatcherSharedState {
94 diagnostic_collection: HashMap::new(),
95 suggested_fix_collection: HashMap::new(),
96 }
97 }
98
99 /// Clear the cached diagnostics, and schedule updating diagnostics by the
100 /// server, to clear stale results.
101 pub fn clear(&mut self, task_send: &Sender<CheckTask>) {
102 let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect();
103
104 self.diagnostic_collection.clear();
105 self.suggested_fix_collection.clear();
106
107 for uri in cleared_files {
108 task_send.send(CheckTask::Update(uri.clone())).unwrap();
109 }
110 }
111
112 pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> {
113 self.diagnostic_collection.get(uri).map(|d| d.as_slice())
114 }
115
116 pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> {
117 self.suggested_fix_collection.get(uri).map(|d| d.as_slice())
118 }
119
120 fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) {
121 let diagnostics = self.diagnostic_collection.entry(file_uri).or_default();
122
123 // If we're building multiple targets it's possible we've already seen this diagnostic
124 let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic));
125 if is_duplicate {
126 return;
127 }
128
129 diagnostics.push(diagnostic);
130 }
131
132 fn add_suggested_fix_for_diagnostic(
133 &mut self,
134 mut suggested_fix: SuggestedFix,
135 diagnostic: &Diagnostic,
136 ) {
137 let file_uri = suggested_fix.location.uri.clone();
138 let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default();
139
140 let existing_suggestion: Option<&mut SuggestedFix> =
141 file_suggestions.iter_mut().find(|s| s == &&suggested_fix);
142 if let Some(existing_suggestion) = existing_suggestion {
143 // The existing suggestion also applies to this new diagnostic
144 existing_suggestion.diagnostics.push(diagnostic.clone());
145 } else {
146 // We haven't seen this suggestion before
147 suggested_fix.diagnostics.push(diagnostic.clone());
148 file_suggestions.push(suggested_fix);
149 }
150 }
151}
152
153#[derive(Debug)]
154pub enum CheckTask {
155 /// Request a update of the given files diagnostics
156 Update(Url),
157
158 /// Request check progress notification to client
159 Status(WorkDoneProgress),
160}
161
162pub enum CheckCommand {
163 /// Request re-start of check thread
164 Update,
165}
166
167struct CheckWatcherState {
168 options: CheckOptions,
169 workspace_root: PathBuf,
170 watcher: WatchThread,
171 last_update_req: Option<Instant>,
172 shared: Arc<RwLock<CheckWatcherSharedState>>,
173}
174
175impl CheckWatcherState {
176 fn new(
177 options: CheckOptions,
178 workspace_root: PathBuf,
179 shared: Arc<RwLock<CheckWatcherSharedState>>,
180 ) -> CheckWatcherState {
181 let watcher = WatchThread::new(&options, &workspace_root);
182 CheckWatcherState { options, workspace_root, watcher, last_update_req: None, shared }
183 }
184
185 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
186 loop {
187 select! {
188 recv(&cmd_recv) -> cmd => match cmd {
189 Ok(cmd) => self.handle_command(cmd),
190 Err(RecvError) => {
191 // Command channel has closed, so shut down
192 break;
193 },
194 },
195 recv(self.watcher.message_recv) -> msg => match msg {
196 Ok(msg) => self.handle_message(msg, task_send),
197 Err(RecvError) => {
198 // Watcher finished, replace it with a never channel to
199 // avoid busy-waiting.
200 std::mem::replace(&mut self.watcher.message_recv, never());
201 },
202 }
203 };
204
205 if self.should_recheck() {
206 self.last_update_req.take();
207 self.shared.write().clear(task_send);
208
209 // By replacing the watcher, we drop the previous one which
210 // causes it to shut down automatically.
211 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
212 }
213 }
214 }
215
216 fn should_recheck(&mut self) -> bool {
217 if let Some(_last_update_req) = &self.last_update_req {
218 // We currently only request an update on save, as we need up to
219 // date source on disk for cargo check to do it's magic, so we
220 // don't really need to debounce the requests at this point.
221 return true;
222 }
223 false
224 }
225
226 fn handle_command(&mut self, cmd: CheckCommand) {
227 match cmd {
228 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
229 }
230 }
231
232 fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
233 match msg {
234 CheckEvent::Begin => {
235 task_send
236 .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
237 title: "Running 'cargo check'".to_string(),
238 cancellable: Some(false),
239 message: None,
240 percentage: None,
241 })))
242 .unwrap();
243 }
244
245 CheckEvent::End => {
246 task_send
247 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
248 message: None,
249 })))
250 .unwrap();
251 }
252
253 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
254 task_send
255 .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
256 cancellable: Some(false),
257 message: Some(msg.target.name),
258 percentage: None,
259 })))
260 .unwrap();
261 }
262
263 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
264 let map_result =
265 match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) {
266 Some(map_result) => map_result,
267 None => return,
268 };
269
270 let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result;
271 let file_uri = location.uri.clone();
272
273 if !suggested_fixes.is_empty() {
274 for suggested_fix in suggested_fixes {
275 self.shared
276 .write()
277 .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic);
278 }
279 }
280 self.shared.write().add_diagnostic(file_uri, diagnostic);
281
282 task_send.send(CheckTask::Update(location.uri)).unwrap();
283 }
284
285 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
286 CheckEvent::Msg(Message::Unknown) => {}
287 }
288 }
289}
290
291/// WatchThread exists to wrap around the communication needed to be able to
292/// run `cargo check` without blocking. Currently the Rust standard library
293/// doesn't provide a way to read sub-process output without blocking, so we
294/// have to wrap sub-processes output handling in a thread and pass messages
295/// back over a channel.
296/// The correct way to dispose of the thread is to drop it, on which the
297/// sub-process will be killed, and the thread will be joined.
298struct WatchThread {
299 handle: Option<JoinHandle<()>>,
300 message_recv: Receiver<CheckEvent>,
301}
302
303enum CheckEvent {
304 Begin,
305 Msg(cargo_metadata::Message),
306 End,
307}
308
309impl WatchThread {
310 fn new(options: &CheckOptions, workspace_root: &PathBuf) -> WatchThread {
311 let mut args: Vec<String> = vec![
312 options.command.clone(),
313 "--message-format=json".to_string(),
314 "--manifest-path".to_string(),
315 format!("{}/Cargo.toml", workspace_root.to_string_lossy()),
316 ];
317 if options.all_targets {
318 args.push("--all-targets".to_string());
319 }
320 args.extend(options.args.iter().cloned());
321
322 let (message_send, message_recv) = unbounded();
323 let enabled = options.enable;
324 let handle = std::thread::spawn(move || {
325 if !enabled {
326 return;
327 }
328
329 let mut command = Command::new("cargo")
330 .args(&args)
331 .stdout(Stdio::piped())
332 .stderr(Stdio::null())
333 .spawn()
334 .expect("couldn't launch cargo");
335
336 // If we trigger an error here, we will do so in the loop instead,
337 // which will break out of the loop, and continue the shutdown
338 let _ = message_send.send(CheckEvent::Begin);
339
340 for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) {
341 let message = match message {
342 Ok(message) => message,
343 Err(err) => {
344 log::error!("Invalid json from cargo check, ignoring: {}", err);
345 continue;
346 }
347 };
348
349 match message_send.send(CheckEvent::Msg(message)) {
350 Ok(()) => {}
351 Err(_err) => {
352 // The send channel was closed, so we want to shutdown
353 break;
354 }
355 }
356 }
357
358 // We can ignore any error here, as we are already in the progress
359 // of shutting down.
360 let _ = message_send.send(CheckEvent::End);
361
362 // It is okay to ignore the result, as it only errors if the process is already dead
363 let _ = command.kill();
364
365 // Again, we don't care about the exit status so just ignore the result
366 let _ = command.wait();
367 });
368 WatchThread { handle: Some(handle), message_recv }
369 }
370}
371
372impl std::ops::Drop for WatchThread {
373 fn drop(&mut self) {
374 if let Some(handle) = self.handle.take() {
375 // Replace our reciever with dummy one, so we can drop and close the
376 // one actually communicating with the thread
377 let recv = std::mem::replace(&mut self.message_recv, never());
378
379 // Dropping the original reciever initiates thread sub-process shutdown
380 drop(recv);
381
382 // Join the thread, it should finish shortly. We don't really care
383 // whether it panicked, so it is safe to ignore the result
384 let _ = handle.join();
385 }
386 }
387}
388
389fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
390 left.source == right.source
391 && left.severity == right.severity
392 && left.range == right.range
393 && left.message == right.message
394}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 76d8f85f1..488f74cfb 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -118,7 +118,7 @@ impl_froms!(
118 BuiltinType 118 BuiltinType
119); 119);
120 120
121pub use hir_def::attr::Attrs; 121pub use hir_def::{attr::Attrs, visibility::Visibility};
122 122
123impl Module { 123impl Module {
124 pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { 124 pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module {
@@ -255,6 +255,15 @@ impl StructField {
255 } 255 }
256} 256}
257 257
258impl HasVisibility for StructField {
259 fn visibility(&self, db: &impl HirDatabase) -> Visibility {
260 let variant_data = self.parent.variant_data(db);
261 let visibility = &variant_data.fields()[self.id].visibility;
262 let parent_id: hir_def::VariantId = self.parent.into();
263 visibility.resolve(db, &parent_id.resolver(db))
264 }
265}
266
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 267#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub struct Struct { 268pub struct Struct {
260 pub(crate) id: StructId, 269 pub(crate) id: StructId,
@@ -1041,3 +1050,11 @@ impl<T: Into<AttrDef> + Copy> Docs for T {
1041 db.documentation(def.into()) 1050 db.documentation(def.into())
1042 } 1051 }
1043} 1052}
1053
1054pub trait HasVisibility {
1055 fn visibility(&self, db: &impl HirDatabase) -> Visibility;
1056 fn is_visible_from(&self, db: &impl HirDatabase, module: Module) -> bool {
1057 let vis = self.visibility(db);
1058 vis.is_visible_from(db, module.id)
1059 }
1060}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 0008a8858..3d13978d4 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -40,8 +40,8 @@ mod from_source;
40pub use crate::{ 40pub use crate::{
41 code_model::{ 41 code_model::{
42 Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, 42 Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum,
43 EnumVariant, FieldSource, Function, GenericDef, HasAttrs, ImplBlock, Local, MacroDef, 43 EnumVariant, FieldSource, Function, GenericDef, HasAttrs, HasVisibility, ImplBlock, Local,
44 Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, 44 MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias,
45 TypeParam, Union, VariantDef, 45 TypeParam, Union, VariantDef,
46 }, 46 },
47 from_source::FromSource, 47 from_source::FromSource,
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 85b378483..2c422af8b 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -215,8 +215,32 @@ impl SourceAnalyzer {
215 self.body_source_map.as_ref()?.node_pat(src) 215 self.body_source_map.as_ref()?.node_pat(src)
216 } 216 }
217 217
218 fn expand_expr(
219 &self,
220 db: &impl HirDatabase,
221 expr: InFile<&ast::Expr>,
222 ) -> Option<InFile<ast::Expr>> {
223 let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?;
224 let macro_file =
225 self.body_source_map.as_ref()?.node_macro_file(expr.with_value(&macro_call))?;
226 let expanded = db.parse_or_expand(macro_file)?;
227 let kind = expanded.kind();
228 let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?);
229
230 if ast::MacroCall::can_cast(kind) {
231 self.expand_expr(db, expr.as_ref())
232 } else {
233 Some(expr)
234 }
235 }
236
218 pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { 237 pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
219 let expr_id = self.expr_id(expr)?; 238 let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) {
239 self.body_source_map.as_ref()?.node_expr(expr.as_ref())?
240 } else {
241 self.expr_id(expr)?
242 };
243
220 let ty = self.infer.as_ref()?[expr_id].clone(); 244 let ty = self.infer.as_ref()?[expr_id].clone();
221 let environment = TraitEnvironment::lower(db, &self.resolver); 245 let environment = TraitEnvironment::lower(db, &self.resolver);
222 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) 246 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index d9ea693e3..aac5f3e15 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -9,11 +9,12 @@ use hir_expand::{
9}; 9};
10use ra_arena::{map::ArenaMap, Arena}; 10use ra_arena::{map::ArenaMap, Arena};
11use ra_prof::profile; 11use ra_prof::profile;
12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; 12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
13 13
14use crate::{ 14use crate::{
15 db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef, EnumId, 15 db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef,
16 LocalEnumVariantId, LocalStructFieldId, Lookup, StructId, UnionId, VariantId, 16 visibility::RawVisibility, EnumId, LocalEnumVariantId, LocalStructFieldId, Lookup, StructId,
17 UnionId, VariantId,
17}; 18};
18 19
19/// Note that we use `StructData` for unions as well! 20/// Note that we use `StructData` for unions as well!
@@ -47,13 +48,14 @@ pub enum VariantData {
47pub struct StructFieldData { 48pub struct StructFieldData {
48 pub name: Name, 49 pub name: Name,
49 pub type_ref: TypeRef, 50 pub type_ref: TypeRef,
51 pub visibility: RawVisibility,
50} 52}
51 53
52impl StructData { 54impl StructData {
53 pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> { 55 pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> {
54 let src = id.lookup(db).source(db); 56 let src = id.lookup(db).source(db);
55 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 57 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
56 let variant_data = VariantData::new(src.value.kind()); 58 let variant_data = VariantData::new(db, src.map(|s| s.kind()));
57 let variant_data = Arc::new(variant_data); 59 let variant_data = Arc::new(variant_data);
58 Arc::new(StructData { name, variant_data }) 60 Arc::new(StructData { name, variant_data })
59 } 61 }
@@ -61,10 +63,12 @@ impl StructData {
61 let src = id.lookup(db).source(db); 63 let src = id.lookup(db).source(db);
62 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 64 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
63 let variant_data = VariantData::new( 65 let variant_data = VariantData::new(
64 src.value 66 db,
65 .record_field_def_list() 67 src.map(|s| {
66 .map(ast::StructKind::Record) 68 s.record_field_def_list()
67 .unwrap_or(ast::StructKind::Unit), 69 .map(ast::StructKind::Record)
70 .unwrap_or(ast::StructKind::Unit)
71 }),
68 ); 72 );
69 let variant_data = Arc::new(variant_data); 73 let variant_data = Arc::new(variant_data);
70 Arc::new(StructData { name, variant_data }) 74 Arc::new(StructData { name, variant_data })
@@ -77,7 +81,7 @@ impl EnumData {
77 let src = e.lookup(db).source(db); 81 let src = e.lookup(db).source(db);
78 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 82 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
79 let mut trace = Trace::new_for_arena(); 83 let mut trace = Trace::new_for_arena();
80 lower_enum(&mut trace, &src.value); 84 lower_enum(db, &mut trace, &src);
81 Arc::new(EnumData { name, variants: trace.into_arena() }) 85 Arc::new(EnumData { name, variants: trace.into_arena() })
82 } 86 }
83 87
@@ -93,30 +97,31 @@ impl HasChildSource for EnumId {
93 fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { 97 fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
94 let src = self.lookup(db).source(db); 98 let src = self.lookup(db).source(db);
95 let mut trace = Trace::new_for_map(); 99 let mut trace = Trace::new_for_map();
96 lower_enum(&mut trace, &src.value); 100 lower_enum(db, &mut trace, &src);
97 src.with_value(trace.into_map()) 101 src.with_value(trace.into_map())
98 } 102 }
99} 103}
100 104
101fn lower_enum( 105fn lower_enum(
106 db: &impl DefDatabase,
102 trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>, 107 trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>,
103 ast: &ast::EnumDef, 108 ast: &InFile<ast::EnumDef>,
104) { 109) {
105 for var in ast.variant_list().into_iter().flat_map(|it| it.variants()) { 110 for var in ast.value.variant_list().into_iter().flat_map(|it| it.variants()) {
106 trace.alloc( 111 trace.alloc(
107 || var.clone(), 112 || var.clone(),
108 || EnumVariantData { 113 || EnumVariantData {
109 name: var.name().map_or_else(Name::missing, |it| it.as_name()), 114 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
110 variant_data: Arc::new(VariantData::new(var.kind())), 115 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()))),
111 }, 116 },
112 ); 117 );
113 } 118 }
114} 119}
115 120
116impl VariantData { 121impl VariantData {
117 fn new(flavor: ast::StructKind) -> Self { 122 fn new(db: &impl DefDatabase, flavor: InFile<ast::StructKind>) -> Self {
118 let mut trace = Trace::new_for_arena(); 123 let mut trace = Trace::new_for_arena();
119 match lower_struct(&mut trace, &flavor) { 124 match lower_struct(db, &mut trace, &flavor) {
120 StructKind::Tuple => VariantData::Tuple(trace.into_arena()), 125 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
121 StructKind::Record => VariantData::Record(trace.into_arena()), 126 StructKind::Record => VariantData::Record(trace.into_arena()),
122 StructKind::Unit => VariantData::Unit, 127 StructKind::Unit => VariantData::Unit,
@@ -163,7 +168,7 @@ impl HasChildSource for VariantId {
163 }), 168 }),
164 }; 169 };
165 let mut trace = Trace::new_for_map(); 170 let mut trace = Trace::new_for_map();
166 lower_struct(&mut trace, &src.value); 171 lower_struct(db, &mut trace, &src);
167 src.with_value(trace.into_map()) 172 src.with_value(trace.into_map())
168 } 173 }
169} 174}
@@ -175,14 +180,15 @@ enum StructKind {
175} 180}
176 181
177fn lower_struct( 182fn lower_struct(
183 db: &impl DefDatabase,
178 trace: &mut Trace< 184 trace: &mut Trace<
179 LocalStructFieldId, 185 LocalStructFieldId,
180 StructFieldData, 186 StructFieldData,
181 Either<ast::TupleFieldDef, ast::RecordFieldDef>, 187 Either<ast::TupleFieldDef, ast::RecordFieldDef>,
182 >, 188 >,
183 ast: &ast::StructKind, 189 ast: &InFile<ast::StructKind>,
184) -> StructKind { 190) -> StructKind {
185 match ast { 191 match &ast.value {
186 ast::StructKind::Tuple(fl) => { 192 ast::StructKind::Tuple(fl) => {
187 for (i, fd) in fl.fields().enumerate() { 193 for (i, fd) in fl.fields().enumerate() {
188 trace.alloc( 194 trace.alloc(
@@ -190,6 +196,7 @@ fn lower_struct(
190 || StructFieldData { 196 || StructFieldData {
191 name: Name::new_tuple_field(i), 197 name: Name::new_tuple_field(i),
192 type_ref: TypeRef::from_ast_opt(fd.type_ref()), 198 type_ref: TypeRef::from_ast_opt(fd.type_ref()),
199 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
193 }, 200 },
194 ); 201 );
195 } 202 }
@@ -202,6 +209,7 @@ fn lower_struct(
202 || StructFieldData { 209 || StructFieldData {
203 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), 210 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
204 type_ref: TypeRef::from_ast_opt(fd.ascribed_type()), 211 type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
212 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
205 }, 213 },
206 ); 214 );
207 } 215 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index d3e4c50ae..142c52d35 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -163,6 +163,7 @@ pub struct BodySourceMap {
163 pat_map: FxHashMap<PatSource, PatId>, 163 pat_map: FxHashMap<PatSource, PatId>,
164 pat_map_back: ArenaMap<PatId, PatSource>, 164 pat_map_back: ArenaMap<PatId, PatSource>,
165 field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>, 165 field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
166 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
166} 167}
167 168
168impl Body { 169impl Body {
@@ -237,6 +238,11 @@ impl BodySourceMap {
237 self.expr_map.get(&src).cloned() 238 self.expr_map.get(&src).cloned()
238 } 239 }
239 240
241 pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
242 let src = node.map(|it| AstPtr::new(it));
243 self.expansions.get(&src).cloned()
244 }
245
240 pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> { 246 pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
241 let src = node.map(|it| Either::Right(AstPtr::new(it))); 247 let src = node.map(|it| Either::Right(AstPtr::new(it)));
242 self.expr_map.get(&src).cloned() 248 self.expr_map.get(&src).cloned()
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 5323af097..e656f9a41 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -446,14 +446,20 @@ where
446 } 446 }
447 } 447 }
448 // FIXME expand to statements in statement position 448 // FIXME expand to statements in statement position
449 ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { 449 ast::Expr::MacroCall(e) => {
450 Some((mark, expansion)) => { 450 let macro_call = self.expander.to_source(AstPtr::new(&e));
451 let id = self.collect_expr(expansion); 451 match self.expander.enter_expand(self.db, e.clone()) {
452 self.expander.exit(self.db, mark); 452 Some((mark, expansion)) => {
453 id 453 self.source_map
454 .expansions
455 .insert(macro_call, self.expander.current_file_id);
456 let id = self.collect_expr(expansion);
457 self.expander.exit(self.db, mark);
458 id
459 }
460 None => self.alloc_expr(Expr::Missing, syntax_ptr),
454 } 461 }
455 None => self.alloc_expr(Expr::Missing, syntax_ptr), 462 }
456 },
457 463
458 // FIXME implement HIR for these: 464 // FIXME implement HIR for these:
459 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), 465 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
@@ -543,7 +549,10 @@ where
543 }; 549 };
544 self.body.item_scope.define_def(def); 550 self.body.item_scope.define_def(def);
545 if let Some(name) = name { 551 if let Some(name) = name {
546 self.body.item_scope.push_res(name.as_name(), def.into()); 552 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
553 self.body
554 .item_scope
555 .push_res(name.as_name(), crate::per_ns::PerNs::from_def(def, vis));
547 } 556 }
548 } 557 }
549 } 558 }
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index b0288ee8d..fe7bb9779 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -5,7 +5,10 @@ use hir_expand::name::Name;
5use once_cell::sync::Lazy; 5use once_cell::sync::Lazy;
6use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
7 7
8use crate::{per_ns::PerNs, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, TraitId}; 8use crate::{
9 per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId,
10 TraitId,
11};
9 12
10#[derive(Debug, Default, PartialEq, Eq)] 13#[derive(Debug, Default, PartialEq, Eq)]
11pub struct ItemScope { 14pub struct ItemScope {
@@ -30,7 +33,7 @@ pub struct ItemScope {
30static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { 33static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
31 BuiltinType::ALL 34 BuiltinType::ALL
32 .iter() 35 .iter()
33 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into()))) 36 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public)))
34 .collect() 37 .collect()
35}); 38});
36 39
@@ -144,8 +147,8 @@ impl ItemScope {
144 changed 147 changed
145 } 148 }
146 149
147 pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> { 150 pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a {
148 self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect() 151 self.visible.iter().map(|(name, res)| (name.clone(), res.clone()))
149 } 152 }
150 153
151 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { 154 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
@@ -153,20 +156,20 @@ impl ItemScope {
153 } 156 }
154} 157}
155 158
156impl From<ModuleDefId> for PerNs { 159impl PerNs {
157 fn from(def: ModuleDefId) -> PerNs { 160 pub(crate) fn from_def(def: ModuleDefId, v: Visibility) -> PerNs {
158 match def { 161 match def {
159 ModuleDefId::ModuleId(_) => PerNs::types(def), 162 ModuleDefId::ModuleId(_) => PerNs::types(def, v),
160 ModuleDefId::FunctionId(_) => PerNs::values(def), 163 ModuleDefId::FunctionId(_) => PerNs::values(def, v),
161 ModuleDefId::AdtId(adt) => match adt { 164 ModuleDefId::AdtId(adt) => match adt {
162 AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def), 165 AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def, v),
163 AdtId::EnumId(_) => PerNs::types(def), 166 AdtId::EnumId(_) => PerNs::types(def, v),
164 }, 167 },
165 ModuleDefId::EnumVariantId(_) => PerNs::both(def, def), 168 ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
166 ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def), 169 ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
167 ModuleDefId::TraitId(_) => PerNs::types(def), 170 ModuleDefId::TraitId(_) => PerNs::types(def, v),
168 ModuleDefId::TypeAliasId(_) => PerNs::types(def), 171 ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
169 ModuleDefId::BuiltinType(_) => PerNs::types(def), 172 ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
170 } 173 }
171 } 174 }
172} 175}
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index cef061837..37c861a87 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -22,6 +22,50 @@ pub enum LangItemTarget {
22 TraitId(TraitId), 22 TraitId(TraitId),
23} 23}
24 24
25impl LangItemTarget {
26 pub fn as_enum(self) -> Option<EnumId> {
27 match self {
28 LangItemTarget::EnumId(id) => Some(id),
29 _ => None,
30 }
31 }
32
33 pub fn as_function(self) -> Option<FunctionId> {
34 match self {
35 LangItemTarget::FunctionId(id) => Some(id),
36 _ => None,
37 }
38 }
39
40 pub fn as_impl_block(self) -> Option<ImplId> {
41 match self {
42 LangItemTarget::ImplBlockId(id) => Some(id),
43 _ => None,
44 }
45 }
46
47 pub fn as_static(self) -> Option<StaticId> {
48 match self {
49 LangItemTarget::StaticId(id) => Some(id),
50 _ => None,
51 }
52 }
53
54 pub fn as_struct(self) -> Option<StructId> {
55 match self {
56 LangItemTarget::StructId(id) => Some(id),
57 _ => None,
58 }
59 }
60
61 pub fn as_trait(self) -> Option<TraitId> {
62 match self {
63 LangItemTarget::TraitId(id) => Some(id),
64 _ => None,
65 }
66 }
67}
68
25#[derive(Default, Debug, Clone, PartialEq, Eq)] 69#[derive(Default, Debug, Clone, PartialEq, Eq)]
26pub struct LangItems { 70pub struct LangItems {
27 items: FxHashMap<SmolStr, LangItemTarget>, 71 items: FxHashMap<SmolStr, LangItemTarget>,
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index f6c7f38d1..61f044ecf 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -36,6 +36,8 @@ pub mod nameres;
36pub mod src; 36pub mod src;
37pub mod child_by_source; 37pub mod child_by_source;
38 38
39pub mod visibility;
40
39#[cfg(test)] 41#[cfg(test)]
40mod test_db; 42mod test_db;
41#[cfg(test)] 43#[cfg(test)]
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index b9f40d3dd..8a22b0585 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -24,6 +24,7 @@ use crate::{
24 }, 24 },
25 path::{ModPath, PathKind}, 25 path::{ModPath, PathKind},
26 per_ns::PerNs, 26 per_ns::PerNs,
27 visibility::Visibility,
27 AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, 28 AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
28 LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, 29 LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
29}; 30};
@@ -108,7 +109,7 @@ struct MacroDirective {
108struct DefCollector<'a, DB> { 109struct DefCollector<'a, DB> {
109 db: &'a DB, 110 db: &'a DB,
110 def_map: CrateDefMap, 111 def_map: CrateDefMap,
111 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, raw::Import)>>, 112 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
112 unresolved_imports: Vec<ImportDirective>, 113 unresolved_imports: Vec<ImportDirective>,
113 resolved_imports: Vec<ImportDirective>, 114 resolved_imports: Vec<ImportDirective>,
114 unexpanded_macros: Vec<MacroDirective>, 115 unexpanded_macros: Vec<MacroDirective>,
@@ -214,7 +215,11 @@ where
214 // In Rust, `#[macro_export]` macros are unconditionally visible at the 215 // In Rust, `#[macro_export]` macros are unconditionally visible at the
215 // crate root, even if the parent modules is **not** visible. 216 // crate root, even if the parent modules is **not** visible.
216 if export { 217 if export {
217 self.update(self.def_map.root, &[(name, PerNs::macros(macro_))]); 218 self.update(
219 self.def_map.root,
220 &[(name, PerNs::macros(macro_, Visibility::Public))],
221 Visibility::Public,
222 );
218 } 223 }
219 } 224 }
220 225
@@ -348,9 +353,12 @@ where
348 353
349 fn record_resolved_import(&mut self, directive: &ImportDirective) { 354 fn record_resolved_import(&mut self, directive: &ImportDirective) {
350 let module_id = directive.module_id; 355 let module_id = directive.module_id;
351 let import_id = directive.import_id;
352 let import = &directive.import; 356 let import = &directive.import;
353 let def = directive.status.namespaces(); 357 let def = directive.status.namespaces();
358 let vis = self
359 .def_map
360 .resolve_visibility(self.db, module_id, &directive.import.visibility)
361 .unwrap_or(Visibility::Public);
354 362
355 if import.is_glob { 363 if import.is_glob {
356 log::debug!("glob import: {:?}", import); 364 log::debug!("glob import: {:?}", import);
@@ -366,9 +374,16 @@ where
366 let scope = &item_map[m.local_id].scope; 374 let scope = &item_map[m.local_id].scope;
367 375
368 // Module scoped macros is included 376 // Module scoped macros is included
369 let items = scope.collect_resolutions(); 377 let items = scope
370 378 .resolutions()
371 self.update(module_id, &items); 379 // only keep visible names...
380 .map(|(n, res)| {
381 (n, res.filter_visibility(|v| v.is_visible_from_other_crate()))
382 })
383 .filter(|(_, res)| !res.is_none())
384 .collect::<Vec<_>>();
385
386 self.update(module_id, &items, vis);
372 } else { 387 } else {
373 // glob import from same crate => we do an initial 388 // glob import from same crate => we do an initial
374 // import, and then need to propagate any further 389 // import, and then need to propagate any further
@@ -376,13 +391,25 @@ where
376 let scope = &self.def_map[m.local_id].scope; 391 let scope = &self.def_map[m.local_id].scope;
377 392
378 // Module scoped macros is included 393 // Module scoped macros is included
379 let items = scope.collect_resolutions(); 394 let items = scope
380 395 .resolutions()
381 self.update(module_id, &items); 396 // only keep visible names...
397 .map(|(n, res)| {
398 (
399 n,
400 res.filter_visibility(|v| {
401 v.is_visible_from_def_map(&self.def_map, module_id)
402 }),
403 )
404 })
405 .filter(|(_, res)| !res.is_none())
406 .collect::<Vec<_>>();
407
408 self.update(module_id, &items, vis);
382 // record the glob import in case we add further items 409 // record the glob import in case we add further items
383 let glob = self.glob_imports.entry(m.local_id).or_default(); 410 let glob = self.glob_imports.entry(m.local_id).or_default();
384 if !glob.iter().any(|it| *it == (module_id, import_id)) { 411 if !glob.iter().any(|(mid, _)| *mid == module_id) {
385 glob.push((module_id, import_id)); 412 glob.push((module_id, vis));
386 } 413 }
387 } 414 }
388 } 415 }
@@ -396,11 +423,11 @@ where
396 .map(|(local_id, variant_data)| { 423 .map(|(local_id, variant_data)| {
397 let name = variant_data.name.clone(); 424 let name = variant_data.name.clone();
398 let variant = EnumVariantId { parent: e, local_id }; 425 let variant = EnumVariantId { parent: e, local_id };
399 let res = PerNs::both(variant.into(), variant.into()); 426 let res = PerNs::both(variant.into(), variant.into(), vis);
400 (name, res) 427 (name, res)
401 }) 428 })
402 .collect::<Vec<_>>(); 429 .collect::<Vec<_>>();
403 self.update(module_id, &resolutions); 430 self.update(module_id, &resolutions, vis);
404 } 431 }
405 Some(d) => { 432 Some(d) => {
406 log::debug!("glob import {:?} from non-module/enum {:?}", import, d); 433 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@@ -422,21 +449,24 @@ where
422 } 449 }
423 } 450 }
424 451
425 self.update(module_id, &[(name, def)]); 452 self.update(module_id, &[(name, def)], vis);
426 } 453 }
427 None => tested_by!(bogus_paths), 454 None => tested_by!(bogus_paths),
428 } 455 }
429 } 456 }
430 } 457 }
431 458
432 fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)]) { 459 fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)], vis: Visibility) {
433 self.update_recursive(module_id, resolutions, 0) 460 self.update_recursive(module_id, resolutions, vis, 0)
434 } 461 }
435 462
436 fn update_recursive( 463 fn update_recursive(
437 &mut self, 464 &mut self,
438 module_id: LocalModuleId, 465 module_id: LocalModuleId,
439 resolutions: &[(Name, PerNs)], 466 resolutions: &[(Name, PerNs)],
467 // All resolutions are imported with this visibility; the visibilies in
468 // the `PerNs` values are ignored and overwritten
469 vis: Visibility,
440 depth: usize, 470 depth: usize,
441 ) { 471 ) {
442 if depth > 100 { 472 if depth > 100 {
@@ -446,7 +476,7 @@ where
446 let scope = &mut self.def_map.modules[module_id].scope; 476 let scope = &mut self.def_map.modules[module_id].scope;
447 let mut changed = false; 477 let mut changed = false;
448 for (name, res) in resolutions { 478 for (name, res) in resolutions {
449 changed |= scope.push_res(name.clone(), *res); 479 changed |= scope.push_res(name.clone(), res.with_visibility(vis));
450 } 480 }
451 481
452 if !changed { 482 if !changed {
@@ -459,9 +489,13 @@ where
459 .flat_map(|v| v.iter()) 489 .flat_map(|v| v.iter())
460 .cloned() 490 .cloned()
461 .collect::<Vec<_>>(); 491 .collect::<Vec<_>>();
462 for (glob_importing_module, _glob_import) in glob_imports { 492 for (glob_importing_module, glob_import_vis) in glob_imports {
463 // We pass the glob import so that the tracked import in those modules is that glob import 493 // we know all resolutions have the same visibility (`vis`), so we
464 self.update_recursive(glob_importing_module, resolutions, depth + 1); 494 // just need to check that once
495 if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) {
496 continue;
497 }
498 self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1);
465 } 499 }
466 } 500 }
467 501
@@ -633,9 +667,13 @@ where
633 let is_macro_use = attrs.by_key("macro_use").exists(); 667 let is_macro_use = attrs.by_key("macro_use").exists();
634 match module { 668 match module {
635 // inline module, just recurse 669 // inline module, just recurse
636 raw::ModuleData::Definition { name, items, ast_id } => { 670 raw::ModuleData::Definition { name, visibility, items, ast_id } => {
637 let module_id = 671 let module_id = self.push_child_module(
638 self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None); 672 name.clone(),
673 AstId::new(self.file_id, *ast_id),
674 None,
675 &visibility,
676 );
639 677
640 ModCollector { 678 ModCollector {
641 def_collector: &mut *self.def_collector, 679 def_collector: &mut *self.def_collector,
@@ -650,7 +688,7 @@ where
650 } 688 }
651 } 689 }
652 // out of line module, resolve, parse and recurse 690 // out of line module, resolve, parse and recurse
653 raw::ModuleData::Declaration { name, ast_id } => { 691 raw::ModuleData::Declaration { name, visibility, ast_id } => {
654 let ast_id = AstId::new(self.file_id, *ast_id); 692 let ast_id = AstId::new(self.file_id, *ast_id);
655 match self.mod_dir.resolve_declaration( 693 match self.mod_dir.resolve_declaration(
656 self.def_collector.db, 694 self.def_collector.db,
@@ -659,7 +697,12 @@ where
659 path_attr, 697 path_attr,
660 ) { 698 ) {
661 Ok((file_id, mod_dir)) => { 699 Ok((file_id, mod_dir)) => {
662 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); 700 let module_id = self.push_child_module(
701 name.clone(),
702 ast_id,
703 Some(file_id),
704 &visibility,
705 );
663 let raw_items = self.def_collector.db.raw_items(file_id.into()); 706 let raw_items = self.def_collector.db.raw_items(file_id.into());
664 ModCollector { 707 ModCollector {
665 def_collector: &mut *self.def_collector, 708 def_collector: &mut *self.def_collector,
@@ -690,7 +733,13 @@ where
690 name: Name, 733 name: Name,
691 declaration: AstId<ast::Module>, 734 declaration: AstId<ast::Module>,
692 definition: Option<FileId>, 735 definition: Option<FileId>,
736 visibility: &crate::visibility::RawVisibility,
693 ) -> LocalModuleId { 737 ) -> LocalModuleId {
738 let vis = self
739 .def_collector
740 .def_map
741 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
742 .unwrap_or(Visibility::Public);
694 let modules = &mut self.def_collector.def_map.modules; 743 let modules = &mut self.def_collector.def_map.modules;
695 let res = modules.alloc(ModuleData::default()); 744 let res = modules.alloc(ModuleData::default());
696 modules[res].parent = Some(self.module_id); 745 modules[res].parent = Some(self.module_id);
@@ -702,7 +751,7 @@ where
702 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; 751 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
703 let def: ModuleDefId = module.into(); 752 let def: ModuleDefId = module.into();
704 self.def_collector.def_map.modules[self.module_id].scope.define_def(def); 753 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
705 self.def_collector.update(self.module_id, &[(name, def.into())]); 754 self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis);
706 res 755 res
707 } 756 }
708 757
@@ -716,6 +765,7 @@ where
716 765
717 let name = def.name.clone(); 766 let name = def.name.clone();
718 let container = ContainerId::ModuleId(module); 767 let container = ContainerId::ModuleId(module);
768 let vis = &def.visibility;
719 let def: ModuleDefId = match def.kind { 769 let def: ModuleDefId = match def.kind {
720 raw::DefKind::Function(ast_id) => FunctionLoc { 770 raw::DefKind::Function(ast_id) => FunctionLoc {
721 container: container.into(), 771 container: container.into(),
@@ -761,7 +811,12 @@ where
761 .into(), 811 .into(),
762 }; 812 };
763 self.def_collector.def_map.modules[self.module_id].scope.define_def(def); 813 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
764 self.def_collector.update(self.module_id, &[(name, def.into())]) 814 let vis = self
815 .def_collector
816 .def_map
817 .resolve_visibility(self.def_collector.db, self.module_id, vis)
818 .unwrap_or(Visibility::Public);
819 self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis)
765 } 820 }
766 821
767 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { 822 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs
index 695014c7b..fd6422d60 100644
--- a/crates/ra_hir_def/src/nameres/path_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/path_resolution.rs
@@ -21,6 +21,7 @@ use crate::{
21 nameres::{BuiltinShadowMode, CrateDefMap}, 21 nameres::{BuiltinShadowMode, CrateDefMap},
22 path::{ModPath, PathKind}, 22 path::{ModPath, PathKind},
23 per_ns::PerNs, 23 per_ns::PerNs,
24 visibility::{RawVisibility, Visibility},
24 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, 25 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
25}; 26};
26 27
@@ -61,7 +62,35 @@ impl ResolvePathResult {
61 62
62impl CrateDefMap { 63impl CrateDefMap {
63 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 64 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
64 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) 65 self.extern_prelude
66 .get(name)
67 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
68 }
69
70 pub(crate) fn resolve_visibility(
71 &self,
72 db: &impl DefDatabase,
73 original_module: LocalModuleId,
74 visibility: &RawVisibility,
75 ) -> Option<Visibility> {
76 match visibility {
77 RawVisibility::Module(path) => {
78 let (result, remaining) =
79 self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
80 if remaining.is_some() {
81 return None;
82 }
83 let types = result.take_types()?;
84 match types {
85 ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
86 _ => {
87 // error: visibility needs to refer to module
88 None
89 }
90 }
91 }
92 RawVisibility::Public => Some(Visibility::Public),
93 }
65 } 94 }
66 95
67 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change 96 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@@ -88,17 +117,21 @@ impl CrateDefMap {
88 PathKind::DollarCrate(krate) => { 117 PathKind::DollarCrate(krate) => {
89 if krate == self.krate { 118 if krate == self.krate {
90 tested_by!(macro_dollar_crate_self); 119 tested_by!(macro_dollar_crate_self);
91 PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into()) 120 PerNs::types(
121 ModuleId { krate: self.krate, local_id: self.root }.into(),
122 Visibility::Public,
123 )
92 } else { 124 } else {
93 let def_map = db.crate_def_map(krate); 125 let def_map = db.crate_def_map(krate);
94 let module = ModuleId { krate, local_id: def_map.root }; 126 let module = ModuleId { krate, local_id: def_map.root };
95 tested_by!(macro_dollar_crate_other); 127 tested_by!(macro_dollar_crate_other);
96 PerNs::types(module.into()) 128 PerNs::types(module.into(), Visibility::Public)
97 } 129 }
98 } 130 }
99 PathKind::Crate => { 131 PathKind::Crate => PerNs::types(
100 PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into()) 132 ModuleId { krate: self.krate, local_id: self.root }.into(),
101 } 133 Visibility::Public,
134 ),
102 // plain import or absolute path in 2015: crate-relative with 135 // plain import or absolute path in 2015: crate-relative with
103 // fallback to extern prelude (with the simplification in 136 // fallback to extern prelude (with the simplification in
104 // rust-lang/rust#57745) 137 // rust-lang/rust#57745)
@@ -126,7 +159,10 @@ impl CrateDefMap {
126 let m = successors(Some(original_module), |m| self.modules[*m].parent) 159 let m = successors(Some(original_module), |m| self.modules[*m].parent)
127 .nth(lvl as usize); 160 .nth(lvl as usize);
128 if let Some(local_id) = m { 161 if let Some(local_id) = m {
129 PerNs::types(ModuleId { krate: self.krate, local_id }.into()) 162 PerNs::types(
163 ModuleId { krate: self.krate, local_id }.into(),
164 Visibility::Public,
165 )
130 } else { 166 } else {
131 log::debug!("super path in root module"); 167 log::debug!("super path in root module");
132 return ResolvePathResult::empty(ReachedFixedPoint::Yes); 168 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
@@ -140,7 +176,7 @@ impl CrateDefMap {
140 }; 176 };
141 if let Some(def) = self.extern_prelude.get(&segment) { 177 if let Some(def) = self.extern_prelude.get(&segment) {
142 log::debug!("absolute path {:?} resolved to crate {:?}", path, def); 178 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
143 PerNs::types(*def) 179 PerNs::types(*def, Visibility::Public)
144 } else { 180 } else {
145 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude 181 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
146 } 182 }
@@ -148,7 +184,7 @@ impl CrateDefMap {
148 }; 184 };
149 185
150 for (i, segment) in segments { 186 for (i, segment) in segments {
151 let curr = match curr_per_ns.take_types() { 187 let (curr, vis) = match curr_per_ns.take_types_vis() {
152 Some(r) => r, 188 Some(r) => r,
153 None => { 189 None => {
154 // we still have path segments left, but the path so far 190 // we still have path segments left, but the path so far
@@ -189,11 +225,11 @@ impl CrateDefMap {
189 match enum_data.variant(&segment) { 225 match enum_data.variant(&segment) {
190 Some(local_id) => { 226 Some(local_id) => {
191 let variant = EnumVariantId { parent: e, local_id }; 227 let variant = EnumVariantId { parent: e, local_id };
192 PerNs::both(variant.into(), variant.into()) 228 PerNs::both(variant.into(), variant.into(), Visibility::Public)
193 } 229 }
194 None => { 230 None => {
195 return ResolvePathResult::with( 231 return ResolvePathResult::with(
196 PerNs::types(e.into()), 232 PerNs::types(e.into(), vis),
197 ReachedFixedPoint::Yes, 233 ReachedFixedPoint::Yes,
198 Some(i), 234 Some(i),
199 Some(self.krate), 235 Some(self.krate),
@@ -211,7 +247,7 @@ impl CrateDefMap {
211 ); 247 );
212 248
213 return ResolvePathResult::with( 249 return ResolvePathResult::with(
214 PerNs::types(s), 250 PerNs::types(s, vis),
215 ReachedFixedPoint::Yes, 251 ReachedFixedPoint::Yes,
216 Some(i), 252 Some(i),
217 Some(self.krate), 253 Some(self.krate),
@@ -235,11 +271,15 @@ impl CrateDefMap {
235 // - current module / scope 271 // - current module / scope
236 // - extern prelude 272 // - extern prelude
237 // - std prelude 273 // - std prelude
238 let from_legacy_macro = 274 let from_legacy_macro = self[module]
239 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros); 275 .scope
276 .get_legacy_macro(name)
277 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
240 let from_scope = self[module].scope.get(name, shadow); 278 let from_scope = self[module].scope.get(name, shadow);
241 let from_extern_prelude = 279 let from_extern_prelude = self
242 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); 280 .extern_prelude
281 .get(name)
282 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
243 let from_prelude = self.resolve_in_prelude(db, name, shadow); 283 let from_prelude = self.resolve_in_prelude(db, name, shadow);
244 284
245 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) 285 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 73dc08745..fac1169ef 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -16,12 +16,15 @@ use hir_expand::{
16use ra_arena::{impl_arena_id, Arena, RawId}; 16use ra_arena::{impl_arena_id, Arena, RawId};
17use ra_prof::profile; 17use ra_prof::profile;
18use ra_syntax::{ 18use ra_syntax::{
19 ast::{self, AttrsOwner, NameOwner}, 19 ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
20 AstNode, 20 AstNode,
21}; 21};
22use test_utils::tested_by; 22use test_utils::tested_by;
23 23
24use crate::{attr::Attrs, db::DefDatabase, path::ModPath, FileAstId, HirFileId, InFile}; 24use crate::{
25 attr::Attrs, db::DefDatabase, path::ModPath, visibility::RawVisibility, FileAstId, HirFileId,
26 InFile,
27};
25 28
26/// `RawItems` is a set of top-level items in a file (except for impls). 29/// `RawItems` is a set of top-level items in a file (except for impls).
27/// 30///
@@ -122,8 +125,17 @@ impl_arena_id!(Module);
122 125
123#[derive(Debug, PartialEq, Eq)] 126#[derive(Debug, PartialEq, Eq)]
124pub(super) enum ModuleData { 127pub(super) enum ModuleData {
125 Declaration { name: Name, ast_id: FileAstId<ast::Module> }, 128 Declaration {
126 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, 129 name: Name,
130 visibility: RawVisibility,
131 ast_id: FileAstId<ast::Module>,
132 },
133 Definition {
134 name: Name,
135 visibility: RawVisibility,
136 ast_id: FileAstId<ast::Module>,
137 items: Vec<RawItem>,
138 },
127} 139}
128 140
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -138,6 +150,7 @@ pub struct ImportData {
138 pub(super) is_prelude: bool, 150 pub(super) is_prelude: bool,
139 pub(super) is_extern_crate: bool, 151 pub(super) is_extern_crate: bool,
140 pub(super) is_macro_use: bool, 152 pub(super) is_macro_use: bool,
153 pub(super) visibility: RawVisibility,
141} 154}
142 155
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -148,6 +161,7 @@ impl_arena_id!(Def);
148pub(super) struct DefData { 161pub(super) struct DefData {
149 pub(super) name: Name, 162 pub(super) name: Name,
150 pub(super) kind: DefKind, 163 pub(super) kind: DefKind,
164 pub(super) visibility: RawVisibility,
151} 165}
152 166
153#[derive(Debug, PartialEq, Eq, Clone, Copy)] 167#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -218,6 +232,7 @@ impl RawItemsCollector {
218 232
219 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { 233 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
220 let attrs = self.parse_attrs(&item); 234 let attrs = self.parse_attrs(&item);
235 let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
221 let (kind, name) = match item { 236 let (kind, name) = match item {
222 ast::ModuleItem::Module(module) => { 237 ast::ModuleItem::Module(module) => {
223 self.add_module(current_module, module); 238 self.add_module(current_module, module);
@@ -266,7 +281,7 @@ impl RawItemsCollector {
266 }; 281 };
267 if let Some(name) = name { 282 if let Some(name) = name {
268 let name = name.as_name(); 283 let name = name.as_name();
269 let def = self.raw_items.defs.alloc(DefData { name, kind }); 284 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
270 self.push_item(current_module, attrs, RawItemKind::Def(def)); 285 self.push_item(current_module, attrs, RawItemKind::Def(def));
271 } 286 }
272 } 287 }
@@ -277,10 +292,12 @@ impl RawItemsCollector {
277 None => return, 292 None => return,
278 }; 293 };
279 let attrs = self.parse_attrs(&module); 294 let attrs = self.parse_attrs(&module);
295 let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
280 296
281 let ast_id = self.source_ast_id_map.ast_id(&module); 297 let ast_id = self.source_ast_id_map.ast_id(&module);
282 if module.has_semi() { 298 if module.has_semi() {
283 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); 299 let item =
300 self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
284 self.push_item(current_module, attrs, RawItemKind::Module(item)); 301 self.push_item(current_module, attrs, RawItemKind::Module(item));
285 return; 302 return;
286 } 303 }
@@ -288,6 +305,7 @@ impl RawItemsCollector {
288 if let Some(item_list) = module.item_list() { 305 if let Some(item_list) = module.item_list() {
289 let item = self.raw_items.modules.alloc(ModuleData::Definition { 306 let item = self.raw_items.modules.alloc(ModuleData::Definition {
290 name, 307 name,
308 visibility,
291 ast_id, 309 ast_id,
292 items: Vec::new(), 310 items: Vec::new(),
293 }); 311 });
@@ -302,6 +320,7 @@ impl RawItemsCollector {
302 // FIXME: cfg_attr 320 // FIXME: cfg_attr
303 let is_prelude = use_item.has_atom_attr("prelude_import"); 321 let is_prelude = use_item.has_atom_attr("prelude_import");
304 let attrs = self.parse_attrs(&use_item); 322 let attrs = self.parse_attrs(&use_item);
323 let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
305 324
306 let mut buf = Vec::new(); 325 let mut buf = Vec::new();
307 ModPath::expand_use_item( 326 ModPath::expand_use_item(
@@ -315,6 +334,7 @@ impl RawItemsCollector {
315 is_prelude, 334 is_prelude,
316 is_extern_crate: false, 335 is_extern_crate: false,
317 is_macro_use: false, 336 is_macro_use: false,
337 visibility: visibility.clone(),
318 }; 338 };
319 buf.push(import_data); 339 buf.push(import_data);
320 }, 340 },
@@ -331,6 +351,8 @@ impl RawItemsCollector {
331 ) { 351 ) {
332 if let Some(name_ref) = extern_crate.name_ref() { 352 if let Some(name_ref) = extern_crate.name_ref() {
333 let path = ModPath::from_name_ref(&name_ref); 353 let path = ModPath::from_name_ref(&name_ref);
354 let visibility =
355 RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
334 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); 356 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
335 let attrs = self.parse_attrs(&extern_crate); 357 let attrs = self.parse_attrs(&extern_crate);
336 // FIXME: cfg_attr 358 // FIXME: cfg_attr
@@ -342,6 +364,7 @@ impl RawItemsCollector {
342 is_prelude: false, 364 is_prelude: false,
343 is_extern_crate: true, 365 is_extern_crate: true,
344 is_macro_use, 366 is_macro_use,
367 visibility,
345 }; 368 };
346 self.push_import(current_module, attrs, import_data); 369 self.push_import(current_module, attrs, import_data);
347 } 370 }
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index ff474b53b..78bcdc850 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -12,8 +12,8 @@ use test_utils::covers;
12 12
13use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId}; 13use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId};
14 14
15fn def_map(fixtute: &str) -> String { 15fn def_map(fixture: &str) -> String {
16 let dm = compute_crate_def_map(fixtute); 16 let dm = compute_crate_def_map(fixture);
17 render_crate_def_map(&dm) 17 render_crate_def_map(&dm)
18} 18}
19 19
@@ -32,7 +32,7 @@ fn render_crate_def_map(map: &CrateDefMap) -> String {
32 *buf += path; 32 *buf += path;
33 *buf += "\n"; 33 *buf += "\n";
34 34
35 let mut entries = map.modules[module].scope.collect_resolutions(); 35 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
36 entries.sort_by_key(|(name, _)| name.clone()); 36 entries.sort_by_key(|(name, _)| name.clone());
37 37
38 for (name, def) in entries { 38 for (name, def) in entries {
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs
index 5e24cb94d..71fa0abe8 100644
--- a/crates/ra_hir_def/src/nameres/tests/globs.rs
+++ b/crates/ra_hir_def/src/nameres/tests/globs.rs
@@ -74,6 +74,83 @@ fn glob_2() {
74} 74}
75 75
76#[test] 76#[test]
77fn glob_privacy_1() {
78 let map = def_map(
79 "
80 //- /lib.rs
81 mod foo;
82 use foo::*;
83
84 //- /foo/mod.rs
85 pub mod bar;
86 pub use self::bar::*;
87 struct PrivateStructFoo;
88
89 //- /foo/bar.rs
90 pub struct Baz;
91 struct PrivateStructBar;
92 pub use super::*;
93 ",
94 );
95 assert_snapshot!(map, @r###"
96 crate
97 Baz: t v
98 bar: t
99 foo: t
100
101 crate::foo
102 Baz: t v
103 PrivateStructFoo: t v
104 bar: t
105
106 crate::foo::bar
107 Baz: t v
108 PrivateStructBar: t v
109 PrivateStructFoo: t v
110 bar: t
111 "###
112 );
113}
114
115#[test]
116fn glob_privacy_2() {
117 let map = def_map(
118 "
119 //- /lib.rs
120 mod foo;
121 use foo::*;
122 use foo::bar::*;
123
124 //- /foo/mod.rs
125 mod bar;
126 fn Foo() {};
127 pub struct Foo {};
128
129 //- /foo/bar.rs
130 pub(super) struct PrivateBaz;
131 struct PrivateBar;
132 pub(crate) struct PubCrateStruct;
133 ",
134 );
135 assert_snapshot!(map, @r###"
136 crate
137 Foo: t
138 PubCrateStruct: t v
139 foo: t
140
141 crate::foo
142 Foo: t v
143 bar: t
144
145 crate::foo::bar
146 PrivateBar: t v
147 PrivateBaz: t v
148 PubCrateStruct: t v
149 "###
150 );
151}
152
153#[test]
77fn glob_across_crates() { 154fn glob_across_crates() {
78 covers!(glob_across_crates); 155 covers!(glob_across_crates);
79 let map = def_map( 156 let map = def_map(
@@ -93,6 +170,26 @@ fn glob_across_crates() {
93} 170}
94 171
95#[test] 172#[test]
173fn glob_privacy_across_crates() {
174 covers!(glob_across_crates);
175 let map = def_map(
176 "
177 //- /main.rs crate:main deps:test_crate
178 use test_crate::*;
179
180 //- /lib.rs crate:test_crate
181 pub struct Baz;
182 struct Foo;
183 ",
184 );
185 assert_snapshot!(map, @r###"
186 ⋮crate
187 ⋮Baz: t v
188 "###
189 );
190}
191
192#[test]
96fn glob_enum() { 193fn glob_enum() {
97 covers!(glob_enum); 194 covers!(glob_enum);
98 let map = def_map( 195 let map = def_map(
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
index ef2e9435c..faeb7aa4d 100644
--- a/crates/ra_hir_def/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -116,7 +116,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
116 let events = db.log_executed(|| { 116 let events = db.log_executed(|| {
117 let crate_def_map = db.crate_def_map(krate); 117 let crate_def_map = db.crate_def_map(krate);
118 let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); 118 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
119 assert_eq!(module_data.scope.collect_resolutions().len(), 1); 119 assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
120 }); 120 });
121 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) 121 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
122 } 122 }
@@ -126,7 +126,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
126 let events = db.log_executed(|| { 126 let events = db.log_executed(|| {
127 let crate_def_map = db.crate_def_map(krate); 127 let crate_def_map = db.crate_def_map(krate);
128 let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); 128 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
129 assert_eq!(module_data.scope.collect_resolutions().len(), 1); 129 assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
130 }); 130 });
131 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) 131 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
132 } 132 }
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 107d2d799..82cfa67a9 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -260,12 +260,8 @@ macro_rules! __known_path {
260 (std::ops::RangeTo) => {}; 260 (std::ops::RangeTo) => {};
261 (std::ops::RangeToInclusive) => {}; 261 (std::ops::RangeToInclusive) => {};
262 (std::ops::RangeInclusive) => {}; 262 (std::ops::RangeInclusive) => {};
263 (std::boxed::Box) => {};
264 (std::future::Future) => {}; 263 (std::future::Future) => {};
265 (std::ops::Try) => {}; 264 (std::ops::Try) => {};
266 (std::ops::Neg) => {};
267 (std::ops::Not) => {};
268 (std::ops::Index) => {};
269 ($path:path) => { 265 ($path:path) => {
270 compile_error!("Please register your known path in the path module") 266 compile_error!("Please register your known path in the path module")
271 }; 267 };
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 3a5105028..6e435c8c1 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,13 +5,13 @@
5 5
6use hir_expand::MacroDefId; 6use hir_expand::MacroDefId;
7 7
8use crate::ModuleDefId; 8use crate::{visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs { 11pub struct PerNs {
12 pub types: Option<ModuleDefId>, 12 pub types: Option<(ModuleDefId, Visibility)>,
13 pub values: Option<ModuleDefId>, 13 pub values: Option<(ModuleDefId, Visibility)>,
14 pub macros: Option<MacroDefId>, 14 pub macros: Option<(MacroDefId, Visibility)>,
15} 15}
16 16
17impl Default for PerNs { 17impl Default for PerNs {
@@ -25,20 +25,20 @@ impl PerNs {
25 PerNs { types: None, values: None, macros: None } 25 PerNs { types: None, values: None, macros: None }
26 } 26 }
27 27
28 pub fn values(t: ModuleDefId) -> PerNs { 28 pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
29 PerNs { types: None, values: Some(t), macros: None } 29 PerNs { types: None, values: Some((t, v)), macros: None }
30 } 30 }
31 31
32 pub fn types(t: ModuleDefId) -> PerNs { 32 pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
33 PerNs { types: Some(t), values: None, macros: None } 33 PerNs { types: Some((t, v)), values: None, macros: None }
34 } 34 }
35 35
36 pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs { 36 pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
37 PerNs { types: Some(types), values: Some(values), macros: None } 37 PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
38 } 38 }
39 39
40 pub fn macros(macro_: MacroDefId) -> PerNs { 40 pub fn macros(macro_: MacroDefId, v: Visibility) -> PerNs {
41 PerNs { types: None, values: None, macros: Some(macro_) } 41 PerNs { types: None, values: None, macros: Some((macro_, v)) }
42 } 42 }
43 43
44 pub fn is_none(&self) -> bool { 44 pub fn is_none(&self) -> bool {
@@ -46,15 +46,35 @@ impl PerNs {
46 } 46 }
47 47
48 pub fn take_types(self) -> Option<ModuleDefId> { 48 pub fn take_types(self) -> Option<ModuleDefId> {
49 self.types.map(|it| it.0)
50 }
51
52 pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
49 self.types 53 self.types
50 } 54 }
51 55
52 pub fn take_values(self) -> Option<ModuleDefId> { 56 pub fn take_values(self) -> Option<ModuleDefId> {
53 self.values 57 self.values.map(|it| it.0)
54 } 58 }
55 59
56 pub fn take_macros(self) -> Option<MacroDefId> { 60 pub fn take_macros(self) -> Option<MacroDefId> {
57 self.macros 61 self.macros.map(|it| it.0)
62 }
63
64 pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
65 PerNs {
66 types: self.types.filter(|(_, v)| f(*v)),
67 values: self.values.filter(|(_, v)| f(*v)),
68 macros: self.macros.filter(|(_, v)| f(*v)),
69 }
70 }
71
72 pub fn with_visibility(self, vis: Visibility) -> PerNs {
73 PerNs {
74 types: self.types.map(|(it, _)| (it, vis)),
75 values: self.values.map(|(it, _)| (it, vis)),
76 macros: self.macros.map(|(it, _)| (it, vis)),
77 }
58 } 78 }
59 79
60 pub fn or(self, other: PerNs) -> PerNs { 80 pub fn or(self, other: PerNs) -> PerNs {
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index cf3c33d78..5d16dd087 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -19,6 +19,7 @@ use crate::{
19 nameres::CrateDefMap, 19 nameres::CrateDefMap,
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility},
22 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, 23 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
23 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, 24 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
24 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, 25 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
@@ -231,6 +232,23 @@ impl Resolver {
231 Some(res) 232 Some(res)
232 } 233 }
233 234
235 pub fn resolve_visibility(
236 &self,
237 db: &impl DefDatabase,
238 visibility: &RawVisibility,
239 ) -> Option<Visibility> {
240 match visibility {
241 RawVisibility::Module(_) => {
242 let (item_map, module) = match self.module() {
243 Some(it) => it,
244 None => return None,
245 };
246 item_map.resolve_visibility(db, module, visibility)
247 }
248 RawVisibility::Public => Some(Visibility::Public),
249 }
250 }
251
234 pub fn resolve_path_in_value_ns( 252 pub fn resolve_path_in_value_ns(
235 &self, 253 &self,
236 db: &impl DefDatabase, 254 db: &impl DefDatabase,
@@ -448,10 +466,10 @@ impl Scope {
448 f(name.clone(), ScopeDef::PerNs(def)); 466 f(name.clone(), ScopeDef::PerNs(def));
449 }); 467 });
450 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { 468 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
451 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_))); 469 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public)));
452 }); 470 });
453 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { 471 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
454 f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into()))); 472 f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into(), Visibility::Public)));
455 }); 473 });
456 if let Some(prelude) = m.crate_def_map.prelude { 474 if let Some(prelude) = m.crate_def_map.prelude {
457 let prelude_def_map = db.crate_def_map(prelude.krate); 475 let prelude_def_map = db.crate_def_map(prelude.krate);
@@ -626,7 +644,7 @@ impl HasResolver for ContainerId {
626 fn resolver(self, db: &impl DefDatabase) -> Resolver { 644 fn resolver(self, db: &impl DefDatabase) -> Resolver {
627 match self { 645 match self {
628 ContainerId::ModuleId(it) => it.resolver(db), 646 ContainerId::ModuleId(it) => it.resolver(db),
629 ContainerId::DefWithBodyId(it) => it.resolver(db), 647 ContainerId::DefWithBodyId(it) => it.module(db).resolver(db),
630 } 648 }
631 } 649 }
632} 650}
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
new file mode 100644
index 000000000..d8296da4b
--- /dev/null
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -0,0 +1,120 @@
1//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
2
3use hir_expand::{hygiene::Hygiene, InFile};
4use ra_syntax::ast;
5
6use crate::{
7 db::DefDatabase,
8 path::{ModPath, PathKind},
9 ModuleId,
10};
11
12/// Visibility of an item, not yet resolved.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum RawVisibility {
15 /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
16 /// equivalent to `pub(self)`.
17 Module(ModPath),
18 /// `pub`.
19 Public,
20}
21
22impl RawVisibility {
23 const fn private() -> RawVisibility {
24 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
25 RawVisibility::Module(path)
26 }
27
28 pub(crate) fn from_ast(
29 db: &impl DefDatabase,
30 node: InFile<Option<ast::Visibility>>,
31 ) -> RawVisibility {
32 Self::from_ast_with_hygiene(node.value, &Hygiene::new(db, node.file_id))
33 }
34
35 pub(crate) fn from_ast_with_hygiene(
36 node: Option<ast::Visibility>,
37 hygiene: &Hygiene,
38 ) -> RawVisibility {
39 let node = match node {
40 None => return RawVisibility::private(),
41 Some(node) => node,
42 };
43 match node.kind() {
44 ast::VisibilityKind::In(path) => {
45 let path = ModPath::from_src(path, hygiene);
46 let path = match path {
47 None => return RawVisibility::private(),
48 Some(path) => path,
49 };
50 RawVisibility::Module(path)
51 }
52 ast::VisibilityKind::PubCrate => {
53 let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
54 RawVisibility::Module(path)
55 }
56 ast::VisibilityKind::PubSuper => {
57 let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
58 RawVisibility::Module(path)
59 }
60 ast::VisibilityKind::Pub => RawVisibility::Public,
61 }
62 }
63
64 pub fn resolve(
65 &self,
66 db: &impl DefDatabase,
67 resolver: &crate::resolver::Resolver,
68 ) -> Visibility {
69 // we fall back to public visibility (i.e. fail open) if the path can't be resolved
70 resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
71 }
72}
73
74/// Visibility of an item, with the path resolved.
75#[derive(Debug, Copy, Clone, PartialEq, Eq)]
76pub enum Visibility {
77 /// Visibility is restricted to a certain module.
78 Module(ModuleId),
79 /// Visibility is unrestricted.
80 Public,
81}
82
83impl Visibility {
84 pub fn is_visible_from(self, db: &impl DefDatabase, from_module: ModuleId) -> bool {
85 let to_module = match self {
86 Visibility::Module(m) => m,
87 Visibility::Public => return true,
88 };
89 // if they're not in the same crate, it can't be visible
90 if from_module.krate != to_module.krate {
91 return false;
92 }
93 let def_map = db.crate_def_map(from_module.krate);
94 self.is_visible_from_def_map(&def_map, from_module.local_id)
95 }
96
97 pub(crate) fn is_visible_from_other_crate(self) -> bool {
98 match self {
99 Visibility::Module(_) => false,
100 Visibility::Public => true,
101 }
102 }
103
104 pub(crate) fn is_visible_from_def_map(
105 self,
106 def_map: &crate::nameres::CrateDefMap,
107 from_module: crate::LocalModuleId,
108 ) -> bool {
109 let to_module = match self {
110 Visibility::Module(m) => m,
111 Visibility::Public => return true,
112 };
113 // from_module needs to be a descendant of to_module
114 let mut ancestors = std::iter::successors(Some(from_module), |m| {
115 let parent_id = def_map[*m].parent?;
116 Some(parent_id)
117 });
118 ancestors.any(|m| m == to_module.local_id)
119 }
120}
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 32c0d07a5..37e69599d 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -24,6 +24,7 @@ use hir_def::{
24 body::Body, 24 body::Body,
25 data::{ConstData, FunctionData}, 25 data::{ConstData, FunctionData},
26 expr::{BindingAnnotation, ExprId, PatId}, 26 expr::{BindingAnnotation, ExprId, PatId},
27 lang_item::LangItemTarget,
27 path::{path, Path}, 28 path::{path, Path},
28 resolver::{HasResolver, Resolver, TypeNs}, 29 resolver::{HasResolver, Resolver, TypeNs},
29 type_ref::{Mutability, TypeRef}, 30 type_ref::{Mutability, TypeRef},
@@ -32,6 +33,7 @@ use hir_def::{
32use hir_expand::{diagnostics::DiagnosticSink, name::name}; 33use hir_expand::{diagnostics::DiagnosticSink, name::name};
33use ra_arena::map::ArenaMap; 34use ra_arena::map::ArenaMap;
34use ra_prof::profile; 35use ra_prof::profile;
36use ra_syntax::SmolStr;
35use test_utils::tested_by; 37use test_utils::tested_by;
36 38
37use super::{ 39use super::{
@@ -482,6 +484,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
482 self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone())); 484 self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
483 } 485 }
484 486
487 fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> {
488 let krate = self.resolver.krate()?;
489 let name = SmolStr::new_inline_from_ascii(name.len(), name.as_bytes());
490 self.db.lang_item(krate, name)
491 }
492
485 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 493 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
486 let path = path![std::iter::IntoIterator]; 494 let path = path![std::iter::IntoIterator];
487 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; 495 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
@@ -495,26 +503,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
495 } 503 }
496 504
497 fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> { 505 fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
498 let path = path![std::ops::Neg]; 506 let trait_ = self.resolve_lang_item("neg")?.as_trait()?;
499 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
500 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 507 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
501 } 508 }
502 509
503 fn resolve_ops_not_output(&self) -> Option<TypeAliasId> { 510 fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
504 let path = path![std::ops::Not]; 511 let trait_ = self.resolve_lang_item("not")?.as_trait()?;
505 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
506 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 512 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
507 } 513 }
508 514
509 fn resolve_future_future_output(&self) -> Option<TypeAliasId> { 515 fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
510 let path = path![std::future::Future]; 516 let trait_ = self.resolve_lang_item("future_trait")?.as_trait()?;
511 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
512 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 517 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
513 } 518 }
514 519
515 fn resolve_boxed_box(&self) -> Option<AdtId> { 520 fn resolve_boxed_box(&self) -> Option<AdtId> {
516 let path = path![std::boxed::Box]; 521 let struct_ = self.resolve_lang_item("owned_box")?.as_struct()?;
517 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
518 Some(struct_.into()) 522 Some(struct_.into())
519 } 523 }
520 524
@@ -555,8 +559,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
555 } 559 }
556 560
557 fn resolve_ops_index_output(&self) -> Option<TypeAliasId> { 561 fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
558 let path = path![std::ops::Index]; 562 let trait_ = self.resolve_lang_item("index")?.as_trait()?;
559 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
560 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 563 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
561 } 564 }
562} 565}
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index d447b4571..d1f10e675 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -11,8 +11,8 @@ use std::fmt::Write;
11use std::sync::Arc; 11use std::sync::Arc;
12 12
13use hir_def::{ 13use hir_def::{
14 body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys, 14 body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, item_scope::ItemScope,
15 nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 15 keys, nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
16}; 16};
17use hir_expand::InFile; 17use hir_expand::InFile;
18use insta::assert_snapshot; 18use insta::assert_snapshot;
@@ -163,35 +163,69 @@ fn visit_module(
163 module_id: LocalModuleId, 163 module_id: LocalModuleId,
164 cb: &mut dyn FnMut(DefWithBodyId), 164 cb: &mut dyn FnMut(DefWithBodyId),
165) { 165) {
166 for decl in crate_def_map[module_id].scope.declarations() { 166 visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb);
167 match decl {
168 ModuleDefId::FunctionId(it) => cb(it.into()),
169 ModuleDefId::ConstId(it) => cb(it.into()),
170 ModuleDefId::StaticId(it) => cb(it.into()),
171 ModuleDefId::TraitId(it) => {
172 let trait_data = db.trait_data(it);
173 for &(_, item) in trait_data.items.iter() {
174 match item {
175 AssocItemId::FunctionId(it) => cb(it.into()),
176 AssocItemId::ConstId(it) => cb(it.into()),
177 AssocItemId::TypeAliasId(_) => (),
178 }
179 }
180 }
181 ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it.local_id, cb),
182 _ => (),
183 }
184 }
185 for impl_id in crate_def_map[module_id].scope.impls() { 167 for impl_id in crate_def_map[module_id].scope.impls() {
186 let impl_data = db.impl_data(impl_id); 168 let impl_data = db.impl_data(impl_id);
187 for &item in impl_data.items.iter() { 169 for &item in impl_data.items.iter() {
188 match item { 170 match item {
189 AssocItemId::FunctionId(it) => cb(it.into()), 171 AssocItemId::FunctionId(it) => {
190 AssocItemId::ConstId(it) => cb(it.into()), 172 let def = it.into();
173 cb(def);
174 let body = db.body(def);
175 visit_scope(db, crate_def_map, &body.item_scope, cb);
176 }
177 AssocItemId::ConstId(it) => {
178 let def = it.into();
179 cb(def);
180 let body = db.body(def);
181 visit_scope(db, crate_def_map, &body.item_scope, cb);
182 }
191 AssocItemId::TypeAliasId(_) => (), 183 AssocItemId::TypeAliasId(_) => (),
192 } 184 }
193 } 185 }
194 } 186 }
187
188 fn visit_scope(
189 db: &TestDB,
190 crate_def_map: &CrateDefMap,
191 scope: &ItemScope,
192 cb: &mut dyn FnMut(DefWithBodyId),
193 ) {
194 for decl in scope.declarations() {
195 match decl {
196 ModuleDefId::FunctionId(it) => {
197 let def = it.into();
198 cb(def);
199 let body = db.body(def);
200 visit_scope(db, crate_def_map, &body.item_scope, cb);
201 }
202 ModuleDefId::ConstId(it) => {
203 let def = it.into();
204 cb(def);
205 let body = db.body(def);
206 visit_scope(db, crate_def_map, &body.item_scope, cb);
207 }
208 ModuleDefId::StaticId(it) => {
209 let def = it.into();
210 cb(def);
211 let body = db.body(def);
212 visit_scope(db, crate_def_map, &body.item_scope, cb);
213 }
214 ModuleDefId::TraitId(it) => {
215 let trait_data = db.trait_data(it);
216 for &(_, item) in trait_data.items.iter() {
217 match item {
218 AssocItemId::FunctionId(it) => cb(it.into()),
219 AssocItemId::ConstId(it) => cb(it.into()),
220 AssocItemId::TypeAliasId(_) => (),
221 }
222 }
223 }
224 ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it.local_id, cb),
225 _ => (),
226 }
227 }
228 }
195} 229}
196 230
197fn ellipsize(mut text: String, max_len: usize) -> String { 231fn ellipsize(mut text: String, max_len: usize) -> String {
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 09d684ac2..8b3aa8564 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -1,7 +1,8 @@
1use super::infer;
2use insta::assert_snapshot; 1use insta::assert_snapshot;
3use test_utils::covers; 2use test_utils::covers;
4 3
4use super::infer;
5
5#[test] 6#[test]
6fn bug_484() { 7fn bug_484() {
7 assert_snapshot!( 8 assert_snapshot!(
@@ -331,3 +332,36 @@ pub fn main_loop() {
331 "### 332 "###
332 ); 333 );
333} 334}
335
336#[test]
337fn issue_2669() {
338 assert_snapshot!(
339 infer(
340 r#"trait A {}
341 trait Write {}
342 struct Response<T> {}
343
344 trait D {
345 fn foo();
346 }
347
348 impl<T:A> D for Response<T> {
349 fn foo() {
350 end();
351 fn end<W: Write>() {
352 let _x: T = loop {};
353 }
354 }
355 }"#
356 ),
357 @r###"
358 [147; 262) '{ ... }': ()
359 [161; 164) 'end': fn end<{unknown}>() -> ()
360 [161; 166) 'end()': ()
361 [199; 252) '{ ... }': ()
362 [221; 223) '_x': !
363 [230; 237) 'loop {}': !
364 [235; 237) '{}': ()
365 "###
366 )
367}
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 3e5e163e3..f7e042c12 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -20,6 +20,7 @@ fn test() {
20mod prelude {} 20mod prelude {}
21 21
22mod boxed { 22mod boxed {
23 #[lang = "owned_box"]
23 pub struct Box<T: ?Sized> { 24 pub struct Box<T: ?Sized> {
24 inner: *mut T, 25 inner: *mut T,
25 } 26 }
@@ -1518,6 +1519,7 @@ fn test() {
1518 [167; 179) 'GLOBAL_CONST': u32 1519 [167; 179) 'GLOBAL_CONST': u32
1519 [189; 191) 'id': u32 1520 [189; 191) 'id': u32
1520 [194; 210) 'Foo::A..._CONST': u32 1521 [194; 210) 'Foo::A..._CONST': u32
1522 [126; 128) '99': u32
1521 "### 1523 "###
1522 ); 1524 );
1523} 1525}
@@ -1549,6 +1551,8 @@ fn test() {
1549 [233; 246) 'GLOBAL_STATIC': u32 1551 [233; 246) 'GLOBAL_STATIC': u32
1550 [256; 257) 'w': u32 1552 [256; 257) 'w': u32
1551 [260; 277) 'GLOBAL...IC_MUT': u32 1553 [260; 277) 'GLOBAL...IC_MUT': u32
1554 [118; 120) '99': u32
1555 [161; 163) '99': u32
1552 "### 1556 "###
1553 ); 1557 );
1554} 1558}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 0bc72644a..4b268510c 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -27,6 +27,7 @@ fn test() {
27//- /std.rs crate:std 27//- /std.rs crate:std
28#[prelude_import] use future::*; 28#[prelude_import] use future::*;
29mod future { 29mod future {
30 #[lang = "future_trait"]
30 trait Future { 31 trait Future {
31 type Output; 32 type Output;
32 } 33 }
@@ -56,6 +57,7 @@ fn test() {
56//- /std.rs crate:std 57//- /std.rs crate:std
57#[prelude_import] use future::*; 58#[prelude_import] use future::*;
58mod future { 59mod future {
60 #[lang = "future_trait"]
59 trait Future { 61 trait Future {
60 type Output; 62 type Output;
61 } 63 }
@@ -198,6 +200,7 @@ fn test() {
198 200
199#[prelude_import] use ops::*; 201#[prelude_import] use ops::*;
200mod ops { 202mod ops {
203 #[lang = "neg"]
201 pub trait Neg { 204 pub trait Neg {
202 type Output; 205 type Output;
203 } 206 }
@@ -230,6 +233,7 @@ fn test() {
230 233
231#[prelude_import] use ops::*; 234#[prelude_import] use ops::*;
232mod ops { 235mod ops {
236 #[lang = "not"]
233 pub trait Not { 237 pub trait Not {
234 type Output; 238 type Output;
235 } 239 }
@@ -506,6 +510,7 @@ fn test() {
506 510
507#[prelude_import] use ops::*; 511#[prelude_import] use ops::*;
508mod ops { 512mod ops {
513 #[lang = "index"]
509 pub trait Index<Idx> { 514 pub trait Index<Idx> {
510 type Output; 515 type Output;
511 } 516 }
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 294964887..210a685e4 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::Type; 3use hir::{HasVisibility, Type};
4 4
5use crate::completion::completion_item::CompletionKind; 5use crate::completion::completion_item::CompletionKind;
6use crate::{ 6use crate::{
@@ -38,9 +38,15 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 38fn 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)) {
42 // Skip private field. FIXME: If the definition location of the
43 // field is editable, we should show the completion
44 continue;
45 }
41 acc.add_field(ctx, field, &ty); 46 acc.add_field(ctx, field, &ty);
42 } 47 }
43 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 48 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
49 // FIXME: Handle visibility
44 acc.add_tuple_field(ctx, i, &ty); 50 acc.add_tuple_field(ctx, i, &ty);
45 } 51 }
46 } 52 }
@@ -187,6 +193,55 @@ mod tests {
187 } 193 }
188 194
189 #[test] 195 #[test]
196 fn test_struct_field_visibility_private() {
197 assert_debug_snapshot!(
198 do_ref_completion(
199 r"
200 mod inner {
201 struct A {
202 private_field: u32,
203 pub pub_field: u32,
204 pub(crate) crate_field: u32,
205 pub(super) super_field: u32,
206 }
207 }
208 fn foo(a: inner::A) {
209 a.<|>
210 }
211 ",
212 ),
213 @r###"
214 [
215 CompletionItem {
216 label: "crate_field",
217 source_range: [313; 313),
218 delete: [313; 313),
219 insert: "crate_field",
220 kind: Field,
221 detail: "u32",
222 },
223 CompletionItem {
224 label: "pub_field",
225 source_range: [313; 313),
226 delete: [313; 313),
227 insert: "pub_field",
228 kind: Field,
229 detail: "u32",
230 },
231 CompletionItem {
232 label: "super_field",
233 source_range: [313; 313),
234 delete: [313; 313),
235 insert: "super_field",
236 kind: Field,
237 detail: "u32",
238 },
239 ]
240 "###
241 );
242 }
243
244 #[test]
190 fn test_method_completion() { 245 fn test_method_completion() {
191 assert_debug_snapshot!( 246 assert_debug_snapshot!(
192 do_ref_completion( 247 do_ref_completion(
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index a097cf8e8..1d130544f 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -38,12 +38,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
38<span class="keyword">fn</span> <span class="function">main</span>() { 38<span class="keyword">fn</span> <span class="function">main</span>() {
39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); 39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>);
40 40
41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>(); 41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = Vec::new();
42 <span class="keyword.control">if</span> <span class="keyword">true</span> { 42 <span class="keyword.control">if</span> <span class="keyword">true</span> {
43 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>; 43 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>;
44 <span class="variable.mut">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> }); 44 <span class="variable.mut">vec</span>.push(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> });
45 } 45 }
46 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.<span class="text">set_len</span>(<span class="literal.numeric">0</span>); } 46 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.set_len(<span class="literal.numeric">0</span>); }
47 47
48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>; 48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>;
49 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; 49 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>;
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 110556c09..d90ee8540 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -25,11 +25,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25</style> 25</style>
26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() { 26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
27 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>; 27 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>;
28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>(); 28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string();
29 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>(); 29 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string();
30 30
31 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>; 31 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>;
32 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.<span class="text">to_string</span>(); 32 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.to_string();
33} 33}
34 34
35<span class="keyword">fn</span> <span class="function">bar</span>() { 35<span class="keyword">fn</span> <span class="function">bar</span>() {
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0228ee7e9..56a36f587 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -20,13 +20,13 @@ pub mod tags {
20 pub(crate) const FIELD: &str = "field"; 20 pub(crate) const FIELD: &str = "field";
21 pub(crate) const FUNCTION: &str = "function"; 21 pub(crate) const FUNCTION: &str = "function";
22 pub(crate) const MODULE: &str = "module"; 22 pub(crate) const MODULE: &str = "module";
23 pub(crate) const TYPE: &str = "type";
24 pub(crate) const CONSTANT: &str = "constant"; 23 pub(crate) const CONSTANT: &str = "constant";
25 pub(crate) const MACRO: &str = "macro"; 24 pub(crate) const MACRO: &str = "macro";
25
26 pub(crate) const VARIABLE: &str = "variable"; 26 pub(crate) const VARIABLE: &str = "variable";
27 pub(crate) const VARIABLE_MUT: &str = "variable.mut"; 27 pub(crate) const VARIABLE_MUT: &str = "variable.mut";
28 pub(crate) const TEXT: &str = "text";
29 28
29 pub(crate) const TYPE: &str = "type";
30 pub(crate) const TYPE_BUILTIN: &str = "type.builtin"; 30 pub(crate) const TYPE_BUILTIN: &str = "type.builtin";
31 pub(crate) const TYPE_SELF: &str = "type.self"; 31 pub(crate) const TYPE_SELF: &str = "type.self";
32 pub(crate) const TYPE_PARAM: &str = "type.param"; 32 pub(crate) const TYPE_PARAM: &str = "type.param";
@@ -35,13 +35,14 @@ pub mod tags {
35 pub(crate) const LITERAL_BYTE: &str = "literal.byte"; 35 pub(crate) const LITERAL_BYTE: &str = "literal.byte";
36 pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric"; 36 pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric";
37 pub(crate) const LITERAL_CHAR: &str = "literal.char"; 37 pub(crate) const LITERAL_CHAR: &str = "literal.char";
38
38 pub(crate) const LITERAL_COMMENT: &str = "comment"; 39 pub(crate) const LITERAL_COMMENT: &str = "comment";
39 pub(crate) const LITERAL_STRING: &str = "string"; 40 pub(crate) const LITERAL_STRING: &str = "string";
40 pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute"; 41 pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute";
41 42
43 pub(crate) const KEYWORD: &str = "keyword";
42 pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe"; 44 pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe";
43 pub(crate) const KEYWORD_CONTROL: &str = "keyword.control"; 45 pub(crate) const KEYWORD_CONTROL: &str = "keyword.control";
44 pub(crate) const KEYWORD: &str = "keyword";
45} 46}
46 47
47#[derive(Debug)] 48#[derive(Debug)]
@@ -109,15 +110,21 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
109 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 110 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
110 let name_kind = 111 let name_kind =
111 classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind); 112 classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind);
113 match name_kind {
114 Some(name_kind) => {
115 if let Local(local) = &name_kind {
116 if let Some(name) = local.name(db) {
117 let shadow_count =
118 bindings_shadow_count.entry(name.clone()).or_default();
119 binding_hash =
120 Some(calc_binding_hash(file_id, &name, *shadow_count))
121 }
122 };
112 123
113 if let Some(Local(local)) = &name_kind { 124 highlight_name(db, name_kind)
114 if let Some(name) = local.name(db) {
115 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
116 binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
117 } 125 }
118 }; 126 _ => continue,
119 127 }
120 name_kind.map_or(tags::TEXT, |it| highlight_name(db, it))
121 } 128 }
122 NAME => { 129 NAME => {
123 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); 130 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 030e9033c..9b7dcb6e9 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -27,6 +27,7 @@ ra_project_model = { path = "../ra_project_model" }
27ra_prof = { path = "../ra_prof" } 27ra_prof = { path = "../ra_prof" }
28ra_vfs_glob = { path = "../ra_vfs_glob" } 28ra_vfs_glob = { path = "../ra_vfs_glob" }
29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } 29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] }
30ra_cargo_watch = { path = "../ra_cargo_watch" }
30 31
31[dev-dependencies] 32[dev-dependencies]
32tempfile = "3" 33tempfile = "3"
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index ceb4c4259..0dee1f6fe 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -3,7 +3,7 @@
3use lsp_types::{ 3use lsp_types::{
4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, 5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, 6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, 7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, 8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
9 TypeDefinitionProviderCapability, WorkDoneProgressOptions, 9 TypeDefinitionProviderCapability, WorkDoneProgressOptions,
@@ -16,7 +16,7 @@ pub fn server_capabilities() -> ServerCapabilities {
16 change: Some(TextDocumentSyncKind::Full), 16 change: Some(TextDocumentSyncKind::Full),
17 will_save: None, 17 will_save: None,
18 will_save_wait_until: None, 18 will_save_wait_until: None,
19 save: None, 19 save: Some(SaveOptions::default()),
20 })), 20 })),
21 hover_provider: Some(true), 21 hover_provider: Some(true),
22 completion_provider: Some(CompletionOptions { 22 completion_provider: Some(CompletionOptions {
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs
index 67942aa41..2d7948d74 100644
--- a/crates/ra_lsp_server/src/config.rs
+++ b/crates/ra_lsp_server/src/config.rs
@@ -32,6 +32,11 @@ pub struct ServerConfig {
32 32
33 pub max_inlay_hint_length: Option<usize>, 33 pub max_inlay_hint_length: Option<usize>,
34 34
35 pub cargo_watch_enable: bool,
36 pub cargo_watch_args: Vec<String>,
37 pub cargo_watch_command: String,
38 pub cargo_watch_all_targets: bool,
39
35 /// For internal usage to make integrated tests faster. 40 /// For internal usage to make integrated tests faster.
36 #[serde(deserialize_with = "nullable_bool_true")] 41 #[serde(deserialize_with = "nullable_bool_true")]
37 pub with_sysroot: bool, 42 pub with_sysroot: bool,
@@ -51,6 +56,10 @@ impl Default for ServerConfig {
51 use_client_watching: false, 56 use_client_watching: false,
52 lru_capacity: None, 57 lru_capacity: None,
53 max_inlay_hint_length: None, 58 max_inlay_hint_length: None,
59 cargo_watch_enable: true,
60 cargo_watch_args: Vec::new(),
61 cargo_watch_command: "check".to_string(),
62 cargo_watch_all_targets: true,
54 with_sysroot: true, 63 with_sysroot: true,
55 feature_flags: FxHashMap::default(), 64 feature_flags: FxHashMap::default(),
56 cargo_features: Default::default(), 65 cargo_features: Default::default(),
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index dda318e43..4336583fe 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -10,6 +10,7 @@ use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant};
10use crossbeam_channel::{select, unbounded, RecvError, Sender}; 10use crossbeam_channel::{select, unbounded, RecvError, Sender};
11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
12use lsp_types::{ClientCapabilities, NumberOrString}; 12use lsp_types::{ClientCapabilities, NumberOrString};
13use ra_cargo_watch::{CheckOptions, CheckTask};
13use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; 14use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId};
14use ra_prof::profile; 15use ra_prof::profile;
15use ra_vfs::{VfsTask, Watch}; 16use ra_vfs::{VfsTask, Watch};
@@ -126,6 +127,12 @@ pub fn main_loop(
126 .and_then(|it| it.line_folding_only) 127 .and_then(|it| it.line_folding_only)
127 .unwrap_or(false), 128 .unwrap_or(false),
128 max_inlay_hint_length: config.max_inlay_hint_length, 129 max_inlay_hint_length: config.max_inlay_hint_length,
130 cargo_watch: CheckOptions {
131 enable: config.cargo_watch_enable,
132 args: config.cargo_watch_args,
133 command: config.cargo_watch_command,
134 all_targets: config.cargo_watch_all_targets,
135 },
129 } 136 }
130 }; 137 };
131 138
@@ -176,7 +183,11 @@ pub fn main_loop(
176 Ok(task) => Event::Vfs(task), 183 Ok(task) => Event::Vfs(task),
177 Err(RecvError) => Err("vfs died")?, 184 Err(RecvError) => Err("vfs died")?,
178 }, 185 },
179 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) 186 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
187 recv(world_state.check_watcher.task_recv) -> task => match task {
188 Ok(task) => Event::CheckWatcher(task),
189 Err(RecvError) => Err("check watcher died")?,
190 }
180 }; 191 };
181 if let Event::Msg(Message::Request(req)) = &event { 192 if let Event::Msg(Message::Request(req)) = &event {
182 if connection.handle_shutdown(&req)? { 193 if connection.handle_shutdown(&req)? {
@@ -222,6 +233,7 @@ enum Event {
222 Task(Task), 233 Task(Task),
223 Vfs(VfsTask), 234 Vfs(VfsTask),
224 Lib(LibraryData), 235 Lib(LibraryData),
236 CheckWatcher(CheckTask),
225} 237}
226 238
227impl fmt::Debug for Event { 239impl fmt::Debug for Event {
@@ -259,6 +271,7 @@ impl fmt::Debug for Event {
259 Event::Task(it) => fmt::Debug::fmt(it, f), 271 Event::Task(it) => fmt::Debug::fmt(it, f),
260 Event::Vfs(it) => fmt::Debug::fmt(it, f), 272 Event::Vfs(it) => fmt::Debug::fmt(it, f),
261 Event::Lib(it) => fmt::Debug::fmt(it, f), 273 Event::Lib(it) => fmt::Debug::fmt(it, f),
274 Event::CheckWatcher(it) => fmt::Debug::fmt(it, f),
262 } 275 }
263 } 276 }
264} 277}
@@ -318,6 +331,28 @@ fn loop_turn(
318 world_state.maybe_collect_garbage(); 331 world_state.maybe_collect_garbage();
319 loop_state.in_flight_libraries -= 1; 332 loop_state.in_flight_libraries -= 1;
320 } 333 }
334 Event::CheckWatcher(task) => match task {
335 CheckTask::Update(uri) => {
336 // We manually send a diagnostic update when the watcher asks
337 // us to, to avoid the issue of having to change the file to
338 // receive updated diagnostics.
339 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
340 if let Some(file_id) = world_state.vfs.read().path2file(&path) {
341 let params =
342 handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?;
343 let not = notification_new::<req::PublishDiagnostics>(params);
344 task_sender.send(Task::Notify(not)).unwrap();
345 }
346 }
347 CheckTask::Status(progress) => {
348 let params = req::ProgressParams {
349 token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()),
350 value: req::ProgressParamsValue::WorkDone(progress),
351 };
352 let not = notification_new::<req::Progress>(params);
353 task_sender.send(Task::Notify(not)).unwrap();
354 }
355 },
321 Event::Msg(msg) => match msg { 356 Event::Msg(msg) => match msg {
322 Message::Request(req) => on_request( 357 Message::Request(req) => on_request(
323 world_state, 358 world_state,
@@ -517,6 +552,13 @@ fn on_notification(
517 } 552 }
518 Err(not) => not, 553 Err(not) => not,
519 }; 554 };
555 let not = match notification_cast::<req::DidSaveTextDocument>(not) {
556 Ok(_params) => {
557 state.check_watcher.update();
558 return Ok(());
559 }
560 Err(not) => not,
561 };
520 let not = match notification_cast::<req::DidCloseTextDocument>(not) { 562 let not = match notification_cast::<req::DidCloseTextDocument>(not) {
521 Ok(params) => { 563 Ok(params) => {
522 let uri = params.text_document.uri; 564 let uri = params.text_document.uri;
@@ -667,16 +709,11 @@ where
667 Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), 709 Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message),
668 Err(e) => { 710 Err(e) => {
669 if is_canceled(&e) { 711 if is_canceled(&e) {
670 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 712 Response::new_err(
671 // gets fixed, we can return the proper response. 713 id,
672 // This works around the issue where "content modified" error would continuously 714 ErrorCode::ContentModified as i32,
673 // show an message pop-up in VsCode 715 "content modified".to_string(),
674 // Response::err( 716 )
675 // id,
676 // ErrorCode::ContentModified as i32,
677 // "content modified".to_string(),
678 // )
679 Response::new_ok(id, ())
680 } else { 717 } else {
681 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string()) 718 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string())
682 } 719 }
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 39eb3df3e..331beab13 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -654,6 +654,29 @@ pub fn handle_code_action(
654 res.push(action.into()); 654 res.push(action.into());
655 } 655 }
656 656
657 for fix in world.check_watcher.read().fixes_for(&params.text_document.uri).into_iter().flatten()
658 {
659 let fix_range = fix.location.range.conv_with(&line_index);
660 if fix_range.intersection(&range).is_none() {
661 continue;
662 }
663
664 let edits = vec![TextEdit::new(fix.location.range, fix.replacement.clone())];
665 let mut edit_map = std::collections::HashMap::new();
666 edit_map.insert(fix.location.uri.clone(), edits);
667 let edit = WorkspaceEdit::new(edit_map);
668
669 let action = CodeAction {
670 title: fix.title.clone(),
671 kind: Some("quickfix".to_string()),
672 diagnostics: Some(fix.diagnostics.clone()),
673 edit: Some(edit),
674 command: None,
675 is_preferred: None,
676 };
677 res.push(action.into());
678 }
679
657 for assist in assists { 680 for assist in assists {
658 let title = assist.change.label.clone(); 681 let title = assist.change.label.clone();
659 let edit = assist.change.try_conv_with(&world)?; 682 let edit = assist.change.try_conv_with(&world)?;
@@ -820,7 +843,7 @@ pub fn publish_diagnostics(
820 let _p = profile("publish_diagnostics"); 843 let _p = profile("publish_diagnostics");
821 let uri = world.file_id_to_uri(file_id)?; 844 let uri = world.file_id_to_uri(file_id)?;
822 let line_index = world.analysis().file_line_index(file_id)?; 845 let line_index = world.analysis().file_line_index(file_id)?;
823 let diagnostics = world 846 let mut diagnostics: Vec<Diagnostic> = world
824 .analysis() 847 .analysis()
825 .diagnostics(file_id)? 848 .diagnostics(file_id)?
826 .into_iter() 849 .into_iter()
@@ -834,6 +857,9 @@ pub fn publish_diagnostics(
834 tags: None, 857 tags: None,
835 }) 858 })
836 .collect(); 859 .collect();
860 if let Some(check_diags) = world.check_watcher.read().diagnostics_for(&uri) {
861 diagnostics.extend(check_diags.iter().cloned());
862 }
837 Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) 863 Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None })
838} 864}
839 865
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index b34e6f9b8..40edaf677 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -9,10 +9,10 @@ pub use lsp_types::{
9 CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, 9 CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams,
10 DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, 10 DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions,
11 DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, 11 DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse,
12 FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, 12 FileSystemWatcher, Hover, InitializeResult, MessageType, ProgressParams, ProgressParamsValue,
13 ReferenceParams, Registration, RegistrationParams, SelectionRange, SelectionRangeParams, 13 ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams,
14 ShowMessageParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, 14 SelectionRange, SelectionRangeParams, ShowMessageParams, SignatureHelp, TextDocumentEdit,
15 WorkspaceEdit, WorkspaceSymbolParams, 15 TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams,
16}; 16};
17 17
18pub enum AnalyzerStatus {} 18pub enum AnalyzerStatus {}
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 79431e7e6..121ddfd1f 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -12,6 +12,9 @@ use crossbeam_channel::{unbounded, Receiver};
12use lsp_server::ErrorCode; 12use lsp_server::ErrorCode;
13use lsp_types::Url; 13use lsp_types::Url;
14use parking_lot::RwLock; 14use parking_lot::RwLock;
15use ra_cargo_watch::{
16 url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher, CheckWatcherSharedState,
17};
15use ra_ide::{ 18use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, 19 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
17 SourceRootId, 20 SourceRootId,
@@ -20,13 +23,11 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 23use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
21use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 24use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
22use relative_path::RelativePathBuf; 25use relative_path::RelativePathBuf;
23use std::path::{Component, Prefix};
24 26
25use crate::{ 27use crate::{
26 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 28 main_loop::pending_requests::{CompletedRequest, LatestRequests},
27 LspError, Result, 29 LspError, Result,
28}; 30};
29use std::str::FromStr;
30 31
31#[derive(Debug, Clone)] 32#[derive(Debug, Clone)]
32pub struct Options { 33pub struct Options {
@@ -34,6 +35,7 @@ pub struct Options {
34 pub supports_location_link: bool, 35 pub supports_location_link: bool,
35 pub line_folding_only: bool, 36 pub line_folding_only: bool,
36 pub max_inlay_hint_length: Option<usize>, 37 pub max_inlay_hint_length: Option<usize>,
38 pub cargo_watch: CheckOptions,
37} 39}
38 40
39/// `WorldState` is the primary mutable state of the language server 41/// `WorldState` is the primary mutable state of the language server
@@ -52,6 +54,7 @@ pub struct WorldState {
52 pub vfs: Arc<RwLock<Vfs>>, 54 pub vfs: Arc<RwLock<Vfs>>,
53 pub task_receiver: Receiver<VfsTask>, 55 pub task_receiver: Receiver<VfsTask>,
54 pub latest_requests: Arc<RwLock<LatestRequests>>, 56 pub latest_requests: Arc<RwLock<LatestRequests>>,
57 pub check_watcher: CheckWatcher,
55} 58}
56 59
57/// An immutable snapshot of the world's state at a point in time. 60/// An immutable snapshot of the world's state at a point in time.
@@ -61,6 +64,7 @@ pub struct WorldSnapshot {
61 pub analysis: Analysis, 64 pub analysis: Analysis,
62 pub vfs: Arc<RwLock<Vfs>>, 65 pub vfs: Arc<RwLock<Vfs>>,
63 pub latest_requests: Arc<RwLock<LatestRequests>>, 66 pub latest_requests: Arc<RwLock<LatestRequests>>,
67 pub check_watcher: Arc<RwLock<CheckWatcherSharedState>>,
64} 68}
65 69
66impl WorldState { 70impl WorldState {
@@ -127,6 +131,10 @@ impl WorldState {
127 } 131 }
128 change.set_crate_graph(crate_graph); 132 change.set_crate_graph(crate_graph);
129 133
134 // FIXME: Figure out the multi-workspace situation
135 let check_watcher =
136 CheckWatcher::new(&options.cargo_watch, folder_roots.first().cloned().unwrap());
137
130 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); 138 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags);
131 analysis_host.apply_change(change); 139 analysis_host.apply_change(change);
132 WorldState { 140 WorldState {
@@ -138,6 +146,7 @@ impl WorldState {
138 vfs: Arc::new(RwLock::new(vfs)), 146 vfs: Arc::new(RwLock::new(vfs)),
139 task_receiver, 147 task_receiver,
140 latest_requests: Default::default(), 148 latest_requests: Default::default(),
149 check_watcher,
141 } 150 }
142 } 151 }
143 152
@@ -199,6 +208,7 @@ impl WorldState {
199 analysis: self.analysis_host.analysis(), 208 analysis: self.analysis_host.analysis(),
200 vfs: Arc::clone(&self.vfs), 209 vfs: Arc::clone(&self.vfs),
201 latest_requests: Arc::clone(&self.latest_requests), 210 latest_requests: Arc::clone(&self.latest_requests),
211 check_watcher: self.check_watcher.shared.clone(),
202 } 212 }
203 } 213 }
204 214
@@ -284,61 +294,3 @@ impl WorldSnapshot {
284 self.analysis.feature_flags() 294 self.analysis.feature_flags()
285 } 295 }
286} 296}
287
288/// Returns a `Url` object from a given path, will lowercase drive letters if present.
289/// This will only happen when processing windows paths.
290///
291/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
292fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
293 let component_has_windows_drive = path.as_ref().components().any(|comp| {
294 if let Component::Prefix(c) = comp {
295 match c.kind() {
296 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
297 _ => return false,
298 }
299 }
300 false
301 });
302
303 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
304 if component_has_windows_drive {
305 let url_original = Url::from_file_path(&path)
306 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
307
308 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
309
310 // There is a drive partition, but we never found a colon.
311 // This should not happen, but in this case we just pass it through.
312 if drive_partition.len() == 1 {
313 return Ok(url_original);
314 }
315
316 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
317 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
318
319 Ok(url)
320 } else {
321 Ok(Url::from_file_path(&path)
322 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
323 }
324}
325
326// `Url` is not able to parse windows paths on unix machines.
327#[cfg(target_os = "windows")]
328#[cfg(test)]
329mod path_conversion_windows_tests {
330 use super::url_from_path_with_drive_lowercasing;
331 #[test]
332 fn test_lowercase_drive_letter_with_drive() {
333 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
334
335 assert_eq!(url.to_string(), "file:///c:/Test");
336 }
337
338 #[test]
339 fn test_drive_without_colon_passthrough() {
340 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
341
342 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
343 }
344}
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 277532a8c..89cb9a9f3 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -17,7 +17,9 @@ use crate::{
17 17
18pub use self::{ 18pub use self::{
19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, 19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
20 extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, 20 extensions::{
21 FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind, VisibilityKind,
22 },
21 generated::*, 23 generated::*,
22 tokens::*, 24 tokens::*,
23 traits::*, 25 traits::*,
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index baaef3023..d9666cdca 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -413,3 +413,32 @@ impl ast::TraitDef {
413 self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) 413 self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
414 } 414 }
415} 415}
416
417pub enum VisibilityKind {
418 In(ast::Path),
419 PubCrate,
420 PubSuper,
421 Pub,
422}
423
424impl ast::Visibility {
425 pub fn kind(&self) -> VisibilityKind {
426 if let Some(path) = children(self).next() {
427 VisibilityKind::In(path)
428 } else if self.is_pub_crate() {
429 VisibilityKind::PubCrate
430 } else if self.is_pub_super() {
431 VisibilityKind::PubSuper
432 } else {
433 VisibilityKind::Pub
434 }
435 }
436
437 fn is_pub_crate(&self) -> bool {
438 self.syntax().children_with_tokens().any(|it| it.kind() == T![crate])
439 }
440
441 fn is_pub_super(&self) -> bool {
442 self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
443 }
444}
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 9f9d6e63c..e64c83d33 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -1064,6 +1064,7 @@ impl AstNode for ExternCrateItem {
1064 } 1064 }
1065} 1065}
1066impl ast::AttrsOwner for ExternCrateItem {} 1066impl ast::AttrsOwner for ExternCrateItem {}
1067impl ast::VisibilityOwner for ExternCrateItem {}
1067impl ExternCrateItem { 1068impl ExternCrateItem {
1068 pub fn name_ref(&self) -> Option<NameRef> { 1069 pub fn name_ref(&self) -> Option<NameRef> {
1069 AstChildren::new(&self.syntax).next() 1070 AstChildren::new(&self.syntax).next()
@@ -2006,6 +2007,7 @@ impl AstNode for ModuleItem {
2006 } 2007 }
2007} 2008}
2008impl ast::AttrsOwner for ModuleItem {} 2009impl ast::AttrsOwner for ModuleItem {}
2010impl ast::VisibilityOwner for ModuleItem {}
2009impl ModuleItem {} 2011impl ModuleItem {}
2010#[derive(Debug, Clone, PartialEq, Eq, Hash)] 2012#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2011pub struct Name { 2013pub struct Name {
@@ -3893,6 +3895,7 @@ impl AstNode for UseItem {
3893 } 3895 }
3894} 3896}
3895impl ast::AttrsOwner for UseItem {} 3897impl ast::AttrsOwner for UseItem {}
3898impl ast::VisibilityOwner for UseItem {}
3896impl UseItem { 3899impl UseItem {
3897 pub fn use_tree(&self) -> Option<UseTree> { 3900 pub fn use_tree(&self) -> Option<UseTree> {
3898 AstChildren::new(&self.syntax).next() 3901 AstChildren::new(&self.syntax).next()
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 08aafb610..e43a724f0 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -412,7 +412,7 @@ Grammar(
412 "ModuleItem": ( 412 "ModuleItem": (
413 enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", 413 enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock",
414 "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], 414 "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ],
415 traits: ["AttrsOwner"], 415 traits: ["AttrsOwner", "VisibilityOwner"],
416 ), 416 ),
417 "ImplItem": ( 417 "ImplItem": (
418 enum: ["FnDef", "TypeAliasDef", "ConstDef"], 418 enum: ["FnDef", "TypeAliasDef", "ConstDef"],
@@ -683,7 +683,7 @@ Grammar(
683 ] 683 ]
684 ), 684 ),
685 "UseItem": ( 685 "UseItem": (
686 traits: ["AttrsOwner"], 686 traits: ["AttrsOwner", "VisibilityOwner"],
687 options: [ "UseTree" ], 687 options: [ "UseTree" ],
688 ), 688 ),
689 "UseTree": ( 689 "UseTree": (
@@ -696,7 +696,7 @@ Grammar(
696 collections: [("use_trees", "UseTree")] 696 collections: [("use_trees", "UseTree")]
697 ), 697 ),
698 "ExternCrateItem": ( 698 "ExternCrateItem": (
699 traits: ["AttrsOwner"], 699 traits: ["AttrsOwner", "VisibilityOwner"],
700 options: ["NameRef", "Alias"], 700 options: ["NameRef", "Alias"],
701 ), 701 ),
702 "ArgList": ( 702 "ArgList": (
diff --git a/docs/user/README.md b/docs/user/README.md
index 9cdabfd42..a4b081dbb 100644
--- a/docs/user/README.md
+++ b/docs/user/README.md
@@ -87,8 +87,10 @@ host.
87 87
88### Settings 88### Settings
89 89
90* `rust-analyzer.highlightingOn`: enables experimental syntax highlighting 90* `rust-analyzer.highlightingOn`: enables experimental syntax highlighting.
91* `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts 91 Colors can be configured via `editor.tokenColorCustomizations`.
92 As an example, [Pale Fire](https://github.com/matklad/pale-fire/) color scheme tweaks rust colors.
93* `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts.
92 `Enter` key to make it easier to continue comments. Note that it may conflict with VIM emulation plugin. 94 `Enter` key to make it easier to continue comments. Note that it may conflict with VIM emulation plugin.
93* `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable 95* `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable
94* `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo 96* `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo
@@ -106,7 +108,7 @@ host.
106* `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging 108* `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging
107* `RUST_SRC_PATH`: environment variable that overwrites the sysroot 109* `RUST_SRC_PATH`: environment variable that overwrites the sysroot
108* `rust-analyzer.featureFlags` -- a JSON object to tweak fine-grained behavior: 110* `rust-analyzer.featureFlags` -- a JSON object to tweak fine-grained behavior:
109 ```js 111 ```jsonc
110 { 112 {
111 // Show diagnostics produced by rust-analyzer itself. 113 // Show diagnostics produced by rust-analyzer itself.
112 "lsp.diagnostics": true, 114 "lsp.diagnostics": true,
diff --git a/editors/code/.prettierignore b/editors/code/.prettierignore
deleted file mode 100644
index 3798f2d1f..000000000
--- a/editors/code/.prettierignore
+++ /dev/null
@@ -1,2 +0,0 @@
1node_modules/
2.vscode-test/
diff --git a/editors/code/.vscode/launch.json b/editors/code/.vscode/launch.json
deleted file mode 100644
index c3578f476..000000000
--- a/editors/code/.vscode/launch.json
+++ /dev/null
@@ -1,31 +0,0 @@
1// A launch configuration that compiles the extension and then opens it inside a new window
2// Use IntelliSense to learn about possible attributes.
3// Hover to view descriptions of existing attributes.
4// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5{
6 "version": "0.2.0",
7 "configurations": [
8 {
9 "name": "Extension",
10 "type": "extensionHost",
11 "request": "launch",
12 "runtimeExecutable": "${execPath}",
13 "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
14 "outFiles": ["${workspaceFolder}/out/**/*.js"],
15 "preLaunchTask": "npm: watch"
16 },
17 {
18 "name": "Extension Tests",
19 "type": "extensionHost",
20 "request": "launch",
21 "runtimeExecutable": "${execPath}",
22 "args": [
23 "${workspaceFolder}/src/test/",
24 "--extensionDevelopmentPath=${workspaceFolder}",
25 "--extensionTestsPath=${workspaceFolder}/out/test"
26 ],
27 "outFiles": ["${workspaceFolder}/out/test/**/*.js"],
28 "preLaunchTask": "npm: watch"
29 }
30 ]
31}
diff --git a/editors/code/.vscode/settings.json b/editors/code/.vscode/settings.json
deleted file mode 100644
index fa0a10487..000000000
--- a/editors/code/.vscode/settings.json
+++ /dev/null
@@ -1,11 +0,0 @@
1// Place your settings in this file to overwrite default and user settings.
2{
3 "files.exclude": {
4 "out": false // set this to true to hide the "out" folder with the compiled JS files
5 },
6 "search.exclude": {
7 "out": true // set this to false to include "out" folder in search results
8 },
9 // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
10 "typescript.tsc.autoDetect": "off"
11}
diff --git a/editors/code/.vscode/tasks.json b/editors/code/.vscode/tasks.json
deleted file mode 100644
index 5deb2bccd..000000000
--- a/editors/code/.vscode/tasks.json
+++ /dev/null
@@ -1,20 +0,0 @@
1// See https://go.microsoft.com/fwlink/?LinkId=733558
2// for the documentation about the tasks.json format
3{
4 "version": "2.0.0",
5 "tasks": [
6 {
7 "type": "npm",
8 "script": "watch",
9 "problemMatcher": "$tsc-watch",
10 "isBackground": true,
11 "presentation": {
12 "reveal": "never"
13 },
14 "group": {
15 "kind": "build",
16 "isDefault": true
17 }
18 }
19 ]
20}
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index f9e72b668..9bcd28e61 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -1,9 +1,4 @@
1.vscode/** 1**
2.vscode-test/** 2!out/main.js
3out/** 3!package.json
4src/** 4!package-lock.json
5.gitignore
6tsconfig.json
7vsc-extension-quickstart.md
8tslint.json
9node_modules/**
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 4c5c13646..adb01760a 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -24,45 +24,61 @@
24 "js-tokens": "^4.0.0" 24 "js-tokens": "^4.0.0"
25 } 25 }
26 }, 26 },
27 "@types/estree": { 27 "@rollup/plugin-commonjs": {
28 "version": "0.0.39", 28 "version": "11.0.0",
29 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 29 "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.0.0.tgz",
30 "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 30 "integrity": "sha512-jnm//T5ZWOZ6zmJ61fReSCBOif+Ax8dHVoVggA+d2NA7T4qCWgQ3KYr+zN2faGEYLpe1wa03IzvhR+sqVLxUWg==",
31 "dev": true 31 "dev": true,
32 "requires": {
33 "@rollup/pluginutils": "^3.0.0",
34 "estree-walker": "^0.6.1",
35 "is-reference": "^1.1.2",
36 "magic-string": "^0.25.2",
37 "resolve": "^1.11.0"
38 }
32 }, 39 },
33 "@types/events": { 40 "@rollup/plugin-node-resolve": {
34 "version": "3.0.0", 41 "version": "6.0.0",
35 "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", 42 "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-6.0.0.tgz",
36 "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", 43 "integrity": "sha512-GqWz1CfXOsqpeVMcoM315+O7zMxpRsmhWyhJoxLFHVSp9S64/u02i7len/FnbTNbmgYs+sZyilasijH8UiuboQ==",
37 "dev": true 44 "dev": true,
45 "requires": {
46 "@rollup/pluginutils": "^3.0.0",
47 "@types/resolve": "0.0.8",
48 "builtin-modules": "^3.1.0",
49 "is-module": "^1.0.0",
50 "resolve": "^1.11.1"
51 }
38 }, 52 },
39 "@types/glob": { 53 "@rollup/plugin-typescript": {
40 "version": "7.1.1", 54 "version": "2.0.1",
41 "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", 55 "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-2.0.1.tgz",
42 "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", 56 "integrity": "sha512-UA/bN/DlHN19xdOllXmp7G7pM2ac9dQMg0q2T1rg4Bogzb7oHXj2WGafpiNpEm54PivcJdzGRJvRnI6zCISW3w==",
43 "dev": true, 57 "dev": true,
44 "requires": { 58 "requires": {
45 "@types/events": "*", 59 "@rollup/pluginutils": "^3.0.0",
46 "@types/minimatch": "*", 60 "resolve": "^1.12.2"
47 "@types/node": "*"
48 } 61 }
49 }, 62 },
50 "@types/minimatch": { 63 "@rollup/pluginutils": {
51 "version": "3.0.3", 64 "version": "3.0.1",
52 "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 65 "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.1.tgz",
53 "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", 66 "integrity": "sha512-PmNurkecagFimv7ZdKCVOfQuqKDPkrcpLFxRBcQ00LYr4HAjJwhCFxBiY2Xoletll2htTIiXBg6g0Yg21h2M3w==",
54 "dev": true 67 "dev": true,
68 "requires": {
69 "estree-walker": "^0.6.1"
70 }
55 }, 71 },
56 "@types/mocha": { 72 "@types/estree": {
57 "version": "5.2.7", 73 "version": "0.0.41",
58 "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", 74 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.41.tgz",
59 "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", 75 "integrity": "sha512-rIAmXyJlqw4KEBO7+u9gxZZSQHaCNnIzYrnNmYVpgfJhxTqO0brCX0SYpqUTkVI5mwwUwzmtspLBGBKroMeynA==",
60 "dev": true 76 "dev": true
61 }, 77 },
62 "@types/node": { 78 "@types/node": {
63 "version": "12.12.21", 79 "version": "12.12.22",
64 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.21.tgz", 80 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.22.tgz",
65 "integrity": "sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==", 81 "integrity": "sha512-r5i93jqbPWGXYXxianGATOxTelkp6ih/U0WVnvaqAvTqM+0U6J3kw6Xk6uq/dWNRkEVw/0SLcO5ORXbVNz4FMQ==",
66 "dev": true 82 "dev": true
67 }, 83 },
68 "@types/resolve": { 84 "@types/resolve": {
@@ -92,27 +108,6 @@
92 "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", 108 "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
93 "dev": true 109 "dev": true
94 }, 110 },
95 "agent-base": {
96 "version": "4.3.0",
97 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
98 "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
99 "dev": true,
100 "requires": {
101 "es6-promisify": "^5.0.0"
102 }
103 },
104 "ansi-colors": {
105 "version": "3.2.3",
106 "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
107 "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
108 "dev": true
109 },
110 "ansi-regex": {
111 "version": "3.0.0",
112 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
113 "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
114 "dev": true
115 },
116 "ansi-styles": { 111 "ansi-styles": {
117 "version": "3.2.1", 112 "version": "3.2.1",
118 "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 113 "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -131,12 +126,6 @@
131 "sprintf-js": "~1.0.2" 126 "sprintf-js": "~1.0.2"
132 } 127 }
133 }, 128 },
134 "atob": {
135 "version": "2.1.2",
136 "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
137 "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
138 "dev": true
139 },
140 "azure-devops-node-api": { 129 "azure-devops-node-api": {
141 "version": "7.2.0", 130 "version": "7.2.0",
142 "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-7.2.0.tgz", 131 "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-7.2.0.tgz",
@@ -171,12 +160,6 @@
171 "concat-map": "0.0.1" 160 "concat-map": "0.0.1"
172 } 161 }
173 }, 162 },
174 "browser-stdout": {
175 "version": "1.3.1",
176 "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
177 "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
178 "dev": true
179 },
180 "buffer-crc32": { 163 "buffer-crc32": {
181 "version": "0.2.13", 164 "version": "0.2.13",
182 "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 165 "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -184,15 +167,9 @@
184 "dev": true 167 "dev": true
185 }, 168 },
186 "builtin-modules": { 169 "builtin-modules": {
187 "version": "1.1.1", 170 "version": "3.1.0",
188 "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 171 "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
189 "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 172 "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
190 "dev": true
191 },
192 "camelcase": {
193 "version": "5.3.1",
194 "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
195 "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
196 "dev": true 173 "dev": true
197 }, 174 },
198 "chalk": { 175 "chalk": {
@@ -204,17 +181,6 @@
204 "ansi-styles": "^3.2.1", 181 "ansi-styles": "^3.2.1",
205 "escape-string-regexp": "^1.0.5", 182 "escape-string-regexp": "^1.0.5",
206 "supports-color": "^5.3.0" 183 "supports-color": "^5.3.0"
207 },
208 "dependencies": {
209 "supports-color": {
210 "version": "5.5.0",
211 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
212 "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
213 "dev": true,
214 "requires": {
215 "has-flag": "^3.0.0"
216 }
217 }
218 } 184 }
219 }, 185 },
220 "cheerio": { 186 "cheerio": {
@@ -231,45 +197,6 @@
231 "parse5": "^3.0.1" 197 "parse5": "^3.0.1"
232 } 198 }
233 }, 199 },
234 "cliui": {
235 "version": "5.0.0",
236 "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
237 "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
238 "dev": true,
239 "requires": {
240 "string-width": "^3.1.0",
241 "strip-ansi": "^5.2.0",
242 "wrap-ansi": "^5.1.0"
243 },
244 "dependencies": {
245 "ansi-regex": {
246 "version": "4.1.0",
247 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
248 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
249 "dev": true
250 },
251 "string-width": {
252 "version": "3.1.0",
253 "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
254 "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
255 "dev": true,
256 "requires": {
257 "emoji-regex": "^7.0.1",
258 "is-fullwidth-code-point": "^2.0.0",
259 "strip-ansi": "^5.1.0"
260 }
261 },
262 "strip-ansi": {
263 "version": "5.2.0",
264 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
265 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
266 "dev": true,
267 "requires": {
268 "ansi-regex": "^4.1.0"
269 }
270 }
271 }
272 },
273 "color-convert": { 200 "color-convert": {
274 "version": "1.9.3", 201 "version": "1.9.3",
275 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 202 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -291,6 +218,12 @@
291 "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 218 "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
292 "dev": true 219 "dev": true
293 }, 220 },
221 "commandpost": {
222 "version": "1.4.0",
223 "resolved": "https://registry.npmjs.org/commandpost/-/commandpost-1.4.0.tgz",
224 "integrity": "sha512-aE2Y4MTFJ870NuB/+2z1cXBhSBBzRydVVjzhFC4gtenEhpnj15yu0qptWGJsO9YGrcPZ3ezX8AWb1VA391MKpQ==",
225 "dev": true
226 },
294 "concat-map": { 227 "concat-map": {
295 "version": "0.0.1", 228 "version": "0.0.1",
296 "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 229 "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -315,36 +248,6 @@
315 "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", 248 "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
316 "dev": true 249 "dev": true
317 }, 250 },
318 "debug": {
319 "version": "3.2.6",
320 "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
321 "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
322 "dev": true,
323 "requires": {
324 "ms": "^2.1.1"
325 }
326 },
327 "decamelize": {
328 "version": "1.2.0",
329 "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
330 "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
331 "dev": true
332 },
333 "decode-uri-component": {
334 "version": "0.2.0",
335 "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
336 "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
337 "dev": true
338 },
339 "define-properties": {
340 "version": "1.1.3",
341 "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
342 "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
343 "dev": true,
344 "requires": {
345 "object-keys": "^1.0.12"
346 }
347 },
348 "denodeify": { 251 "denodeify": {
349 "version": "1.2.1", 252 "version": "1.2.1",
350 "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz", 253 "resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
@@ -358,9 +261,9 @@
358 "dev": true 261 "dev": true
359 }, 262 },
360 "diff": { 263 "diff": {
361 "version": "3.5.0", 264 "version": "4.0.1",
362 "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 265 "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
363 "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 266 "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
364 "dev": true 267 "dev": true
365 }, 268 },
366 "dom-serializer": { 269 "dom-serializer": {
@@ -398,11 +301,25 @@
398 "domelementtype": "1" 301 "domelementtype": "1"
399 } 302 }
400 }, 303 },
401 "emoji-regex": { 304 "editorconfig": {
402 "version": "7.0.3", 305 "version": "0.15.3",
403 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 306 "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
404 "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 307 "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
405 "dev": true 308 "dev": true,
309 "requires": {
310 "commander": "^2.19.0",
311 "lru-cache": "^4.1.5",
312 "semver": "^5.6.0",
313 "sigmund": "^1.0.1"
314 },
315 "dependencies": {
316 "semver": {
317 "version": "5.7.1",
318 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
319 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
320 "dev": true
321 }
322 }
406 }, 323 },
407 "entities": { 324 "entities": {
408 "version": "1.1.2", 325 "version": "1.1.2",
@@ -410,72 +327,12 @@
410 "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", 327 "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
411 "dev": true 328 "dev": true
412 }, 329 },
413 "es-abstract": {
414 "version": "1.16.3",
415 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz",
416 "integrity": "sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==",
417 "dev": true,
418 "requires": {
419 "es-to-primitive": "^1.2.1",
420 "function-bind": "^1.1.1",
421 "has": "^1.0.3",
422 "has-symbols": "^1.0.1",
423 "is-callable": "^1.1.4",
424 "is-regex": "^1.0.4",
425 "object-inspect": "^1.7.0",
426 "object-keys": "^1.1.1",
427 "string.prototype.trimleft": "^2.1.0",
428 "string.prototype.trimright": "^2.1.0"
429 }
430 },
431 "es-to-primitive": {
432 "version": "1.2.1",
433 "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
434 "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
435 "dev": true,
436 "requires": {
437 "is-callable": "^1.1.4",
438 "is-date-object": "^1.0.1",
439 "is-symbol": "^1.0.2"
440 }
441 },
442 "es6-object-assign": {
443 "version": "1.1.0",
444 "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
445 "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=",
446 "dev": true
447 },
448 "es6-promise": {
449 "version": "4.2.8",
450 "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
451 "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
452 "dev": true
453 },
454 "es6-promisify": {
455 "version": "5.0.0",
456 "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
457 "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
458 "dev": true,
459 "requires": {
460 "es6-promise": "^4.0.3"
461 }
462 },
463 "escape-string-regexp": { 330 "escape-string-regexp": {
464 "version": "1.0.5", 331 "version": "1.0.5",
465 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 332 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
466 "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 333 "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
467 "dev": true 334 "dev": true
468 }, 335 },
469 "eslint-plugin-prettier": {
470 "version": "2.7.0",
471 "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz",
472 "integrity": "sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==",
473 "dev": true,
474 "requires": {
475 "fast-diff": "^1.1.1",
476 "jest-docblock": "^21.0.0"
477 }
478 },
479 "esprima": { 336 "esprima": {
480 "version": "4.0.1", 337 "version": "4.0.1",
481 "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 338 "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -494,12 +351,6 @@
494 "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 351 "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
495 "dev": true 352 "dev": true
496 }, 353 },
497 "fast-diff": {
498 "version": "1.2.0",
499 "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
500 "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
501 "dev": true
502 },
503 "fd-slicer": { 354 "fd-slicer": {
504 "version": "1.1.0", 355 "version": "1.1.0",
505 "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 356 "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@@ -509,42 +360,12 @@
509 "pend": "~1.2.0" 360 "pend": "~1.2.0"
510 } 361 }
511 }, 362 },
512 "find-up": {
513 "version": "3.0.0",
514 "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
515 "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
516 "dev": true,
517 "requires": {
518 "locate-path": "^3.0.0"
519 }
520 },
521 "flat": {
522 "version": "4.1.0",
523 "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
524 "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
525 "dev": true,
526 "requires": {
527 "is-buffer": "~2.0.3"
528 }
529 },
530 "fs.realpath": { 363 "fs.realpath": {
531 "version": "1.0.0", 364 "version": "1.0.0",
532 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 365 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
533 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 366 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
534 "dev": true 367 "dev": true
535 }, 368 },
536 "function-bind": {
537 "version": "1.1.1",
538 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
539 "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
540 "dev": true
541 },
542 "get-caller-file": {
543 "version": "2.0.5",
544 "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
545 "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
546 "dev": true
547 },
548 "glob": { 369 "glob": {
549 "version": "7.1.6", 370 "version": "7.1.6",
550 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 371 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -559,39 +380,12 @@
559 "path-is-absolute": "^1.0.0" 380 "path-is-absolute": "^1.0.0"
560 } 381 }
561 }, 382 },
562 "growl": {
563 "version": "1.10.5",
564 "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
565 "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
566 "dev": true
567 },
568 "has": {
569 "version": "1.0.3",
570 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
571 "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
572 "dev": true,
573 "requires": {
574 "function-bind": "^1.1.1"
575 }
576 },
577 "has-flag": { 383 "has-flag": {
578 "version": "3.0.0", 384 "version": "3.0.0",
579 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 385 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
580 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 386 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
581 "dev": true 387 "dev": true
582 }, 388 },
583 "has-symbols": {
584 "version": "1.0.1",
585 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
586 "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
587 "dev": true
588 },
589 "he": {
590 "version": "1.2.0",
591 "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
592 "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
593 "dev": true
594 },
595 "htmlparser2": { 389 "htmlparser2": {
596 "version": "3.10.1", 390 "version": "3.10.1",
597 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 391 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -606,43 +400,6 @@
606 "readable-stream": "^3.1.1" 400 "readable-stream": "^3.1.1"
607 } 401 }
608 }, 402 },
609 "http-proxy-agent": {
610 "version": "2.1.0",
611 "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
612 "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
613 "dev": true,
614 "requires": {
615 "agent-base": "4",
616 "debug": "3.1.0"
617 },
618 "dependencies": {
619 "debug": {
620 "version": "3.1.0",
621 "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
622 "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
623 "dev": true,
624 "requires": {
625 "ms": "2.0.0"
626 }
627 },
628 "ms": {
629 "version": "2.0.0",
630 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
631 "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
632 "dev": true
633 }
634 }
635 },
636 "https-proxy-agent": {
637 "version": "2.2.4",
638 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
639 "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
640 "dev": true,
641 "requires": {
642 "agent-base": "^4.3.0",
643 "debug": "^3.1.0"
644 }
645 },
646 "inflight": { 403 "inflight": {
647 "version": "1.0.6", 404 "version": "1.0.6",
648 "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 405 "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -659,36 +416,6 @@
659 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 416 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
660 "dev": true 417 "dev": true
661 }, 418 },
662 "interpret": {
663 "version": "1.2.0",
664 "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
665 "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
666 "dev": true
667 },
668 "is-buffer": {
669 "version": "2.0.4",
670 "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
671 "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
672 "dev": true
673 },
674 "is-callable": {
675 "version": "1.1.4",
676 "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
677 "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
678 "dev": true
679 },
680 "is-date-object": {
681 "version": "1.0.1",
682 "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
683 "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
684 "dev": true
685 },
686 "is-fullwidth-code-point": {
687 "version": "2.0.0",
688 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
689 "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
690 "dev": true
691 },
692 "is-module": { 419 "is-module": {
693 "version": "1.0.0", 420 "version": "1.0.0",
694 "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 421 "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
@@ -696,44 +423,22 @@
696 "dev": true 423 "dev": true
697 }, 424 },
698 "is-reference": { 425 "is-reference": {
699 "version": "1.1.3", 426 "version": "1.1.4",
700 "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.3.tgz", 427 "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz",
701 "integrity": "sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw==", 428 "integrity": "sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw==",
702 "dev": true, 429 "dev": true,
703 "requires": { 430 "requires": {
704 "@types/estree": "0.0.39" 431 "@types/estree": "0.0.39"
432 },
433 "dependencies": {
434 "@types/estree": {
435 "version": "0.0.39",
436 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
437 "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
438 "dev": true
439 }
705 } 440 }
706 }, 441 },
707 "is-regex": {
708 "version": "1.0.4",
709 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
710 "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
711 "dev": true,
712 "requires": {
713 "has": "^1.0.1"
714 }
715 },
716 "is-symbol": {
717 "version": "1.0.3",
718 "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
719 "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
720 "dev": true,
721 "requires": {
722 "has-symbols": "^1.0.1"
723 }
724 },
725 "isexe": {
726 "version": "2.0.0",
727 "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
728 "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
729 "dev": true
730 },
731 "jest-docblock": {
732 "version": "21.2.0",
733 "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz",
734 "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==",
735 "dev": true
736 },
737 "js-tokens": { 442 "js-tokens": {
738 "version": "4.0.0", 443 "version": "4.0.0",
739 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 444 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -750,11 +455,10 @@
750 "esprima": "^4.0.0" 455 "esprima": "^4.0.0"
751 } 456 }
752 }, 457 },
753 "lines-and-columns": { 458 "jsonc-parser": {
754 "version": "1.1.6", 459 "version": "2.2.0",
755 "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", 460 "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.0.tgz",
756 "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", 461 "integrity": "sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA=="
757 "dev": true
758 }, 462 },
759 "linkify-it": { 463 "linkify-it": {
760 "version": "2.2.0", 464 "version": "2.2.0",
@@ -765,40 +469,26 @@
765 "uc.micro": "^1.0.1" 469 "uc.micro": "^1.0.1"
766 } 470 }
767 }, 471 },
768 "locate-path": {
769 "version": "3.0.0",
770 "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
771 "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
772 "dev": true,
773 "requires": {
774 "p-locate": "^3.0.0",
775 "path-exists": "^3.0.0"
776 }
777 },
778 "lodash": { 472 "lodash": {
779 "version": "4.17.15", 473 "version": "4.17.15",
780 "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 474 "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
781 "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 475 "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
782 "dev": true 476 "dev": true
783 }, 477 },
784 "log-symbols": { 478 "lru-cache": {
785 "version": "2.2.0", 479 "version": "4.1.5",
786 "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 480 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
787 "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 481 "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
788 "dev": true, 482 "dev": true,
789 "requires": { 483 "requires": {
790 "chalk": "^2.0.1" 484 "pseudomap": "^1.0.2",
485 "yallist": "^2.1.2"
791 } 486 }
792 }, 487 },
793 "lookpath": {
794 "version": "1.0.4",
795 "resolved": "https://registry.npmjs.org/lookpath/-/lookpath-1.0.4.tgz",
796 "integrity": "sha512-xVFrWlfo7n8VZs1YjBWKkbSIJU7DKE/0Mep62KeT94V1Ui1IY9w5fXfgiCsDIDZkakIYSXSeaW2FLbCqfw9/Cw=="
797 },
798 "magic-string": { 488 "magic-string": {
799 "version": "0.25.3", 489 "version": "0.25.4",
800 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", 490 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz",
801 "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", 491 "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==",
802 "dev": true, 492 "dev": true,
803 "requires": { 493 "requires": {
804 "sourcemap-codec": "^1.4.4" 494 "sourcemap-codec": "^1.4.4"
@@ -853,75 +543,12 @@
853 "minimist": "0.0.8" 543 "minimist": "0.0.8"
854 } 544 }
855 }, 545 },
856 "mocha": {
857 "version": "6.2.2",
858 "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.2.tgz",
859 "integrity": "sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==",
860 "dev": true,
861 "requires": {
862 "ansi-colors": "3.2.3",
863 "browser-stdout": "1.3.1",
864 "debug": "3.2.6",
865 "diff": "3.5.0",
866 "escape-string-regexp": "1.0.5",
867 "find-up": "3.0.0",
868 "glob": "7.1.3",
869 "growl": "1.10.5",
870 "he": "1.2.0",
871 "js-yaml": "3.13.1",
872 "log-symbols": "2.2.0",
873 "minimatch": "3.0.4",
874 "mkdirp": "0.5.1",
875 "ms": "2.1.1",
876 "node-environment-flags": "1.0.5",
877 "object.assign": "4.1.0",
878 "strip-json-comments": "2.0.1",
879 "supports-color": "6.0.0",
880 "which": "1.3.1",
881 "wide-align": "1.1.3",
882 "yargs": "13.3.0",
883 "yargs-parser": "13.1.1",
884 "yargs-unparser": "1.6.0"
885 },
886 "dependencies": {
887 "glob": {
888 "version": "7.1.3",
889 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
890 "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
891 "dev": true,
892 "requires": {
893 "fs.realpath": "^1.0.0",
894 "inflight": "^1.0.4",
895 "inherits": "2",
896 "minimatch": "^3.0.4",
897 "once": "^1.3.0",
898 "path-is-absolute": "^1.0.0"
899 }
900 }
901 }
902 },
903 "ms": {
904 "version": "2.1.1",
905 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
906 "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
907 "dev": true
908 },
909 "mute-stream": { 546 "mute-stream": {
910 "version": "0.0.8", 547 "version": "0.0.8",
911 "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", 548 "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
912 "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", 549 "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
913 "dev": true 550 "dev": true
914 }, 551 },
915 "node-environment-flags": {
916 "version": "1.0.5",
917 "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
918 "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
919 "dev": true,
920 "requires": {
921 "object.getownpropertydescriptors": "^2.0.3",
922 "semver": "^5.7.0"
923 }
924 },
925 "nth-check": { 552 "nth-check": {
926 "version": "1.0.2", 553 "version": "1.0.2",
927 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 554 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
@@ -931,40 +558,6 @@
931 "boolbase": "~1.0.0" 558 "boolbase": "~1.0.0"
932 } 559 }
933 }, 560 },
934 "object-inspect": {
935 "version": "1.7.0",
936 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
937 "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
938 "dev": true
939 },
940 "object-keys": {
941 "version": "1.1.1",
942 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
943 "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
944 "dev": true
945 },
946 "object.assign": {
947 "version": "4.1.0",
948 "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
949 "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
950 "dev": true,
951 "requires": {
952 "define-properties": "^1.1.2",
953 "function-bind": "^1.1.1",
954 "has-symbols": "^1.0.0",
955 "object-keys": "^1.0.11"
956 }
957 },
958 "object.getownpropertydescriptors": {
959 "version": "2.0.3",
960 "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
961 "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
962 "dev": true,
963 "requires": {
964 "define-properties": "^1.1.2",
965 "es-abstract": "^1.5.1"
966 }
967 },
968 "once": { 561 "once": {
969 "version": "1.4.0", 562 "version": "1.4.0",
970 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 563 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1002,30 +595,6 @@
1002 "os-tmpdir": "^1.0.0" 595 "os-tmpdir": "^1.0.0"
1003 } 596 }
1004 }, 597 },
1005 "p-limit": {
1006 "version": "2.2.1",
1007 "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
1008 "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
1009 "dev": true,
1010 "requires": {
1011 "p-try": "^2.0.0"
1012 }
1013 },
1014 "p-locate": {
1015 "version": "3.0.0",
1016 "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
1017 "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
1018 "dev": true,
1019 "requires": {
1020 "p-limit": "^2.0.0"
1021 }
1022 },
1023 "p-try": {
1024 "version": "2.2.0",
1025 "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
1026 "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
1027 "dev": true
1028 },
1029 "parse-semver": { 598 "parse-semver": {
1030 "version": "1.1.1", 599 "version": "1.1.1",
1031 "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", 600 "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz",
@@ -1033,6 +602,14 @@
1033 "dev": true, 602 "dev": true,
1034 "requires": { 603 "requires": {
1035 "semver": "^5.1.0" 604 "semver": "^5.1.0"
605 },
606 "dependencies": {
607 "semver": {
608 "version": "5.7.1",
609 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
610 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
611 "dev": true
612 }
1036 } 613 }
1037 }, 614 },
1038 "parse5": { 615 "parse5": {
@@ -1044,12 +621,6 @@
1044 "@types/node": "*" 621 "@types/node": "*"
1045 } 622 }
1046 }, 623 },
1047 "path-exists": {
1048 "version": "3.0.0",
1049 "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
1050 "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
1051 "dev": true
1052 },
1053 "path-is-absolute": { 624 "path-is-absolute": {
1054 "version": "1.0.1", 625 "version": "1.0.1",
1055 "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 626 "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -1068,10 +639,10 @@
1068 "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", 639 "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
1069 "dev": true 640 "dev": true
1070 }, 641 },
1071 "prettier": { 642 "pseudomap": {
1072 "version": "1.19.1", 643 "version": "1.0.2",
1073 "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", 644 "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
1074 "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", 645 "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
1075 "dev": true 646 "dev": true
1076 }, 647 },
1077 "read": { 648 "read": {
@@ -1094,55 +665,19 @@
1094 "util-deprecate": "^1.0.1" 665 "util-deprecate": "^1.0.1"
1095 } 666 }
1096 }, 667 },
1097 "rechoir": {
1098 "version": "0.6.2",
1099 "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
1100 "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
1101 "dev": true,
1102 "requires": {
1103 "resolve": "^1.1.6"
1104 }
1105 },
1106 "require-directory": {
1107 "version": "2.1.1",
1108 "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
1109 "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
1110 "dev": true
1111 },
1112 "require-main-filename": {
1113 "version": "2.0.0",
1114 "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
1115 "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
1116 "dev": true
1117 },
1118 "resolve": { 668 "resolve": {
1119 "version": "1.9.0", 669 "version": "1.14.1",
1120 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", 670 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.1.tgz",
1121 "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", 671 "integrity": "sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg==",
1122 "dev": true, 672 "dev": true,
1123 "requires": { 673 "requires": {
1124 "path-parse": "^1.0.6" 674 "path-parse": "^1.0.6"
1125 } 675 }
1126 }, 676 },
1127 "resolve-url": {
1128 "version": "0.2.1",
1129 "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
1130 "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
1131 "dev": true
1132 },
1133 "rimraf": {
1134 "version": "2.7.1",
1135 "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
1136 "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
1137 "dev": true,
1138 "requires": {
1139 "glob": "^7.1.3"
1140 }
1141 },
1142 "rollup": { 677 "rollup": {
1143 "version": "1.27.13", 678 "version": "1.27.14",
1144 "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.27.13.tgz", 679 "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.27.14.tgz",
1145 "integrity": "sha512-hDi7M07MpmNSDE8YVwGVFA8L7n8jTLJ4lG65nMAijAyqBe//rtu4JdxjUBE7JqXfdpqxqDTbCDys9WcqdpsQvw==", 680 "integrity": "sha512-DuDjEyn8Y79ALYXMt+nH/EI58L5pEw5HU9K38xXdRnxQhvzUTI/nxAawhkAHUQeudANQ//8iyrhVRHJBuR6DSQ==",
1146 "dev": true, 681 "dev": true,
1147 "requires": { 682 "requires": {
1148 "@types/estree": "*", 683 "@types/estree": "*",
@@ -1150,100 +685,6 @@
1150 "acorn": "^7.1.0" 685 "acorn": "^7.1.0"
1151 } 686 }
1152 }, 687 },
1153 "rollup-plugin-commonjs": {
1154 "version": "10.1.0",
1155 "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz",
1156 "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==",
1157 "dev": true,
1158 "requires": {
1159 "estree-walker": "^0.6.1",
1160 "is-reference": "^1.1.2",
1161 "magic-string": "^0.25.2",
1162 "resolve": "^1.11.0",
1163 "rollup-pluginutils": "^2.8.1"
1164 },
1165 "dependencies": {
1166 "resolve": {
1167 "version": "1.12.0",
1168 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
1169 "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
1170 "dev": true,
1171 "requires": {
1172 "path-parse": "^1.0.6"
1173 }
1174 }
1175 }
1176 },
1177 "rollup-plugin-node-resolve": {
1178 "version": "5.2.0",
1179 "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz",
1180 "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==",
1181 "dev": true,
1182 "requires": {
1183 "@types/resolve": "0.0.8",
1184 "builtin-modules": "^3.1.0",
1185 "is-module": "^1.0.0",
1186 "resolve": "^1.11.1",
1187 "rollup-pluginutils": "^2.8.1"
1188 },
1189 "dependencies": {
1190 "builtin-modules": {
1191 "version": "3.1.0",
1192 "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
1193 "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
1194 "dev": true
1195 },
1196 "resolve": {
1197 "version": "1.12.0",
1198 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
1199 "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
1200 "dev": true,
1201 "requires": {
1202 "path-parse": "^1.0.6"
1203 }
1204 }
1205 }
1206 },
1207 "rollup-plugin-sourcemaps": {
1208 "version": "0.4.2",
1209 "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz",
1210 "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=",
1211 "dev": true,
1212 "requires": {
1213 "rollup-pluginutils": "^2.0.1",
1214 "source-map-resolve": "^0.5.0"
1215 }
1216 },
1217 "rollup-plugin-typescript": {
1218 "version": "1.0.1",
1219 "resolved": "https://registry.npmjs.org/rollup-plugin-typescript/-/rollup-plugin-typescript-1.0.1.tgz",
1220 "integrity": "sha512-rwJDNn9jv/NsKZuyBb/h0jsclP4CJ58qbvZt2Q9zDIGILF2LtdtvCqMOL+Gq9IVq5MTrTlHZNrn8h7VjQgd8tw==",
1221 "dev": true,
1222 "requires": {
1223 "resolve": "^1.10.0",
1224 "rollup-pluginutils": "^2.5.0"
1225 },
1226 "dependencies": {
1227 "resolve": {
1228 "version": "1.12.0",
1229 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
1230 "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
1231 "dev": true,
1232 "requires": {
1233 "path-parse": "^1.0.6"
1234 }
1235 }
1236 }
1237 },
1238 "rollup-pluginutils": {
1239 "version": "2.8.2",
1240 "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
1241 "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
1242 "dev": true,
1243 "requires": {
1244 "estree-walker": "^0.6.1"
1245 }
1246 },
1247 "safe-buffer": { 688 "safe-buffer": {
1248 "version": "5.2.0", 689 "version": "5.2.0",
1249 "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 690 "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
@@ -1256,64 +697,14 @@
1256 "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" 697 "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
1257 }, 698 },
1258 "semver": { 699 "semver": {
1259 "version": "5.7.1", 700 "version": "6.3.0",
1260 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 701 "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
1261 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 702 "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
1262 "dev": true
1263 }, 703 },
1264 "set-blocking": { 704 "sigmund": {
1265 "version": "2.0.0", 705 "version": "1.0.1",
1266 "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 706 "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
1267 "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 707 "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
1268 "dev": true
1269 },
1270 "shelljs": {
1271 "version": "0.8.3",
1272 "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
1273 "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
1274 "dev": true,
1275 "requires": {
1276 "glob": "^7.0.0",
1277 "interpret": "^1.0.0",
1278 "rechoir": "^0.6.2"
1279 }
1280 },
1281 "shx": {
1282 "version": "0.3.2",
1283 "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.2.tgz",
1284 "integrity": "sha512-aS0mWtW3T2sHAenrSrip2XGv39O9dXIFUqxAEWHEOS1ePtGIBavdPJY1kE2IHl14V/4iCbUiNDPGdyYTtmhSoA==",
1285 "dev": true,
1286 "requires": {
1287 "es6-object-assign": "^1.0.3",
1288 "minimist": "^1.2.0",
1289 "shelljs": "^0.8.1"
1290 },
1291 "dependencies": {
1292 "minimist": {
1293 "version": "1.2.0",
1294 "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
1295 "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
1296 "dev": true
1297 }
1298 }
1299 },
1300 "source-map-resolve": {
1301 "version": "0.5.2",
1302 "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
1303 "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
1304 "dev": true,
1305 "requires": {
1306 "atob": "^2.1.1",
1307 "decode-uri-component": "^0.2.0",
1308 "resolve-url": "^0.2.1",
1309 "source-map-url": "^0.4.0",
1310 "urix": "^0.1.0"
1311 }
1312 },
1313 "source-map-url": {
1314 "version": "0.4.0",
1315 "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
1316 "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
1317 "dev": true 708 "dev": true
1318 }, 709 },
1319 "sourcemap-codec": { 710 "sourcemap-codec": {
@@ -1328,36 +719,6 @@
1328 "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 719 "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
1329 "dev": true 720 "dev": true
1330 }, 721 },
1331 "string-width": {
1332 "version": "2.1.1",
1333 "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
1334 "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
1335 "dev": true,
1336 "requires": {
1337 "is-fullwidth-code-point": "^2.0.0",
1338 "strip-ansi": "^4.0.0"
1339 }
1340 },
1341 "string.prototype.trimleft": {
1342 "version": "2.1.0",
1343 "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
1344 "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
1345 "dev": true,
1346 "requires": {
1347 "define-properties": "^1.1.3",
1348 "function-bind": "^1.1.1"
1349 }
1350 },
1351 "string.prototype.trimright": {
1352 "version": "2.1.0",
1353 "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
1354 "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
1355 "dev": true,
1356 "requires": {
1357 "define-properties": "^1.1.3",
1358 "function-bind": "^1.1.1"
1359 }
1360 },
1361 "string_decoder": { 722 "string_decoder": {
1362 "version": "1.3.0", 723 "version": "1.3.0",
1363 "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 724 "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -1367,25 +728,10 @@
1367 "safe-buffer": "~5.2.0" 728 "safe-buffer": "~5.2.0"
1368 } 729 }
1369 }, 730 },
1370 "strip-ansi": {
1371 "version": "4.0.0",
1372 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
1373 "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
1374 "dev": true,
1375 "requires": {
1376 "ansi-regex": "^3.0.0"
1377 }
1378 },
1379 "strip-json-comments": {
1380 "version": "2.0.1",
1381 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
1382 "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
1383 "dev": true
1384 },
1385 "supports-color": { 731 "supports-color": {
1386 "version": "6.0.0", 732 "version": "5.5.0",
1387 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 733 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1388 "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 734 "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1389 "dev": true, 735 "dev": true,
1390 "requires": { 736 "requires": {
1391 "has-flag": "^3.0.0" 737 "has-flag": "^3.0.0"
@@ -1427,31 +773,20 @@
1427 "tsutils": "^2.29.0" 773 "tsutils": "^2.29.0"
1428 }, 774 },
1429 "dependencies": { 775 "dependencies": {
1430 "diff": { 776 "builtin-modules": {
1431 "version": "4.0.1", 777 "version": "1.1.1",
1432 "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", 778 "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
1433 "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", 779 "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
780 "dev": true
781 },
782 "semver": {
783 "version": "5.7.1",
784 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
785 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1434 "dev": true 786 "dev": true
1435 } 787 }
1436 } 788 }
1437 }, 789 },
1438 "tslint-config-prettier": {
1439 "version": "1.18.0",
1440 "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz",
1441 "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==",
1442 "dev": true
1443 },
1444 "tslint-plugin-prettier": {
1445 "version": "2.0.1",
1446 "resolved": "https://registry.npmjs.org/tslint-plugin-prettier/-/tslint-plugin-prettier-2.0.1.tgz",
1447 "integrity": "sha512-4FX9JIx/1rKHIPJNfMb+ooX1gPk5Vg3vNi7+dyFYpLO+O57F4g+b/fo1+W/G0SUOkBLHB/YKScxjX/P+7ZT/Tw==",
1448 "dev": true,
1449 "requires": {
1450 "eslint-plugin-prettier": "^2.2.0",
1451 "lines-and-columns": "^1.1.6",
1452 "tslib": "^1.7.1"
1453 }
1454 },
1455 "tsutils": { 790 "tsutils": {
1456 "version": "2.29.0", 791 "version": "2.29.0",
1457 "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 792 "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
@@ -1478,11 +813,21 @@
1478 } 813 }
1479 }, 814 },
1480 "typescript": { 815 "typescript": {
1481 "version": "3.7.3", 816 "version": "3.7.4",
1482 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", 817 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
1483 "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", 818 "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
1484 "dev": true 819 "dev": true
1485 }, 820 },
821 "typescript-formatter": {
822 "version": "7.2.2",
823 "resolved": "https://registry.npmjs.org/typescript-formatter/-/typescript-formatter-7.2.2.tgz",
824 "integrity": "sha512-V7vfI9XArVhriOTYHPzMU2WUnm5IMdu9X/CPxs8mIMGxmTBFpDABlbkBka64PZJ9/xgQeRpK8KzzAG4MPzxBDQ==",
825 "dev": true,
826 "requires": {
827 "commandpost": "^1.0.0",
828 "editorconfig": "^0.15.0"
829 }
830 },
1486 "uc.micro": { 831 "uc.micro": {
1487 "version": "1.0.6", 832 "version": "1.0.6",
1488 "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", 833 "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
@@ -1495,12 +840,6 @@
1495 "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", 840 "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
1496 "dev": true 841 "dev": true
1497 }, 842 },
1498 "urix": {
1499 "version": "0.1.0",
1500 "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
1501 "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
1502 "dev": true
1503 },
1504 "url-join": { 843 "url-join": {
1505 "version": "1.1.0", 844 "version": "1.1.0",
1506 "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz", 845 "resolved": "https://registry.npmjs.org/url-join/-/url-join-1.1.0.tgz",
@@ -1539,6 +878,14 @@
1539 "url-join": "^1.1.0", 878 "url-join": "^1.1.0",
1540 "yauzl": "^2.3.1", 879 "yauzl": "^2.3.1",
1541 "yazl": "^2.2.2" 880 "yazl": "^2.2.2"
881 },
882 "dependencies": {
883 "semver": {
884 "version": "5.7.1",
885 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
886 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
887 "dev": true
888 }
1542 } 889 }
1543 }, 890 },
1544 "vscode-jsonrpc": { 891 "vscode-jsonrpc": {
@@ -1553,13 +900,6 @@
1553 "requires": { 900 "requires": {
1554 "semver": "^6.3.0", 901 "semver": "^6.3.0",
1555 "vscode-languageserver-protocol": "^3.15.0-next.14" 902 "vscode-languageserver-protocol": "^3.15.0-next.14"
1556 },
1557 "dependencies": {
1558 "semver": {
1559 "version": "6.3.0",
1560 "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
1561 "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
1562 }
1563 } 903 }
1564 }, 904 },
1565 "vscode-languageserver-protocol": { 905 "vscode-languageserver-protocol": {
@@ -1576,159 +916,18 @@
1576 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz", 916 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz",
1577 "integrity": "sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw==" 917 "integrity": "sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw=="
1578 }, 918 },
1579 "vscode-test": {
1580 "version": "1.3.0",
1581 "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.3.0.tgz",
1582 "integrity": "sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==",
1583 "dev": true,
1584 "requires": {
1585 "http-proxy-agent": "^2.1.0",
1586 "https-proxy-agent": "^2.2.4",
1587 "rimraf": "^2.6.3"
1588 }
1589 },
1590 "which": {
1591 "version": "1.3.1",
1592 "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1593 "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1594 "dev": true,
1595 "requires": {
1596 "isexe": "^2.0.0"
1597 }
1598 },
1599 "which-module": {
1600 "version": "2.0.0",
1601 "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
1602 "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
1603 "dev": true
1604 },
1605 "wide-align": {
1606 "version": "1.1.3",
1607 "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
1608 "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
1609 "dev": true,
1610 "requires": {
1611 "string-width": "^1.0.2 || 2"
1612 }
1613 },
1614 "wrap-ansi": {
1615 "version": "5.1.0",
1616 "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
1617 "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
1618 "dev": true,
1619 "requires": {
1620 "ansi-styles": "^3.2.0",
1621 "string-width": "^3.0.0",
1622 "strip-ansi": "^5.0.0"
1623 },
1624 "dependencies": {
1625 "ansi-regex": {
1626 "version": "4.1.0",
1627 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
1628 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
1629 "dev": true
1630 },
1631 "string-width": {
1632 "version": "3.1.0",
1633 "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
1634 "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
1635 "dev": true,
1636 "requires": {
1637 "emoji-regex": "^7.0.1",
1638 "is-fullwidth-code-point": "^2.0.0",
1639 "strip-ansi": "^5.1.0"
1640 }
1641 },
1642 "strip-ansi": {
1643 "version": "5.2.0",
1644 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
1645 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
1646 "dev": true,
1647 "requires": {
1648 "ansi-regex": "^4.1.0"
1649 }
1650 }
1651 }
1652 },
1653 "wrappy": { 919 "wrappy": {
1654 "version": "1.0.2", 920 "version": "1.0.2",
1655 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 921 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1656 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 922 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
1657 "dev": true 923 "dev": true
1658 }, 924 },
1659 "y18n": { 925 "yallist": {
1660 "version": "4.0.0", 926 "version": "2.1.2",
1661 "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 927 "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
1662 "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 928 "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
1663 "dev": true 929 "dev": true
1664 }, 930 },
1665 "yargs": {
1666 "version": "13.3.0",
1667 "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
1668 "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
1669 "dev": true,
1670 "requires": {
1671 "cliui": "^5.0.0",
1672 "find-up": "^3.0.0",
1673 "get-caller-file": "^2.0.1",
1674 "require-directory": "^2.1.1",
1675 "require-main-filename": "^2.0.0",
1676 "set-blocking": "^2.0.0",
1677 "string-width": "^3.0.0",
1678 "which-module": "^2.0.0",
1679 "y18n": "^4.0.0",
1680 "yargs-parser": "^13.1.1"
1681 },
1682 "dependencies": {
1683 "ansi-regex": {
1684 "version": "4.1.0",
1685 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
1686 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
1687 "dev": true
1688 },
1689 "string-width": {
1690 "version": "3.1.0",
1691 "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
1692 "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
1693 "dev": true,
1694 "requires": {
1695 "emoji-regex": "^7.0.1",
1696 "is-fullwidth-code-point": "^2.0.0",
1697 "strip-ansi": "^5.1.0"
1698 }
1699 },
1700 "strip-ansi": {
1701 "version": "5.2.0",
1702 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
1703 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
1704 "dev": true,
1705 "requires": {
1706 "ansi-regex": "^4.1.0"
1707 }
1708 }
1709 }
1710 },
1711 "yargs-parser": {
1712 "version": "13.1.1",
1713 "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
1714 "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
1715 "dev": true,
1716 "requires": {
1717 "camelcase": "^5.0.0",
1718 "decamelize": "^1.2.0"
1719 }
1720 },
1721 "yargs-unparser": {
1722 "version": "1.6.0",
1723 "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
1724 "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
1725 "dev": true,
1726 "requires": {
1727 "flat": "^4.1.0",
1728 "lodash": "^4.17.15",
1729 "yargs": "^13.3.0"
1730 }
1731 },
1732 "yauzl": { 931 "yauzl": {
1733 "version": "2.10.0", 932 "version": "2.10.0",
1734 "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 933 "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index f75fafeb9..7d809a2d3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -16,47 +16,29 @@
16 "vscode": "^1.41.0" 16 "vscode": "^1.41.0"
17 }, 17 },
18 "scripts": { 18 "scripts": {
19 "vscode:prepublish": "npm run compile", 19 "vscode:prepublish": "rollup -c",
20 "package": "vsce package", 20 "package": "vsce package",
21 "compile": "rollup -c && shx cp src/utils/terminateProcess.sh bundle/terminateProcess.sh",
22 "watch": "tsc -watch -p ./", 21 "watch": "tsc -watch -p ./",
23 "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix", 22 "fmt": "tsfmt -r && tslint -c tslint.json 'src/**/*.ts' --fix"
24 "lint": "tslint --project .",
25 "prettier": "prettier **/*.{json,ts}",
26 "test": "tsc -p . && node ./out/test/runTest.js",
27 "travis": "npm run compile && npm run test && npm run lint && npm run prettier -- --write && git diff --exit-code"
28 },
29 "prettier": {
30 "singleQuote": true,
31 "tabWidth": 4,
32 "trailingComma": "all"
33 }, 23 },
34 "dependencies": { 24 "dependencies": {
35 "lookpath": "^1.0.4", 25 "jsonc-parser": "^2.1.0",
36 "seedrandom": "^3.0.5", 26 "seedrandom": "^3.0.5",
37 "vscode-languageclient": "^6.0.0-next.9" 27 "vscode-languageclient": "^6.0.0-next.9"
38 }, 28 },
39 "devDependencies": { 29 "devDependencies": {
40 "@types/glob": "^7.1.1", 30 "@rollup/plugin-commonjs": "^11.0.0",
41 "@types/mocha": "^5.2.7", 31 "@rollup/plugin-node-resolve": "^6.0.0",
32 "@rollup/plugin-typescript": "^2.0.1",
42 "@types/node": "^12.12.21", 33 "@types/node": "^12.12.21",
43 "@types/seedrandom": "^2.4.28", 34 "@types/seedrandom": "^2.4.28",
44 "@types/vscode": "^1.41.0", 35 "@types/vscode": "^1.41.0",
45 "glob": "^7.1.6", 36 "rollup": "^1.27.14",
46 "mocha": "^6.2.2", 37 "tslib": "^1.10.0",
47 "prettier": "^1.19.1",
48 "rollup": "^1.27.13",
49 "rollup-plugin-commonjs": "^10.1.0",
50 "rollup-plugin-node-resolve": "^5.2.0",
51 "rollup-plugin-sourcemaps": "^0.4.2",
52 "rollup-plugin-typescript": "^1.0.1",
53 "shx": "^0.3.1",
54 "tslint": "^5.20.1", 38 "tslint": "^5.20.1",
55 "tslint-config-prettier": "^1.18.0",
56 "tslint-plugin-prettier": "^2.0.1",
57 "typescript": "^3.7.3", 39 "typescript": "^3.7.3",
58 "vsce": "^1.71.0", 40 "typescript-formatter": "^7.2.2",
59 "vscode-test": "^1.3.0" 41 "vsce": "^1.71.0"
60 }, 42 },
61 "activationEvents": [ 43 "activationEvents": [
62 "onLanguage:rust", 44 "onLanguage:rust",
@@ -64,7 +46,7 @@
64 "onCommand:rust-analyzer.collectGarbage", 46 "onCommand:rust-analyzer.collectGarbage",
65 "workspaceContains:**/Cargo.toml" 47 "workspaceContains:**/Cargo.toml"
66 ], 48 ],
67 "main": "./bundle/extension", 49 "main": "./out/main",
68 "contributes": { 50 "contributes": {
69 "taskDefinitions": [ 51 "taskDefinitions": [
70 { 52 {
@@ -133,16 +115,6 @@
133 "command": "rust-analyzer.reload", 115 "command": "rust-analyzer.reload",
134 "title": "Restart server", 116 "title": "Restart server",
135 "category": "Rust Analyzer" 117 "category": "Rust Analyzer"
136 },
137 {
138 "command": "rust-analyzer.startCargoWatch",
139 "title": "Start Cargo Watch",
140 "category": "Rust Analyzer"
141 },
142 {
143 "command": "rust-analyzer.stopCargoWatch",
144 "title": "Stop Cargo Watch",
145 "category": "Rust Analyzer"
146 } 118 }
147 ], 119 ],
148 "keybindings": [ 120 "keybindings": [
@@ -198,21 +170,6 @@
198 "default": "ra_lsp_server", 170 "default": "ra_lsp_server",
199 "description": "Path to ra_lsp_server executable" 171 "description": "Path to ra_lsp_server executable"
200 }, 172 },
201 "rust-analyzer.enableCargoWatchOnStartup": {
202 "type": "string",
203 "default": "ask",
204 "enum": [
205 "ask",
206 "enabled",
207 "disabled"
208 ],
209 "enumDescriptions": [
210 "Asks each time whether to run `cargo watch`",
211 "`cargo watch` is always started",
212 "Don't start `cargo watch`"
213 ],
214 "description": "Whether to run `cargo watch` on startup"
215 },
216 "rust-analyzer.excludeGlobs": { 173 "rust-analyzer.excludeGlobs": {
217 "type": "array", 174 "type": "array",
218 "default": [], 175 "default": [],
@@ -223,21 +180,21 @@
223 "default": true, 180 "default": true,
224 "description": "client provided file watching instead of notify watching." 181 "description": "client provided file watching instead of notify watching."
225 }, 182 },
183 "rust-analyzer.cargo-watch.enable": {
184 "type": "boolean",
185 "default": true,
186 "description": "Run `cargo check` for diagnostics on save"
187 },
226 "rust-analyzer.cargo-watch.arguments": { 188 "rust-analyzer.cargo-watch.arguments": {
227 "type": "string", 189 "type": "array",
228 "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", 190 "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )",
229 "default": "" 191 "default": []
230 }, 192 },
231 "rust-analyzer.cargo-watch.command": { 193 "rust-analyzer.cargo-watch.command": {
232 "type": "string", 194 "type": "string",
233 "description": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", 195 "description": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )",
234 "default": "check" 196 "default": "check"
235 }, 197 },
236 "rust-analyzer.cargo-watch.ignore": {
237 "type": "array",
238 "description": "A list of patterns for cargo-watch to ignore (will be passed as `--ignore`)",
239 "default": []
240 },
241 "rust-analyzer.cargo-watch.allTargets": { 198 "rust-analyzer.cargo-watch.allTargets": {
242 "type": "boolean", 199 "type": "boolean",
243 "description": "Check all targets and tests (will be passed as `--all-targets`)", 200 "description": "Check all targets and tests (will be passed as `--all-targets`)",
@@ -259,17 +216,6 @@
259 "default": "off", 216 "default": "off",
260 "description": "Trace requests to the ra_lsp_server" 217 "description": "Trace requests to the ra_lsp_server"
261 }, 218 },
262 "rust-analyzer.trace.cargo-watch": {
263 "type": "string",
264 "scope": "window",
265 "enum": [
266 "off",
267 "error",
268 "verbose"
269 ],
270 "default": "off",
271 "description": "Trace output of cargo-watch"
272 },
273 "rust-analyzer.lruCapacity": { 219 "rust-analyzer.lruCapacity": {
274 "type": "number", 220 "type": "number",
275 "default": null, 221 "default": null,
@@ -367,232 +313,7 @@
367 ], 313 ],
368 "colors": [ 314 "colors": [
369 { 315 {
370 "id": "ralsp.comment", 316 "id": "rust_analyzer.inlayHint",
371 "description": "Color for comments",
372 "defaults": {
373 "dark": "#6A9955",
374 "light": "#008000",
375 "highContrast": "#7CA668"
376 }
377 },
378 {
379 "id": "ralsp.string",
380 "description": "Color for strings",
381 "defaults": {
382 "dark": "#CE9178",
383 "light": "#A31515",
384 "highContrast": "#CE9178"
385 }
386 },
387 {
388 "id": "ralsp.keyword",
389 "description": "Color for keywords",
390 "defaults": {
391 "dark": "#569cd6",
392 "light": "#0000FF",
393 "highContrast": "#569CD6"
394 }
395 },
396 {
397 "id": "ralsp.keyword.control",
398 "description": "Color for control keywords",
399 "defaults": {
400 "dark": "#C586C0",
401 "light": "#AF00DB",
402 "highContrast": "#C586C0"
403 }
404 },
405 {
406 "id": "ralsp.keyword.unsafe",
407 "description": "Color for unsafe",
408 "defaults": {
409 "dark": "#FF3030",
410 "light": "#FF1010",
411 "highContrast": "#FF1010"
412 }
413 },
414 {
415 "id": "ralsp.function",
416 "description": "Color for functions",
417 "defaults": {
418 "dark": "#DCDCAA",
419 "light": "#795E26",
420 "highContrast": "#DCDCAA"
421 }
422 },
423 {
424 "id": "ralsp.parameter",
425 "description": "Color for parameters",
426 "defaults": {
427 "dark": "#9CDCFE",
428 "light": "#001080",
429 "highContrast": "#9CDCFE"
430 }
431 },
432 {
433 "id": "ralsp.builtin",
434 "description": "Color for builtins",
435 "defaults": {
436 "dark": "#DD6718",
437 "light": "#DD6718",
438 "highContrast": "#DD6718"
439 }
440 },
441 {
442 "id": "ralsp.text",
443 "description": "Color for text",
444 "defaults": {
445 "dark": "#D4D4D4",
446 "light": "#000000",
447 "highContrast": "#FFFFFF"
448 }
449 },
450 {
451 "id": "ralsp.attribute",
452 "description": "Color for attributes",
453 "defaults": {
454 "dark": "#9FE9BF",
455 "light": "#1F4B1F",
456 "highContrast": "#108010"
457 }
458 },
459 {
460 "id": "ralsp.literal",
461 "description": "Color for literals",
462 "defaults": {
463 "dark": "#BECEA8",
464 "light": "#09885A",
465 "highContrast": "#B5CEA8"
466 }
467 },
468 {
469 "id": "ralsp.literal.numeric",
470 "description": "Color for numeric literals",
471 "defaults": {
472 "dark": "#BECEA8",
473 "light": "#09885A",
474 "highContrast": "#B5CEA8"
475 }
476 },
477 {
478 "id": "ralsp.literal.char",
479 "description": "Color for character literals",
480 "defaults": {
481 "dark": "#BECEA8",
482 "light": "#09885A",
483 "highContrast": "#B5CEA8"
484 }
485 },
486 {
487 "id": "ralsp.literal.byte",
488 "description": "Color for byte literals",
489 "defaults": {
490 "dark": "#BECEA8",
491 "light": "#09885A",
492 "highContrast": "#B5CEA8"
493 }
494 },
495 {
496 "id": "ralsp.macro",
497 "description": "Color for macros",
498 "defaults": {
499 "dark": "#BFEBBF",
500 "light": "#DD6718",
501 "highContrast": "#ED7718"
502 }
503 },
504 {
505 "id": "ralsp.constant",
506 "description": "Color for constants",
507 "defaults": {
508 "dark": "#569cd6",
509 "light": "#267cb6",
510 "highContrast": "#569cd6"
511 }
512 },
513 {
514 "id": "ralsp.type",
515 "description": "Color for other types (traits, aliases..)",
516 "defaults": {
517 "dark": "#4EC9B0",
518 "light": "#267F99",
519 "highContrast": "#4EC9B0"
520 }
521 },
522 {
523 "id": "ralsp.type.builtin",
524 "description": "Color for built-in types (&str, bool, u16, u32)",
525 "defaults": {
526 "dark": "#4EC9B0",
527 "light": "#267F99",
528 "highContrast": "#4EC9B0"
529 }
530 },
531 {
532 "id": "ralsp.type.lifetime",
533 "description": "Color for lifetimes parameters",
534 "defaults": {
535 "dark": "#4EC9B0",
536 "light": "#267F99",
537 "highContrast": "#4EC9B0"
538 }
539 },
540 {
541 "id": "ralsp.type.self",
542 "description": "Color for `Self` param type",
543 "defaults": {
544 "dark": "#4EC9B0",
545 "light": "#267F99",
546 "highContrast": "#4EC9B0"
547 }
548 },
549 {
550 "id": "ralsp.type.param",
551 "description": "Color for type parameters",
552 "defaults": {
553 "dark": "#4EC9B0",
554 "light": "#267F99",
555 "highContrast": "#4EC9B0"
556 }
557 },
558 {
559 "id": "ralsp.field",
560 "description": "Color for fields",
561 "defaults": {
562 "dark": "#4EC9B0",
563 "light": "#267F99",
564 "highContrast": "#4EC9B0"
565 }
566 },
567 {
568 "id": "ralsp.variable",
569 "description": "Color for variables",
570 "defaults": {
571 "dark": "#4EC9B0",
572 "light": "#267F99",
573 "highContrast": "#4EC9B0"
574 }
575 },
576 {
577 "id": "ralsp.variable.mut",
578 "description": "Color for mutable variables",
579 "defaults": {
580 "dark": "#4EC9B0",
581 "light": "#267F99",
582 "highContrast": "#4EC9B0"
583 }
584 },
585 {
586 "id": "ralsp.module",
587 "description": "Color for modules",
588 "defaults": {
589 "dark": "#D4D4D4",
590 "light": "#000000",
591 "highContrast": "#FFFFFF"
592 }
593 },
594 {
595 "id": "ralsp.inlayHint",
596 "description": "Color for inlay hints", 317 "description": "Color for inlay hints",
597 "defaults": { 318 "defaults": {
598 "dark": "#A0A0A0F0", 319 "dark": "#A0A0A0F0",
diff --git a/editors/code/rollup.config.js b/editors/code/rollup.config.js
index 1b222bbe7..14fb9e085 100644
--- a/editors/code/rollup.config.js
+++ b/editors/code/rollup.config.js
@@ -1,30 +1,25 @@
1import typescript from 'rollup-plugin-typescript'; 1import typescript from '@rollup/plugin-typescript';
2import resolve from 'rollup-plugin-node-resolve'; 2import resolve from '@rollup/plugin-node-resolve';
3import commonjs from 'rollup-plugin-commonjs'; 3import commonjs from '@rollup/plugin-commonjs';
4import sourcemaps from 'rollup-plugin-sourcemaps'
5import nodeBuiltins from 'builtin-modules'; 4import nodeBuiltins from 'builtin-modules';
6 5
7export default { 6export default {
8 input: './src/extension.ts', 7 input: 'src/main.ts',
9 plugins: [ 8 plugins: [
10 typescript(), 9 typescript(),
11 sourcemaps(), 10 resolve({
12 resolve(), 11 preferBuiltins: true
12 }),
13 commonjs({ 13 commonjs({
14 namedExports: { 14 namedExports: {
15 // squelch missing import warnings 15 // squelch missing import warnings
16 'vscode-languageclient': [ 'CreateFile', 'RenameFile' ] 16 'vscode-languageclient': ['CreateFile', 'RenameFile', 'ErrorCodes']
17 } 17 }
18 }), 18 })
19 ],
20 // keep these as require() calls, bundle the rest
21 external: [
22 ...nodeBuiltins,
23 'vscode',
24 ], 19 ],
20 external: [...nodeBuiltins, 'vscode'],
25 output: { 21 output: {
26 file: './bundle/extension.js', 22 file: './out/main.js',
27 sourcemap: true, 23 format: 'cjs'
28 format: 'cjs',
29 } 24 }
30}; 25};
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
new file mode 100644
index 000000000..743384bd7
--- /dev/null
+++ b/editors/code/src/client.ts
@@ -0,0 +1,90 @@
1import { homedir } from 'os';
2import * as lc from 'vscode-languageclient';
3
4import { window, workspace } from 'vscode';
5import { Config } from './config';
6
7export function createClient(config: Config): lc.LanguageClient {
8 // '.' Is the fallback if no folder is open
9 // TODO?: Workspace folders support Uri's (eg: file://test.txt). It might be a good idea to test if the uri points to a file.
10 let folder: string = '.';
11 if (workspace.workspaceFolders !== undefined) {
12 folder = workspace.workspaceFolders[0].uri.fsPath.toString();
13 }
14
15 const command = expandPathResolving(config.raLspServerPath);
16 const run: lc.Executable = {
17 command,
18 options: { cwd: folder },
19 };
20 const serverOptions: lc.ServerOptions = {
21 run,
22 debug: run,
23 };
24 const traceOutputChannel = window.createOutputChannel(
25 'Rust Analyzer Language Server Trace',
26 );
27 const clientOptions: lc.LanguageClientOptions = {
28 documentSelector: [{ scheme: 'file', language: 'rust' }],
29 initializationOptions: {
30 publishDecorations: true,
31 lruCapacity: config.lruCapacity,
32 maxInlayHintLength: config.maxInlayHintLength,
33 cargoWatchEnable: config.cargoWatchOptions.enable,
34 cargoWatchArgs: config.cargoWatchOptions.arguments,
35 cargoWatchCommand: config.cargoWatchOptions.command,
36 cargoWatchAllTargets:
37 config.cargoWatchOptions.allTargets,
38 excludeGlobs: config.excludeGlobs,
39 useClientWatching: config.useClientWatching,
40 featureFlags: config.featureFlags,
41 withSysroot: config.withSysroot,
42 cargoFeatures: config.cargoFeatures,
43 },
44 traceOutputChannel,
45 };
46
47 const res = new lc.LanguageClient(
48 'rust-analyzer',
49 'Rust Analyzer Language Server',
50 serverOptions,
51 clientOptions,
52 );
53
54 // HACK: This is an awful way of filtering out the decorations notifications
55 // However, pending proper support, this is the most effecitve approach
56 // Proper support for this would entail a change to vscode-languageclient to allow not notifying on certain messages
57 // Or the ability to disable the serverside component of highlighting (but this means that to do tracing we need to disable hihlighting)
58 // This also requires considering our settings strategy, which is work which needs doing
59 // @ts-ignore The tracer is private to vscode-languageclient, but we need access to it to not log publishDecorations requests
60 res._tracer = {
61 log: (messageOrDataObject: string | any, data?: string) => {
62 if (typeof messageOrDataObject === 'string') {
63 if (
64 messageOrDataObject.includes(
65 'rust-analyzer/publishDecorations',
66 ) ||
67 messageOrDataObject.includes(
68 'rust-analyzer/decorationsRequest',
69 )
70 ) {
71 // Don't log publish decorations requests
72 } else {
73 // @ts-ignore This is just a utility function
74 res.logTrace(messageOrDataObject, data);
75 }
76 } else {
77 // @ts-ignore
78 res.logObjectTrace(messageOrDataObject);
79 }
80 },
81 };
82 res.registerProposedFeatures();
83 return res;
84}
85function expandPathResolving(path: string) {
86 if (path.startsWith('~/')) {
87 return path.replace('~', homedir());
88 }
89 return path;
90}
diff --git a/editors/code/src/color_theme.ts b/editors/code/src/color_theme.ts
new file mode 100644
index 000000000..cbad47f35
--- /dev/null
+++ b/editors/code/src/color_theme.ts
@@ -0,0 +1,123 @@
1import * as fs from 'fs';
2import * as jsonc from 'jsonc-parser';
3import * as path from 'path';
4import * as vscode from 'vscode';
5
6export interface TextMateRuleSettings {
7 foreground?: string;
8 background?: string;
9 fontStyle?: string;
10}
11
12export class ColorTheme {
13 private rules: Map<string, TextMateRuleSettings> = new Map();
14
15 static load(): ColorTheme {
16 // Find out current color theme
17 const themeName = vscode.workspace
18 .getConfiguration('workbench')
19 .get('colorTheme');
20
21 if (typeof themeName !== 'string') {
22 // console.warn('workbench.colorTheme is', themeName)
23 return new ColorTheme();
24 }
25 return loadThemeNamed(themeName);
26 }
27
28 static fromRules(rules: TextMateRule[]): ColorTheme {
29 const res = new ColorTheme();
30 for (const rule of rules) {
31 const scopes = typeof rule.scope === 'string'
32 ? [rule.scope]
33 : rule.scope;
34 for (const scope of scopes) {
35 res.rules.set(scope, rule.settings);
36 }
37 }
38 return res;
39 }
40
41 lookup(scopes: string[]): TextMateRuleSettings {
42 let res: TextMateRuleSettings = {};
43 for (const scope of scopes) {
44 this.rules.forEach((value, key) => {
45 if (scope.startsWith(key)) {
46 res = mergeRuleSettings(res, value);
47 }
48 });
49 }
50 return res;
51 }
52
53 mergeFrom(other: ColorTheme) {
54 other.rules.forEach((value, key) => {
55 const merged = mergeRuleSettings(this.rules.get(key), value);
56 this.rules.set(key, merged);
57 });
58 }
59}
60
61function loadThemeNamed(themeName: string): ColorTheme {
62 function isTheme(extension: vscode.Extension<any>): boolean {
63 return (
64 extension.extensionKind === vscode.ExtensionKind.UI &&
65 extension.packageJSON.contributes &&
66 extension.packageJSON.contributes.themes
67 );
68 }
69
70 let themePaths = vscode.extensions.all
71 .filter(isTheme)
72 .flatMap(ext => {
73 return ext.packageJSON.contributes.themes
74 .filter((it: any) => (it.id || it.label) === themeName)
75 .map((it: any) => path.join(ext.extensionPath, it.path));
76 });
77
78 const res = new ColorTheme();
79 for (const themePath of themePaths) {
80 res.mergeFrom(loadThemeFile(themePath));
81 }
82
83 const customizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
84 res.mergeFrom(ColorTheme.fromRules(customizations?.textMateRules ?? []));
85
86 return res;
87}
88
89function loadThemeFile(themePath: string): ColorTheme {
90 let text;
91 try {
92 text = fs.readFileSync(themePath, 'utf8');
93 } catch {
94 return new ColorTheme();
95 }
96 const obj = jsonc.parse(text);
97 const tokenColors = obj?.tokenColors ?? [];
98 const res = ColorTheme.fromRules(tokenColors);
99
100 for (const include in obj?.include ?? []) {
101 const includePath = path.join(path.dirname(themePath), include);
102 const tmp = loadThemeFile(includePath);
103 res.mergeFrom(tmp);
104 }
105
106 return res;
107}
108
109interface TextMateRule {
110 scope: string | string[];
111 settings: TextMateRuleSettings;
112}
113
114function mergeRuleSettings(
115 defaultSetting: TextMateRuleSettings | undefined,
116 override: TextMateRuleSettings,
117): TextMateRuleSettings {
118 return {
119 foreground: override.foreground ?? defaultSetting?.foreground,
120 background: override.background ?? defaultSetting?.background,
121 fontStyle: override.fontStyle ?? defaultSetting?.fontStyle,
122 };
123}
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
index 2777ced24..cfe7d1af0 100644
--- a/editors/code/src/commands/analyzer_status.ts
+++ b/editors/code/src/commands/analyzer_status.ts
@@ -1,45 +1,20 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { Server } from '../server';
3 2
4const statusUri = vscode.Uri.parse('rust-analyzer-status://status'); 3import { Ctx, Cmd } from '../ctx';
5
6export class TextDocumentContentProvider
7 implements vscode.TextDocumentContentProvider {
8 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
9 public syntaxTree: string = 'Not available';
10
11 public provideTextDocumentContent(
12 _uri: vscode.Uri,
13 ): vscode.ProviderResult<string> {
14 const editor = vscode.window.activeTextEditor;
15 if (editor == null) {
16 return '';
17 }
18 return Server.client.sendRequest<string>(
19 'rust-analyzer/analyzerStatus',
20 null,
21 );
22 }
23
24 get onDidChange(): vscode.Event<vscode.Uri> {
25 return this.eventEmitter.event;
26 }
27}
28
29let poller: NodeJS.Timer | null = null;
30 4
31// Shows status of rust-analyzer (for debugging) 5// Shows status of rust-analyzer (for debugging)
6export function analyzerStatus(ctx: Ctx): Cmd {
7 let poller: NodeJS.Timer | null = null;
8 const tdcp = new TextDocumentContentProvider(ctx);
32 9
33export function makeCommand(context: vscode.ExtensionContext) { 10 ctx.pushCleanup(
34 const textDocumentContentProvider = new TextDocumentContentProvider();
35 context.subscriptions.push(
36 vscode.workspace.registerTextDocumentContentProvider( 11 vscode.workspace.registerTextDocumentContentProvider(
37 'rust-analyzer-status', 12 'rust-analyzer-status',
38 textDocumentContentProvider, 13 tdcp,
39 ), 14 ),
40 ); 15 );
41 16
42 context.subscriptions.push({ 17 ctx.pushCleanup({
43 dispose() { 18 dispose() {
44 if (poller != null) { 19 if (poller != null) {
45 clearInterval(poller); 20 clearInterval(poller);
@@ -49,12 +24,9 @@ export function makeCommand(context: vscode.ExtensionContext) {
49 24
50 return async function handle() { 25 return async function handle() {
51 if (poller == null) { 26 if (poller == null) {
52 poller = setInterval( 27 poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
53 () => textDocumentContentProvider.eventEmitter.fire(statusUri),
54 1000,
55 );
56 } 28 }
57 const document = await vscode.workspace.openTextDocument(statusUri); 29 const document = await vscode.workspace.openTextDocument(tdcp.uri);
58 return vscode.window.showTextDocument( 30 return vscode.window.showTextDocument(
59 document, 31 document,
60 vscode.ViewColumn.Two, 32 vscode.ViewColumn.Two,
@@ -62,3 +34,31 @@ export function makeCommand(context: vscode.ExtensionContext) {
62 ); 34 );
63 }; 35 };
64} 36}
37
38class TextDocumentContentProvider
39 implements vscode.TextDocumentContentProvider {
40 private ctx: Ctx;
41 uri = vscode.Uri.parse('rust-analyzer-status://status');
42 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
43
44 constructor(ctx: Ctx) {
45 this.ctx = ctx;
46 }
47
48 provideTextDocumentContent(
49 _uri: vscode.Uri,
50 ): vscode.ProviderResult<string> {
51 const editor = vscode.window.activeTextEditor;
52 const client = this.ctx.client;
53 if (!editor || !client) return '';
54
55 return client.sendRequest<string>(
56 'rust-analyzer/analyzerStatus',
57 null,
58 );
59 }
60
61 get onDidChange(): vscode.Event<vscode.Uri> {
62 return this.eventEmitter.event;
63 }
64}
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts
deleted file mode 100644
index ac62bdd48..000000000
--- a/editors/code/src/commands/cargo_watch.ts
+++ /dev/null
@@ -1,264 +0,0 @@
1import * as child_process from 'child_process';
2import * as path from 'path';
3import * as vscode from 'vscode';
4
5import { Server } from '../server';
6import { terminate } from '../utils/processes';
7import { LineBuffer } from './line_buffer';
8import { StatusDisplay } from './watch_status';
9
10import {
11 mapRustDiagnosticToVsCode,
12 RustDiagnostic,
13} from '../utils/diagnostics/rust';
14import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection';
15import { areDiagnosticsEqual } from '../utils/diagnostics/vscode';
16
17export async function registerCargoWatchProvider(
18 subscriptions: vscode.Disposable[],
19): Promise<CargoWatchProvider | undefined> {
20 let cargoExists = false;
21
22 // Check if the working directory is valid cargo root path
23 const cargoTomlPath = path.join(vscode.workspace.rootPath!, 'Cargo.toml');
24 const cargoTomlUri = vscode.Uri.file(cargoTomlPath);
25 const cargoTomlFileInfo = await vscode.workspace.fs.stat(cargoTomlUri);
26
27 if (cargoTomlFileInfo) {
28 cargoExists = true;
29 }
30
31 if (!cargoExists) {
32 vscode.window.showErrorMessage(
33 `Couldn\'t find \'Cargo.toml\' at ${cargoTomlPath}`,
34 );
35 return;
36 }
37
38 const provider = new CargoWatchProvider();
39 subscriptions.push(provider);
40 return provider;
41}
42
43export class CargoWatchProvider implements vscode.Disposable {
44 private readonly diagnosticCollection: vscode.DiagnosticCollection;
45 private readonly statusDisplay: StatusDisplay;
46 private readonly outputChannel: vscode.OutputChannel;
47
48 private suggestedFixCollection: SuggestedFixCollection;
49 private codeActionDispose: vscode.Disposable;
50
51 private cargoProcess?: child_process.ChildProcess;
52
53 constructor() {
54 this.diagnosticCollection = vscode.languages.createDiagnosticCollection(
55 'rustc',
56 );
57 this.statusDisplay = new StatusDisplay(
58 Server.config.cargoWatchOptions.command,
59 );
60 this.outputChannel = vscode.window.createOutputChannel(
61 'Cargo Watch Trace',
62 );
63
64 // Track `rustc`'s suggested fixes so we can convert them to code actions
65 this.suggestedFixCollection = new SuggestedFixCollection();
66 this.codeActionDispose = vscode.languages.registerCodeActionsProvider(
67 [{ scheme: 'file', language: 'rust' }],
68 this.suggestedFixCollection,
69 {
70 providedCodeActionKinds:
71 SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS,
72 },
73 );
74 }
75
76 public start() {
77 if (this.cargoProcess) {
78 vscode.window.showInformationMessage(
79 'Cargo Watch is already running',
80 );
81 return;
82 }
83
84 let args =
85 Server.config.cargoWatchOptions.command + ' --message-format json';
86 if (Server.config.cargoWatchOptions.allTargets) {
87 args += ' --all-targets';
88 }
89 if (Server.config.cargoWatchOptions.command.length > 0) {
90 // Excape the double quote string:
91 args += ' ' + Server.config.cargoWatchOptions.arguments;
92 }
93 // Windows handles arguments differently than the unix-likes, so we need to wrap the args in double quotes
94 if (process.platform === 'win32') {
95 args = '"' + args + '"';
96 }
97
98 const ignoreFlags = Server.config.cargoWatchOptions.ignore.reduce(
99 (flags, pattern) => [...flags, '--ignore', pattern],
100 [] as string[],
101 );
102
103 // Start the cargo watch with json message
104 this.cargoProcess = child_process.spawn(
105 'cargo',
106 ['watch', '-x', args, ...ignoreFlags],
107 {
108 stdio: ['ignore', 'pipe', 'pipe'],
109 cwd: vscode.workspace.rootPath,
110 windowsVerbatimArguments: true,
111 },
112 );
113
114 if (!this.cargoProcess) {
115 vscode.window.showErrorMessage('Cargo Watch failed to start');
116 return;
117 }
118
119 const stdoutData = new LineBuffer();
120 this.cargoProcess.stdout?.on('data', (s: string) => {
121 stdoutData.processOutput(s, line => {
122 this.logInfo(line);
123 try {
124 this.parseLine(line);
125 } catch (err) {
126 this.logError(`Failed to parse: ${err}, content : ${line}`);
127 }
128 });
129 });
130
131 const stderrData = new LineBuffer();
132 this.cargoProcess.stderr?.on('data', (s: string) => {
133 stderrData.processOutput(s, line => {
134 this.logError('Error on cargo-watch : {\n' + line + '}\n');
135 });
136 });
137
138 this.cargoProcess.on('error', (err: Error) => {
139 this.logError(
140 'Error on cargo-watch process : {\n' + err.message + '}\n',
141 );
142 });
143
144 this.logInfo('cargo-watch started.');
145 }
146
147 public stop() {
148 if (this.cargoProcess) {
149 this.cargoProcess.kill();
150 terminate(this.cargoProcess);
151 this.cargoProcess = undefined;
152 } else {
153 vscode.window.showInformationMessage('Cargo Watch is not running');
154 }
155 }
156
157 public dispose(): void {
158 this.stop();
159
160 this.diagnosticCollection.clear();
161 this.diagnosticCollection.dispose();
162 this.outputChannel.dispose();
163 this.statusDisplay.dispose();
164 this.codeActionDispose.dispose();
165 }
166
167 private logInfo(line: string) {
168 if (Server.config.cargoWatchOptions.trace === 'verbose') {
169 this.outputChannel.append(line);
170 }
171 }
172
173 private logError(line: string) {
174 if (
175 Server.config.cargoWatchOptions.trace === 'error' ||
176 Server.config.cargoWatchOptions.trace === 'verbose'
177 ) {
178 this.outputChannel.append(line);
179 }
180 }
181
182 private parseLine(line: string) {
183 if (line.startsWith('[Running')) {
184 this.diagnosticCollection.clear();
185 this.suggestedFixCollection.clear();
186 this.statusDisplay.show();
187 }
188
189 if (line.startsWith('[Finished running')) {
190 this.statusDisplay.hide();
191 }
192
193 interface CargoArtifact {
194 reason: string;
195 package_id: string;
196 }
197
198 // https://github.com/rust-lang/cargo/blob/master/src/cargo/util/machine_message.rs
199 interface CargoMessage {
200 reason: string;
201 package_id: string;
202 message: RustDiagnostic;
203 }
204
205 // cargo-watch itself output non json format
206 // Ignore these lines
207 let data: CargoMessage;
208 try {
209 data = JSON.parse(line.trim());
210 } catch (error) {
211 this.logError(`Fail to parse to json : { ${error} }`);
212 return;
213 }
214
215 if (data.reason === 'compiler-artifact') {
216 const msg = data as CargoArtifact;
217
218 // The format of the package_id is "{name} {version} ({source_id})",
219 // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53
220 this.statusDisplay.packageName = msg.package_id.split(' ')[0];
221 } else if (data.reason === 'compiler-message') {
222 const msg = data.message as RustDiagnostic;
223
224 const mapResult = mapRustDiagnosticToVsCode(msg);
225 if (!mapResult) {
226 return;
227 }
228
229 const { location, diagnostic, suggestedFixes } = mapResult;
230 const fileUri = location.uri;
231
232 const diagnostics: vscode.Diagnostic[] = [
233 ...(this.diagnosticCollection!.get(fileUri) || []),
234 ];
235
236 // If we're building multiple targets it's possible we've already seen this diagnostic
237 const isDuplicate = diagnostics.some(d =>
238 areDiagnosticsEqual(d, diagnostic),
239 );
240 if (isDuplicate) {
241 return;
242 }
243
244 diagnostics.push(diagnostic);
245 this.diagnosticCollection!.set(fileUri, diagnostics);
246
247 if (suggestedFixes.length) {
248 for (const suggestedFix of suggestedFixes) {
249 this.suggestedFixCollection.addSuggestedFixForDiagnostic(
250 suggestedFix,
251 diagnostic,
252 );
253 }
254
255 // Have VsCode query us for the code actions
256 vscode.commands.executeCommand(
257 'vscode.executeCodeActionProvider',
258 fileUri,
259 diagnostic.range,
260 );
261 }
262 }
263 }
264}
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
index 17c78280a..dcdde78af 100644
--- a/editors/code/src/commands/expand_macro.ts
+++ b/editors/code/src/commands/expand_macro.ts
@@ -1,60 +1,23 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import { Server } from '../server';
4 3
5export const expandMacroUri = vscode.Uri.parse( 4import { Ctx, Cmd } from '../ctx';
6 'rust-analyzer://expandMacro/[EXPANSION].rs',
7);
8
9export class ExpandMacroContentProvider
10 implements vscode.TextDocumentContentProvider {
11 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
12
13 public provideTextDocumentContent(
14 _uri: vscode.Uri,
15 ): vscode.ProviderResult<string> {
16 async function handle() {
17 const editor = vscode.window.activeTextEditor;
18 if (editor == null) {
19 return '';
20 }
21
22 const position = editor.selection.active;
23 const request: MacroExpandParams = {
24 textDocument: { uri: editor.document.uri.toString() },
25 position,
26 };
27 const expanded = await Server.client.sendRequest<ExpandedMacro>(
28 'rust-analyzer/expandMacro',
29 request,
30 );
31
32 if (expanded == null) {
33 return 'Not available';
34 }
35
36 return code_format(expanded);
37 }
38
39 return handle();
40 }
41
42 get onDidChange(): vscode.Event<vscode.Uri> {
43 return this.eventEmitter.event;
44 }
45}
46 5
47// Opens the virtual file that will show the syntax tree 6// Opens the virtual file that will show the syntax tree
48// 7//
49// The contents of the file come from the `TextDocumentContentProvider` 8// The contents of the file come from the `TextDocumentContentProvider`
50export function createHandle(provider: ExpandMacroContentProvider) { 9export function expandMacro(ctx: Ctx): Cmd {
51 return async () => { 10 const tdcp = new TextDocumentContentProvider(ctx);
52 const uri = expandMacroUri; 11 ctx.pushCleanup(
53 12 vscode.workspace.registerTextDocumentContentProvider(
54 const document = await vscode.workspace.openTextDocument(uri); 13 'rust-analyzer',
55 14 tdcp,
56 provider.eventEmitter.fire(uri); 15 ),
16 );
57 17
18 return async () => {
19 const document = await vscode.workspace.openTextDocument(tdcp.uri);
20 tdcp.eventEmitter.fire(tdcp.uri);
58 return vscode.window.showTextDocument( 21 return vscode.window.showTextDocument(
59 document, 22 document,
60 vscode.ViewColumn.Two, 23 vscode.ViewColumn.Two,
@@ -63,11 +26,6 @@ export function createHandle(provider: ExpandMacroContentProvider) {
63 }; 26 };
64} 27}
65 28
66interface MacroExpandParams {
67 textDocument: TextDocumentIdentifier;
68 position: Position;
69}
70
71interface ExpandedMacro { 29interface ExpandedMacro {
72 name: string; 30 name: string;
73 expansion: string; 31 expansion: string;
@@ -81,3 +39,38 @@ function code_format(expanded: ExpandedMacro): string {
81 39
82 return result; 40 return result;
83} 41}
42
43class TextDocumentContentProvider
44 implements vscode.TextDocumentContentProvider {
45 private ctx: Ctx;
46 uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
47 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
48
49 constructor(ctx: Ctx) {
50 this.ctx = ctx;
51 }
52
53 async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
54 const editor = vscode.window.activeTextEditor;
55 const client = this.ctx.client;
56 if (!editor || !client) return '';
57
58 const position = editor.selection.active;
59 const request: lc.TextDocumentPositionParams = {
60 textDocument: { uri: editor.document.uri.toString() },
61 position,
62 };
63 const expanded = await client.sendRequest<ExpandedMacro>(
64 'rust-analyzer/expandMacro',
65 request,
66 );
67
68 if (expanded == null) return 'Not available';
69
70 return code_format(expanded);
71 }
72
73 get onDidChange(): vscode.Event<vscode.Uri> {
74 return this.eventEmitter.event;
75 }
76}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 13a696758..9a1697dcb 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -1,23 +1,63 @@
1import * as analyzerStatus from './analyzer_status'; 1import * as vscode from 'vscode';
2import * as applySourceChange from './apply_source_change'; 2import * as lc from 'vscode-languageclient';
3import * as expandMacro from './expand_macro'; 3
4import * as inlayHints from './inlay_hints'; 4import { Ctx, Cmd } from '../ctx';
5import * as joinLines from './join_lines'; 5import * as sourceChange from '../source_change';
6import * as matchingBrace from './matching_brace'; 6
7import * as onEnter from './on_enter'; 7import { analyzerStatus } from './analyzer_status';
8import * as parentModule from './parent_module'; 8import { matchingBrace } from './matching_brace';
9import * as runnables from './runnables'; 9import { joinLines } from './join_lines';
10import * as syntaxTree from './syntaxTree'; 10import { onEnter } from './on_enter';
11import { parentModule } from './parent_module';
12import { syntaxTree } from './syntax_tree';
13import { expandMacro } from './expand_macro';
14import { run, runSingle } from './runnables';
15
16function collectGarbage(ctx: Ctx): Cmd {
17 return async () => {
18 ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null);
19 };
20}
21
22function showReferences(ctx: Ctx): Cmd {
23 return (uri: string, position: lc.Position, locations: lc.Location[]) => {
24 let client = ctx.client;
25 if (client) {
26 vscode.commands.executeCommand(
27 'editor.action.showReferences',
28 vscode.Uri.parse(uri),
29 client.protocol2CodeConverter.asPosition(position),
30 locations.map(client.protocol2CodeConverter.asLocation),
31 );
32 }
33 };
34}
35
36function applySourceChange(ctx: Ctx): Cmd {
37 return async (change: sourceChange.SourceChange) => {
38 sourceChange.applySourceChange(ctx, change);
39 };
40}
41
42function reload(ctx: Ctx): Cmd {
43 return async () => {
44 vscode.window.showInformationMessage('Reloading rust-analyzer...');
45 await ctx.restartServer();
46 };
47}
11 48
12export { 49export {
13 analyzerStatus, 50 analyzerStatus,
14 applySourceChange,
15 expandMacro, 51 expandMacro,
16 joinLines, 52 joinLines,
17 matchingBrace, 53 matchingBrace,
18 parentModule, 54 parentModule,
19 runnables,
20 syntaxTree, 55 syntaxTree,
21 onEnter, 56 onEnter,
22 inlayHints, 57 collectGarbage,
58 run,
59 runSingle,
60 showReferences,
61 applySourceChange,
62 reload
23}; 63};
diff --git a/editors/code/src/commands/inlay_hints.ts b/editors/code/src/commands/inlay_hints.ts
deleted file mode 100644
index ac7dcce60..000000000
--- a/editors/code/src/commands/inlay_hints.ts
+++ /dev/null
@@ -1,115 +0,0 @@
1import * as vscode from 'vscode';
2import { Range, TextDocumentChangeEvent, TextEditor } from 'vscode';
3import { TextDocumentIdentifier } from 'vscode-languageclient';
4import { Server } from '../server';
5
6interface InlayHintsParams {
7 textDocument: TextDocumentIdentifier;
8}
9
10interface InlayHint {
11 range: Range;
12 kind: string;
13 label: string;
14}
15
16const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
17 after: {
18 color: new vscode.ThemeColor('ralsp.inlayHint'),
19 },
20});
21
22export class HintsUpdater {
23 private displayHints = true;
24
25 public async toggleHintsDisplay(displayHints: boolean): Promise<void> {
26 if (this.displayHints !== displayHints) {
27 this.displayHints = displayHints;
28 return this.refreshVisibleEditorsHints(
29 displayHints ? undefined : [],
30 );
31 }
32 }
33
34 public async refreshHintsForVisibleEditors(
35 cause?: TextDocumentChangeEvent,
36 ): Promise<void> {
37 if (!this.displayHints) {
38 return;
39 }
40 if (
41 cause !== undefined &&
42 (cause.contentChanges.length === 0 ||
43 !this.isRustDocument(cause.document))
44 ) {
45 return;
46 }
47 return this.refreshVisibleEditorsHints();
48 }
49
50 private async refreshVisibleEditorsHints(
51 newDecorations?: vscode.DecorationOptions[],
52 ) {
53 const promises: Array<Promise<void>> = [];
54
55 for (const rustEditor of vscode.window.visibleTextEditors.filter(
56 editor => this.isRustDocument(editor.document),
57 )) {
58 if (newDecorations !== undefined) {
59 promises.push(
60 Promise.resolve(
61 rustEditor.setDecorations(
62 typeHintDecorationType,
63 newDecorations,
64 ),
65 ),
66 );
67 } else {
68 promises.push(this.updateDecorationsFromServer(rustEditor));
69 }
70 }
71
72 for (const promise of promises) {
73 await promise;
74 }
75 }
76
77 private isRustDocument(document: vscode.TextDocument): boolean {
78 return document && document.languageId === 'rust';
79 }
80
81 private async updateDecorationsFromServer(
82 editor: TextEditor,
83 ): Promise<void> {
84 const newHints = await this.queryHints(editor.document.uri.toString());
85 if (newHints !== null) {
86 const newDecorations = newHints.map(hint => ({
87 range: hint.range,
88 renderOptions: {
89 after: {
90 contentText: `: ${hint.label}`,
91 },
92 },
93 }));
94 return editor.setDecorations(
95 typeHintDecorationType,
96 newDecorations,
97 );
98 }
99 }
100
101 private async queryHints(documentUri: string): Promise<InlayHint[] | null> {
102 const request: InlayHintsParams = {
103 textDocument: { uri: documentUri },
104 };
105 const client = Server.client;
106 return client
107 .onReady()
108 .then(() =>
109 client.sendRequest<InlayHint[] | null>(
110 'rust-analyzer/inlayHints',
111 request,
112 ),
113 );
114 }
115}
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
index 134ddc801..7b08c3255 100644
--- a/editors/code/src/commands/join_lines.ts
+++ b/editors/code/src/commands/join_lines.ts
@@ -1,29 +1,27 @@
1import * as vscode from 'vscode'; 1import * as lc from 'vscode-languageclient';
2 2
3import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; 3import { Ctx, Cmd } from '../ctx';
4import { Server } from '../server'; 4import { applySourceChange, SourceChange } from '../source_change';
5import {
6 handle as applySourceChange,
7 SourceChange,
8} from './apply_source_change';
9 5
10interface JoinLinesParams { 6export function joinLines(ctx: Ctx): Cmd {
11 textDocument: TextDocumentIdentifier; 7 return async () => {
12 range: Range; 8 const editor = ctx.activeRustEditor;
13} 9 const client = ctx.client;
10 if (!editor || !client) return;
14 11
15export async function handle() { 12 const request: JoinLinesParams = {
16 const editor = vscode.window.activeTextEditor; 13 range: client.code2ProtocolConverter.asRange(editor.selection),
17 if (editor == null || editor.document.languageId !== 'rust') { 14 textDocument: { uri: editor.document.uri.toString() },
18 return; 15 };
19 } 16 const change = await client.sendRequest<SourceChange>(
20 const request: JoinLinesParams = { 17 'rust-analyzer/joinLines',
21 range: Server.client.code2ProtocolConverter.asRange(editor.selection), 18 request,
22 textDocument: { uri: editor.document.uri.toString() }, 19 );
20 await applySourceChange(ctx, change);
23 }; 21 };
24 const change = await Server.client.sendRequest<SourceChange>( 22}
25 'rust-analyzer/joinLines', 23
26 request, 24interface JoinLinesParams {
27 ); 25 textDocument: lc.TextDocumentIdentifier;
28 await applySourceChange(change); 26 range: lc.Range;
29} 27}
diff --git a/editors/code/src/commands/line_buffer.ts b/editors/code/src/commands/line_buffer.ts
deleted file mode 100644
index fb5b9f7f2..000000000
--- a/editors/code/src/commands/line_buffer.ts
+++ /dev/null
@@ -1,16 +0,0 @@
1export class LineBuffer {
2 private outBuffer: string = '';
3
4 public processOutput(chunk: string, cb: (line: string) => void) {
5 this.outBuffer += chunk;
6 let eolIndex = this.outBuffer.indexOf('\n');
7 while (eolIndex >= 0) {
8 // line includes the EOL
9 const line = this.outBuffer.slice(0, eolIndex + 1);
10 cb(line);
11 this.outBuffer = this.outBuffer.slice(eolIndex + 1);
12
13 eolIndex = this.outBuffer.indexOf('\n');
14 }
15 }
16}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
index 364208cc7..7c58bb7e7 100644
--- a/editors/code/src/commands/matching_brace.ts
+++ b/editors/code/src/commands/matching_brace.ts
@@ -1,34 +1,36 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
2 3
3import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; 4import { Ctx, Cmd } from '../ctx';
4import { Server } from '../server';
5 5
6interface FindMatchingBraceParams { 6export function matchingBrace(ctx: Ctx): Cmd {
7 textDocument: TextDocumentIdentifier; 7 return async () => {
8 offsets: Position[]; 8 const editor = ctx.activeRustEditor;
9} 9 const client = ctx.client;
10 if (!editor || !client) return;
10 11
11export async function handle() { 12 const request: FindMatchingBraceParams = {
12 const editor = vscode.window.activeTextEditor; 13 textDocument: { uri: editor.document.uri.toString() },
13 if (editor == null || editor.document.languageId !== 'rust') { 14 offsets: editor.selections.map(s =>
14 return; 15 client.code2ProtocolConverter.asPosition(s.active),
15 } 16 ),
16 const request: FindMatchingBraceParams = { 17 };
17 textDocument: { uri: editor.document.uri.toString() }, 18 const response = await client.sendRequest<lc.Position[]>(
18 offsets: editor.selections.map(s => { 19 'rust-analyzer/findMatchingBrace',
19 return Server.client.code2ProtocolConverter.asPosition(s.active); 20 request,
20 }),
21 };
22 const response = await Server.client.sendRequest<Position[]>(
23 'rust-analyzer/findMatchingBrace',
24 request,
25 );
26 editor.selections = editor.selections.map((sel, idx) => {
27 const active = Server.client.protocol2CodeConverter.asPosition(
28 response[idx],
29 ); 21 );
30 const anchor = sel.isEmpty ? active : sel.anchor; 22 editor.selections = editor.selections.map((sel, idx) => {
31 return new vscode.Selection(anchor, active); 23 const active = client.protocol2CodeConverter.asPosition(
32 }); 24 response[idx],
33 editor.revealRange(editor.selection); 25 );
26 const anchor = sel.isEmpty ? active : sel.anchor;
27 return new vscode.Selection(anchor, active);
28 });
29 editor.revealRange(editor.selection);
30 };
31}
32
33interface FindMatchingBraceParams {
34 textDocument: lc.TextDocumentIdentifier;
35 offsets: lc.Position[];
34} 36}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index 772c64b3c..6f61883cd 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -1,33 +1,28 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 1import * as lc from 'vscode-languageclient';
3import { Server } from '../server';
4import {
5 handle as applySourceChange,
6 SourceChange,
7} from './apply_source_change';
8 2
9export async function handle(event: { text: string }): Promise<boolean> { 3import { applySourceChange, SourceChange } from '../source_change';
10 const editor = vscode.window.activeTextEditor; 4import { Cmd, Ctx } from '../ctx';
11 if ( 5
12 editor == null || 6export function onEnter(ctx: Ctx): Cmd {
13 editor.document.languageId !== 'rust' || 7 return async (event: { text: string }) => {
14 event.text !== '\n' 8 const editor = ctx.activeRustEditor;
15 ) { 9 const client = ctx.client;
16 return false; 10 if (!editor || event.text !== '\n') return false;
17 } 11 if (!client) return false;
18 const request: lc.TextDocumentPositionParams = { 12
19 textDocument: { uri: editor.document.uri.toString() }, 13 const request: lc.TextDocumentPositionParams = {
20 position: Server.client.code2ProtocolConverter.asPosition( 14 textDocument: { uri: editor.document.uri.toString() },
21 editor.selection.active, 15 position: client.code2ProtocolConverter.asPosition(
22 ), 16 editor.selection.active,
17 ),
18 };
19 const change = await client.sendRequest<undefined | SourceChange>(
20 'rust-analyzer/onEnter',
21 request,
22 );
23 if (!change) return false;
24
25 await applySourceChange(ctx, change);
26 return true;
23 }; 27 };
24 const change = await Server.client.sendRequest<undefined | SourceChange>(
25 'rust-analyzer/onEnter',
26 request,
27 );
28 if (!change) {
29 return false;
30 }
31 await applySourceChange(change);
32 return true;
33} 28}
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
index ad49e1bdb..bf40b4021 100644
--- a/editors/code/src/commands/parent_module.ts
+++ b/editors/code/src/commands/parent_module.ts
@@ -1,32 +1,33 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2
3import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
4import { Server } from '../server';
5 3
6export async function handle() { 4import { Ctx, Cmd } from '../ctx';
7 const editor = vscode.window.activeTextEditor; 5
8 if (editor == null || editor.document.languageId !== 'rust') { 6export function parentModule(ctx: Ctx): Cmd {
9 return; 7 return async () => {
10 } 8 const editor = ctx.activeRustEditor;
11 const request: lc.TextDocumentPositionParams = { 9 const client = ctx.client;
12 textDocument: { uri: editor.document.uri.toString() }, 10 if (!editor || !client) return;
13 position: Server.client.code2ProtocolConverter.asPosition(
14 editor.selection.active,
15 ),
16 };
17 const response = await Server.client.sendRequest<lc.Location[]>(
18 'rust-analyzer/parentModule',
19 request,
20 );
21 const loc = response[0];
22 if (loc == null) {
23 return;
24 }
25 const uri = Server.client.protocol2CodeConverter.asUri(loc.uri);
26 const range = Server.client.protocol2CodeConverter.asRange(loc.range);
27 11
28 const doc = await vscode.workspace.openTextDocument(uri); 12 const request: lc.TextDocumentPositionParams = {
29 const e = await vscode.window.showTextDocument(doc); 13 textDocument: { uri: editor.document.uri.toString() },
30 e.selection = new vscode.Selection(range.start, range.start); 14 position: client.code2ProtocolConverter.asPosition(
31 e.revealRange(range, vscode.TextEditorRevealType.InCenter); 15 editor.selection.active,
16 ),
17 };
18 const response = await client.sendRequest<lc.Location[]>(
19 'rust-analyzer/parentModule',
20 request,
21 );
22 const loc = response[0];
23 if (loc == null) return;
24
25 const uri = client.protocol2CodeConverter.asUri(loc.uri);
26 const range = client.protocol2CodeConverter.asRange(loc.range);
27
28 const doc = await vscode.workspace.openTextDocument(uri);
29 const e = await vscode.window.showTextDocument(doc);
30 e.selection = new vscode.Selection(range.start, range.start);
31 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
32 };
32} 33}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index cf980e257..7919997ce 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,11 +1,68 @@
1import * as child_process from 'child_process';
2
3import * as util from 'util';
4import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
5import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
6 3
7import { Server } from '../server'; 4import { Ctx, Cmd } from '../ctx';
8import { CargoWatchProvider, registerCargoWatchProvider } from './cargo_watch'; 5
6export function run(ctx: Ctx): Cmd {
7 let prevRunnable: RunnableQuickPick | undefined;
8
9 return async () => {
10 const editor = ctx.activeRustEditor;
11 const client = ctx.client;
12 if (!editor || !client) return;
13
14 const textDocument: lc.TextDocumentIdentifier = {
15 uri: editor.document.uri.toString(),
16 };
17 const params: RunnablesParams = {
18 textDocument,
19 position: client.code2ProtocolConverter.asPosition(
20 editor.selection.active,
21 ),
22 };
23 const runnables = await client.sendRequest<Runnable[]>(
24 'rust-analyzer/runnables',
25 params,
26 );
27 const items: RunnableQuickPick[] = [];
28 if (prevRunnable) {
29 items.push(prevRunnable);
30 }
31 for (const r of runnables) {
32 if (
33 prevRunnable &&
34 JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
35 ) {
36 continue;
37 }
38 items.push(new RunnableQuickPick(r));
39 }
40 const item = await vscode.window.showQuickPick(items);
41 if (!item) return;
42
43 item.detail = 'rerun';
44 prevRunnable = item;
45 const task = createTask(item.runnable);
46 return await vscode.tasks.executeTask(task);
47 };
48}
49
50export function runSingle(ctx: Ctx): Cmd {
51 return async (runnable: Runnable) => {
52 const editor = ctx.activeRustEditor;
53 if (!editor) return;
54
55 const task = createTask(runnable);
56 task.group = vscode.TaskGroup.Build;
57 task.presentationOptions = {
58 reveal: vscode.TaskRevealKind.Always,
59 panel: vscode.TaskPanelKind.Dedicated,
60 clear: true,
61 };
62
63 return vscode.tasks.executeTask(task);
64 };
65}
9 66
10interface RunnablesParams { 67interface RunnablesParams {
11 textDocument: lc.TextDocumentIdentifier; 68 textDocument: lc.TextDocumentIdentifier;
@@ -71,150 +128,3 @@ function createTask(spec: Runnable): vscode.Task {
71 t.presentationOptions.clear = true; 128 t.presentationOptions.clear = true;
72 return t; 129 return t;
73} 130}
74
75let prevRunnable: RunnableQuickPick | undefined;
76export async function handle(): Promise<vscode.TaskExecution | undefined> {
77 const editor = vscode.window.activeTextEditor;
78 if (editor == null || editor.document.languageId !== 'rust') {
79 return;
80 }
81 const textDocument: lc.TextDocumentIdentifier = {
82 uri: editor.document.uri.toString(),
83 };
84 const params: RunnablesParams = {
85 textDocument,
86 position: Server.client.code2ProtocolConverter.asPosition(
87 editor.selection.active,
88 ),
89 };
90 const runnables = await Server.client.sendRequest<Runnable[]>(
91 'rust-analyzer/runnables',
92 params,
93 );
94 const items: RunnableQuickPick[] = [];
95 if (prevRunnable) {
96 items.push(prevRunnable);
97 }
98 for (const r of runnables) {
99 if (
100 prevRunnable &&
101 JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
102 ) {
103 continue;
104 }
105 items.push(new RunnableQuickPick(r));
106 }
107 const item = await vscode.window.showQuickPick(items);
108 if (!item) {
109 return;
110 }
111
112 item.detail = 'rerun';
113 prevRunnable = item;
114 const task = createTask(item.runnable);
115 return await vscode.tasks.executeTask(task);
116}
117
118export async function handleSingle(runnable: Runnable) {
119 const editor = vscode.window.activeTextEditor;
120 if (editor == null || editor.document.languageId !== 'rust') {
121 return;
122 }
123
124 const task = createTask(runnable);
125 task.group = vscode.TaskGroup.Build;
126 task.presentationOptions = {
127 reveal: vscode.TaskRevealKind.Always,
128 panel: vscode.TaskPanelKind.Dedicated,
129 clear: true,
130 };
131
132 return vscode.tasks.executeTask(task);
133}
134
135/**
136 * Interactively asks the user whether we should run `cargo check` in order to
137 * provide inline diagnostics; the user is met with a series of dialog boxes
138 * that, when accepted, allow us to `cargo install cargo-watch` and then run it.
139 */
140export async function interactivelyStartCargoWatch(
141 context: vscode.ExtensionContext,
142): Promise<CargoWatchProvider | undefined> {
143 if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') {
144 return;
145 }
146
147 if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') {
148 const watch = await vscode.window.showInformationMessage(
149 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)',
150 'yes',
151 'no',
152 );
153 if (watch !== 'yes') {
154 return;
155 }
156 }
157
158 return startCargoWatch(context);
159}
160
161export async function startCargoWatch(
162 context: vscode.ExtensionContext,
163): Promise<CargoWatchProvider | undefined> {
164 const execPromise = util.promisify(child_process.exec);
165
166 const { stderr, code = 0 } = await execPromise(
167 'cargo watch --version',
168 ).catch(e => e);
169
170 if (stderr.includes('no such subcommand: `watch`')) {
171 const msg =
172 'The `cargo-watch` subcommand is not installed. Install? (takes ~1-2 minutes)';
173 const install = await vscode.window.showInformationMessage(
174 msg,
175 'yes',
176 'no',
177 );
178 if (install !== 'yes') {
179 return;
180 }
181
182 const label = 'install-cargo-watch';
183 const taskFinished = new Promise((resolve, _reject) => {
184 const disposable = vscode.tasks.onDidEndTask(({ execution }) => {
185 if (execution.task.name === label) {
186 disposable.dispose();
187 resolve();
188 }
189 });
190 });
191
192 vscode.tasks.executeTask(
193 createTask({
194 label,
195 bin: 'cargo',
196 args: ['install', 'cargo-watch'],
197 env: {},
198 }),
199 );
200 await taskFinished;
201 const output = await execPromise('cargo watch --version').catch(e => e);
202 if (output.stderr !== '') {
203 vscode.window.showErrorMessage(
204 `Couldn't install \`cargo-\`watch: ${output.stderr}`,
205 );
206 return;
207 }
208 } else if (code !== 0) {
209 vscode.window.showErrorMessage(
210 `\`cargo watch\` failed with ${code}: ${stderr}`,
211 );
212 return;
213 }
214
215 const provider = await registerCargoWatchProvider(context.subscriptions);
216 if (provider) {
217 provider.start();
218 }
219 return provider;
220}
diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts
deleted file mode 100644
index 89a80550c..000000000
--- a/editors/code/src/commands/syntaxTree.ts
+++ /dev/null
@@ -1,76 +0,0 @@
1import * as vscode from 'vscode';
2import { Range, TextDocumentIdentifier } from 'vscode-languageclient';
3
4import { Server } from '../server';
5
6export const syntaxTreeUri = vscode.Uri.parse('rust-analyzer://syntaxtree');
7
8export class SyntaxTreeContentProvider
9 implements vscode.TextDocumentContentProvider {
10 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
11 public syntaxTree: string = 'Not available';
12
13 public provideTextDocumentContent(
14 uri: vscode.Uri,
15 ): vscode.ProviderResult<string> {
16 const editor = vscode.window.activeTextEditor;
17 if (editor == null) {
18 return '';
19 }
20
21 let range: Range | undefined;
22
23 // When the range based query is enabled we take the range of the selection
24 if (uri.query === 'range=true') {
25 range = editor.selection.isEmpty
26 ? undefined
27 : Server.client.code2ProtocolConverter.asRange(
28 editor.selection,
29 );
30 }
31
32 const request: SyntaxTreeParams = {
33 textDocument: { uri: editor.document.uri.toString() },
34 range,
35 };
36 return Server.client.sendRequest<SyntaxTreeResult>(
37 'rust-analyzer/syntaxTree',
38 request,
39 );
40 }
41
42 get onDidChange(): vscode.Event<vscode.Uri> {
43 return this.eventEmitter.event;
44 }
45}
46
47interface SyntaxTreeParams {
48 textDocument: TextDocumentIdentifier;
49 range?: Range;
50}
51
52type SyntaxTreeResult = string;
53
54// Opens the virtual file that will show the syntax tree
55//
56// The contents of the file come from the `TextDocumentContentProvider`
57export function createHandle(provider: SyntaxTreeContentProvider) {
58 return async () => {
59 const editor = vscode.window.activeTextEditor;
60 const rangeEnabled = !!(editor && !editor.selection.isEmpty);
61
62 const uri = rangeEnabled
63 ? vscode.Uri.parse(`${syntaxTreeUri.toString()}?range=true`)
64 : syntaxTreeUri;
65
66 const document = await vscode.workspace.openTextDocument(uri);
67
68 provider.eventEmitter.fire(uri);
69
70 return vscode.window.showTextDocument(
71 document,
72 vscode.ViewColumn.Two,
73 true,
74 );
75 };
76}
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
new file mode 100644
index 000000000..02ea9f166
--- /dev/null
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -0,0 +1,104 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import { Ctx, Cmd } from '../ctx';
5
6// Opens the virtual file that will show the syntax tree
7//
8// The contents of the file come from the `TextDocumentContentProvider`
9export function syntaxTree(ctx: Ctx): Cmd {
10 const tdcp = new TextDocumentContentProvider(ctx);
11
12 ctx.pushCleanup(
13 vscode.workspace.registerTextDocumentContentProvider(
14 'rust-analyzer',
15 tdcp,
16 ),
17 );
18
19 vscode.workspace.onDidChangeTextDocument(
20 (event: vscode.TextDocumentChangeEvent) => {
21 const doc = event.document;
22 if (doc.languageId !== 'rust') return;
23 afterLs(() => tdcp.eventEmitter.fire(tdcp.uri));
24 },
25 ctx.subscriptions,
26 );
27
28 vscode.window.onDidChangeActiveTextEditor(
29 (editor: vscode.TextEditor | undefined) => {
30 if (!editor || editor.document.languageId !== 'rust') return;
31 tdcp.eventEmitter.fire(tdcp.uri);
32 },
33 ctx.subscriptions,
34 );
35
36 return async () => {
37 const editor = vscode.window.activeTextEditor;
38 const rangeEnabled = !!(editor && !editor.selection.isEmpty);
39
40 const uri = rangeEnabled
41 ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
42 : tdcp.uri;
43
44 const document = await vscode.workspace.openTextDocument(uri);
45
46 tdcp.eventEmitter.fire(uri);
47
48 return vscode.window.showTextDocument(
49 document,
50 vscode.ViewColumn.Two,
51 true,
52 );
53 };
54}
55
56// We need to order this after LS updates, but there's no API for that.
57// Hence, good old setTimeout.
58function afterLs(f: () => any) {
59 setTimeout(f, 10);
60}
61
62interface SyntaxTreeParams {
63 textDocument: lc.TextDocumentIdentifier;
64 range?: lc.Range;
65}
66
67class TextDocumentContentProvider
68 implements vscode.TextDocumentContentProvider {
69 private ctx: Ctx;
70 uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
71 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
72
73 constructor(ctx: Ctx) {
74 this.ctx = ctx;
75 }
76
77 provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
78 const editor = vscode.window.activeTextEditor;
79 const client = this.ctx.client;
80 if (!editor || !client) return '';
81
82 let range: lc.Range | undefined;
83
84 // When the range based query is enabled we take the range of the selection
85 if (uri.query === 'range=true') {
86 range = editor.selection.isEmpty
87 ? undefined
88 : client.code2ProtocolConverter.asRange(editor.selection);
89 }
90
91 const request: SyntaxTreeParams = {
92 textDocument: { uri: editor.document.uri.toString() },
93 range,
94 };
95 return client.sendRequest<string>(
96 'rust-analyzer/syntaxTree',
97 request,
98 );
99 }
100
101 get onDidChange(): vscode.Event<vscode.Uri> {
102 return this.eventEmitter.event;
103 }
104}
diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts
deleted file mode 100644
index 8d64394c7..000000000
--- a/editors/code/src/commands/watch_status.ts
+++ /dev/null
@@ -1,63 +0,0 @@
1import * as vscode from 'vscode';
2
3const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
4
5export class StatusDisplay implements vscode.Disposable {
6 public packageName?: string;
7
8 private i = 0;
9 private statusBarItem: vscode.StatusBarItem;
10 private command: string;
11 private timer?: NodeJS.Timeout;
12
13 constructor(command: string) {
14 this.statusBarItem = vscode.window.createStatusBarItem(
15 vscode.StatusBarAlignment.Left,
16 10,
17 );
18 this.command = command;
19 this.statusBarItem.hide();
20 }
21
22 public show() {
23 this.packageName = undefined;
24
25 this.timer =
26 this.timer ||
27 setInterval(() => {
28 if (this.packageName) {
29 this.statusBarItem!.text = `cargo ${this.command} [${
30 this.packageName
31 }] ${this.frame()}`;
32 } else {
33 this.statusBarItem!.text = `cargo ${
34 this.command
35 } ${this.frame()}`;
36 }
37 }, 300);
38
39 this.statusBarItem.show();
40 }
41
42 public hide() {
43 if (this.timer) {
44 clearInterval(this.timer);
45 this.timer = undefined;
46 }
47
48 this.statusBarItem.hide();
49 }
50
51 public dispose() {
52 if (this.timer) {
53 clearInterval(this.timer);
54 this.timer = undefined;
55 }
56
57 this.statusBarItem.dispose();
58 }
59
60 private frame() {
61 return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)];
62 }
63}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index e131f09df..ec2790b63 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -1,18 +1,11 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2 2
3import { Server } from './server';
4
5const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; 3const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG;
6 4
7export type CargoWatchStartupOptions = 'ask' | 'enabled' | 'disabled';
8export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose';
9
10export interface CargoWatchOptions { 5export interface CargoWatchOptions {
11 enableOnStartup: CargoWatchStartupOptions; 6 enable: boolean;
12 arguments: string; 7 arguments: string[];
13 command: string; 8 command: string;
14 trace: CargoWatchTraceOptions;
15 ignore: string[];
16 allTargets: boolean; 9 allTargets: boolean;
17} 10}
18 11
@@ -23,27 +16,25 @@ export interface CargoFeatures {
23} 16}
24 17
25export class Config { 18export class Config {
26 public highlightingOn = true; 19 highlightingOn = true;
27 public rainbowHighlightingOn = false; 20 rainbowHighlightingOn = false;
28 public enableEnhancedTyping = true; 21 enableEnhancedTyping = true;
29 public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; 22 raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server';
30 public lruCapacity: null | number = null; 23 lruCapacity: null | number = null;
31 public displayInlayHints = true; 24 displayInlayHints = true;
32 public maxInlayHintLength: null | number = null; 25 maxInlayHintLength: null | number = null;
33 public excludeGlobs = []; 26 excludeGlobs = [];
34 public useClientWatching = true; 27 useClientWatching = true;
35 public featureFlags = {}; 28 featureFlags = {};
36 // for internal use 29 // for internal use
37 public withSysroot: null | boolean = null; 30 withSysroot: null | boolean = null;
38 public cargoWatchOptions: CargoWatchOptions = { 31 cargoWatchOptions: CargoWatchOptions = {
39 enableOnStartup: 'ask', 32 enable: true,
40 trace: 'off', 33 arguments: [],
41 arguments: '',
42 command: '', 34 command: '',
43 ignore: [],
44 allTargets: true, 35 allTargets: true,
45 }; 36 };
46 public cargoFeatures: CargoFeatures = { 37 cargoFeatures: CargoFeatures = {
47 noDefaultFeatures: false, 38 noDefaultFeatures: false,
48 allFeatures: true, 39 allFeatures: true,
49 features: [], 40 features: [],
@@ -52,15 +43,14 @@ export class Config {
52 private prevEnhancedTyping: null | boolean = null; 43 private prevEnhancedTyping: null | boolean = null;
53 private prevCargoFeatures: null | CargoFeatures = null; 44 private prevCargoFeatures: null | CargoFeatures = null;
54 45
55 constructor() { 46 constructor(ctx: vscode.ExtensionContext) {
56 vscode.workspace.onDidChangeConfiguration(_ => 47 vscode.workspace.onDidChangeConfiguration(_ => this.refresh(), ctx.subscriptions);
57 this.userConfigChanged(), 48 this.refresh();
58 );
59 this.userConfigChanged();
60 } 49 }
61 50
62 public userConfigChanged() { 51 private refresh() {
63 const config = vscode.workspace.getConfiguration('rust-analyzer'); 52 const config = vscode.workspace.getConfiguration('rust-analyzer');
53
64 let requireReloadMessage = null; 54 let requireReloadMessage = null;
65 55
66 if (config.has('highlightingOn')) { 56 if (config.has('highlightingOn')) {
@@ -73,10 +63,6 @@ export class Config {
73 ) as boolean; 63 ) as boolean;
74 } 64 }
75 65
76 if (!this.highlightingOn && Server) {
77 Server.highlighter.removeHighlights();
78 }
79
80 if (config.has('enableEnhancedTyping')) { 66 if (config.has('enableEnhancedTyping')) {
81 this.enableEnhancedTyping = config.get( 67 this.enableEnhancedTyping = config.get(
82 'enableEnhancedTyping', 68 'enableEnhancedTyping',
@@ -100,23 +86,17 @@ export class Config {
100 RA_LSP_DEBUG || (config.get('raLspServerPath') as string); 86 RA_LSP_DEBUG || (config.get('raLspServerPath') as string);
101 } 87 }
102 88
103 if (config.has('enableCargoWatchOnStartup')) { 89 if (config.has('cargo-watch.enable')) {
104 this.cargoWatchOptions.enableOnStartup = config.get< 90 this.cargoWatchOptions.enable = config.get<boolean>(
105 CargoWatchStartupOptions 91 'cargo-watch.enable',
106 >('enableCargoWatchOnStartup', 'ask'); 92 true,
107 }
108
109 if (config.has('trace.cargo-watch')) {
110 this.cargoWatchOptions.trace = config.get<CargoWatchTraceOptions>(
111 'trace.cargo-watch',
112 'off',
113 ); 93 );
114 } 94 }
115 95
116 if (config.has('cargo-watch.arguments')) { 96 if (config.has('cargo-watch.arguments')) {
117 this.cargoWatchOptions.arguments = config.get<string>( 97 this.cargoWatchOptions.arguments = config.get<string[]>(
118 'cargo-watch.arguments', 98 'cargo-watch.arguments',
119 '', 99 [],
120 ); 100 );
121 } 101 }
122 102
@@ -127,13 +107,6 @@ export class Config {
127 ); 107 );
128 } 108 }
129 109
130 if (config.has('cargo-watch.ignore')) {
131 this.cargoWatchOptions.ignore = config.get<string[]>(
132 'cargo-watch.ignore',
133 [],
134 );
135 }
136
137 if (config.has('cargo-watch.allTargets')) { 110 if (config.has('cargo-watch.allTargets')) {
138 this.cargoWatchOptions.allTargets = config.get<boolean>( 111 this.cargoWatchOptions.allTargets = config.get<boolean>(
139 'cargo-watch.allTargets', 112 'cargo-watch.allTargets',
@@ -190,9 +163,9 @@ export class Config {
190 (this.cargoFeatures.allFeatures !== 163 (this.cargoFeatures.allFeatures !==
191 this.prevCargoFeatures.allFeatures || 164 this.prevCargoFeatures.allFeatures ||
192 this.cargoFeatures.noDefaultFeatures !== 165 this.cargoFeatures.noDefaultFeatures !==
193 this.prevCargoFeatures.noDefaultFeatures || 166 this.prevCargoFeatures.noDefaultFeatures ||
194 this.cargoFeatures.features.length !== 167 this.cargoFeatures.features.length !==
195 this.prevCargoFeatures.features.length || 168 this.prevCargoFeatures.features.length ||
196 this.cargoFeatures.features.some( 169 this.cargoFeatures.features.some(
197 (v, i) => v !== this.prevCargoFeatures!.features[i], 170 (v, i) => v !== this.prevCargoFeatures!.features[i],
198 )) 171 ))
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
new file mode 100644
index 000000000..a2a4e42a9
--- /dev/null
+++ b/editors/code/src/ctx.ts
@@ -0,0 +1,112 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import { Config } from './config';
4import { createClient } from './client';
5
6export class Ctx {
7 readonly config: Config;
8 // Because we have "reload server" action, various listeners **will** face a
9 // situation where the client is not ready yet, and should be prepared to
10 // deal with it.
11 //
12 // Ideally, this should be replaced with async getter though.
13 client: lc.LanguageClient | null = null;
14 private extCtx: vscode.ExtensionContext;
15 private onDidRestartHooks: Array<(client: lc.LanguageClient) => void> = [];
16
17 constructor(extCtx: vscode.ExtensionContext) {
18 this.config = new Config(extCtx);
19 this.extCtx = extCtx;
20 }
21
22 async restartServer() {
23 let old = this.client;
24 if (old) {
25 await old.stop();
26 }
27 this.client = null;
28 const client = createClient(this.config);
29 this.pushCleanup(client.start());
30 await client.onReady();
31
32 this.client = client;
33 for (const hook of this.onDidRestartHooks) {
34 hook(client);
35 }
36 }
37
38 get activeRustEditor(): vscode.TextEditor | undefined {
39 const editor = vscode.window.activeTextEditor;
40 return editor && editor.document.languageId === 'rust'
41 ? editor
42 : undefined;
43 }
44
45 registerCommand(name: string, factory: (ctx: Ctx) => Cmd) {
46 const fullName = `rust-analyzer.${name}`;
47 const cmd = factory(this);
48 const d = vscode.commands.registerCommand(fullName, cmd);
49 this.pushCleanup(d);
50 }
51
52 overrideCommand(name: string, factory: (ctx: Ctx) => Cmd) {
53 const defaultCmd = `default:${name}`;
54 const override = factory(this);
55 const original = (...args: any[]) =>
56 vscode.commands.executeCommand(defaultCmd, ...args);
57 try {
58 const d = vscode.commands.registerCommand(
59 name,
60 async (...args: any[]) => {
61 if (!(await override(...args))) {
62 return await original(...args);
63 }
64 },
65 );
66 this.pushCleanup(d);
67 } catch (_) {
68 vscode.window.showWarningMessage(
69 'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings',
70 );
71 }
72 }
73
74 get subscriptions(): { dispose(): any }[] {
75 return this.extCtx.subscriptions;
76 }
77
78 pushCleanup(d: { dispose(): any }) {
79 this.extCtx.subscriptions.push(d);
80 }
81
82 onDidRestart(hook: (client: lc.LanguageClient) => void) {
83 this.onDidRestartHooks.push(hook);
84 }
85}
86
87export type Cmd = (...args: any[]) => any;
88
89export async function sendRequestWithRetry<R>(
90 client: lc.LanguageClient,
91 method: string,
92 param: any,
93 token?: vscode.CancellationToken,
94): Promise<R> {
95 for (const delay of [2, 4, 6, 8, 10, null]) {
96 try {
97 return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param));
98 } catch (e) {
99 if (
100 e.code === lc.ErrorCodes.ContentModified &&
101 delay !== null
102 ) {
103 await sleep(10 * (1 << delay));
104 continue;
105 }
106 throw e;
107 }
108 }
109 throw 'unreachable';
110}
111
112const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts
deleted file mode 100644
index 74b91bd48..000000000
--- a/editors/code/src/events/change_active_text_editor.ts
+++ /dev/null
@@ -1,32 +0,0 @@
1import { TextEditor } from 'vscode';
2import { TextDocumentIdentifier } from 'vscode-languageclient';
3
4import {
5 SyntaxTreeContentProvider,
6 syntaxTreeUri,
7} from '../commands/syntaxTree';
8import { Decoration } from '../highlighting';
9import { Server } from '../server';
10
11export function makeHandler(syntaxTreeProvider: SyntaxTreeContentProvider) {
12 return async function handle(editor: TextEditor | undefined) {
13 if (!editor || editor.document.languageId !== 'rust') {
14 return;
15 }
16
17 syntaxTreeProvider.eventEmitter.fire(syntaxTreeUri);
18
19 if (!Server.config.highlightingOn) {
20 return;
21 }
22
23 const params: TextDocumentIdentifier = {
24 uri: editor.document.uri.toString(),
25 };
26 const decorations = await Server.client.sendRequest<Decoration[]>(
27 'rust-analyzer/decorationsRequest',
28 params,
29 );
30 Server.highlighter.setHighlights(editor, decorations);
31 };
32}
diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts
deleted file mode 100644
index 2e998e889..000000000
--- a/editors/code/src/events/change_text_document.ts
+++ /dev/null
@@ -1,24 +0,0 @@
1import * as vscode from 'vscode';
2
3import {
4 SyntaxTreeContentProvider,
5 syntaxTreeUri,
6} from '../commands/syntaxTree';
7
8export function createHandler(syntaxTreeProvider: SyntaxTreeContentProvider) {
9 return (event: vscode.TextDocumentChangeEvent) => {
10 const doc = event.document;
11 if (doc.languageId !== 'rust') {
12 return;
13 }
14 afterLs(() => {
15 syntaxTreeProvider.eventEmitter.fire(syntaxTreeUri);
16 });
17 };
18}
19
20// We need to order this after LS updates, but there's no API for that.
21// Hence, good old setTimeout.
22function afterLs(f: () => any) {
23 setTimeout(f, 10);
24}
diff --git a/editors/code/src/events/index.ts b/editors/code/src/events/index.ts
deleted file mode 100644
index 4c154563f..000000000
--- a/editors/code/src/events/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
1import * as changeActiveTextEditor from './change_active_text_editor';
2import * as changeTextDocument from './change_text_document';
3
4export { changeActiveTextEditor, changeTextDocument };
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts
deleted file mode 100644
index 815f3692c..000000000
--- a/editors/code/src/extension.ts
+++ /dev/null
@@ -1,218 +0,0 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import * as commands from './commands';
5import { CargoWatchProvider } from './commands/cargo_watch';
6import { ExpandMacroContentProvider } from './commands/expand_macro';
7import { HintsUpdater } from './commands/inlay_hints';
8import {
9 interactivelyStartCargoWatch,
10 startCargoWatch,
11} from './commands/runnables';
12import { SyntaxTreeContentProvider } from './commands/syntaxTree';
13import * as events from './events';
14import * as notifications from './notifications';
15import { Server } from './server';
16
17export async function activate(context: vscode.ExtensionContext) {
18 function disposeOnDeactivation(disposable: vscode.Disposable) {
19 context.subscriptions.push(disposable);
20 }
21
22 function registerCommand(name: string, f: any) {
23 disposeOnDeactivation(vscode.commands.registerCommand(name, f));
24 }
25 function overrideCommand(
26 name: string,
27 f: (...args: any[]) => Promise<boolean>,
28 ) {
29 const defaultCmd = `default:${name}`;
30 const original = (...args: any[]) =>
31 vscode.commands.executeCommand(defaultCmd, ...args);
32
33 try {
34 registerCommand(name, async (...args: any[]) => {
35 const editor = vscode.window.activeTextEditor;
36 if (
37 !editor ||
38 !editor.document ||
39 editor.document.languageId !== 'rust'
40 ) {
41 return await original(...args);
42 }
43 if (!(await f(...args))) {
44 return await original(...args);
45 }
46 });
47 } catch (_) {
48 vscode.window.showWarningMessage(
49 'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings',
50 );
51 }
52 }
53
54 // Commands are requests from vscode to the language server
55 registerCommand(
56 'rust-analyzer.analyzerStatus',
57 commands.analyzerStatus.makeCommand(context),
58 );
59 registerCommand('rust-analyzer.collectGarbage', () =>
60 Server.client.sendRequest<null>('rust-analyzer/collectGarbage', null),
61 );
62 registerCommand(
63 'rust-analyzer.matchingBrace',
64 commands.matchingBrace.handle,
65 );
66 registerCommand('rust-analyzer.joinLines', commands.joinLines.handle);
67 registerCommand('rust-analyzer.parentModule', commands.parentModule.handle);
68 registerCommand('rust-analyzer.run', commands.runnables.handle);
69 // Unlike the above this does not send requests to the language server
70 registerCommand('rust-analyzer.runSingle', commands.runnables.handleSingle);
71 registerCommand(
72 'rust-analyzer.applySourceChange',
73 commands.applySourceChange.handle,
74 );
75 registerCommand(
76 'rust-analyzer.showReferences',
77 (uri: string, position: lc.Position, locations: lc.Location[]) => {
78 vscode.commands.executeCommand(
79 'editor.action.showReferences',
80 vscode.Uri.parse(uri),
81 Server.client.protocol2CodeConverter.asPosition(position),
82 locations.map(Server.client.protocol2CodeConverter.asLocation),
83 );
84 },
85 );
86
87 if (Server.config.enableEnhancedTyping) {
88 overrideCommand('type', commands.onEnter.handle);
89 }
90
91 // Notifications are events triggered by the language server
92 const allNotifications: Iterable<[
93 string,
94 lc.GenericNotificationHandler,
95 ]> = [
96 [
97 'rust-analyzer/publishDecorations',
98 notifications.publishDecorations.handle,
99 ],
100 ];
101 const syntaxTreeContentProvider = new SyntaxTreeContentProvider();
102 const expandMacroContentProvider = new ExpandMacroContentProvider();
103
104 // The events below are plain old javascript events, triggered and handled by vscode
105 vscode.window.onDidChangeActiveTextEditor(
106 events.changeActiveTextEditor.makeHandler(syntaxTreeContentProvider),
107 );
108
109 disposeOnDeactivation(
110 vscode.workspace.registerTextDocumentContentProvider(
111 'rust-analyzer',
112 syntaxTreeContentProvider,
113 ),
114 );
115 disposeOnDeactivation(
116 vscode.workspace.registerTextDocumentContentProvider(
117 'rust-analyzer',
118 expandMacroContentProvider,
119 ),
120 );
121
122 registerCommand(
123 'rust-analyzer.syntaxTree',
124 commands.syntaxTree.createHandle(syntaxTreeContentProvider),
125 );
126 registerCommand(
127 'rust-analyzer.expandMacro',
128 commands.expandMacro.createHandle(expandMacroContentProvider),
129 );
130
131 vscode.workspace.onDidChangeTextDocument(
132 events.changeTextDocument.createHandler(syntaxTreeContentProvider),
133 null,
134 context.subscriptions,
135 );
136
137 const startServer = () => Server.start(allNotifications);
138 const reloadCommand = () => reloadServer(startServer);
139
140 vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand);
141
142 // Executing `cargo watch` provides us with inline diagnostics on save
143 let provider: CargoWatchProvider | undefined;
144 interactivelyStartCargoWatch(context).then(p => {
145 provider = p;
146 });
147 registerCommand('rust-analyzer.startCargoWatch', () => {
148 if (provider) {
149 provider.start();
150 } else {
151 startCargoWatch(context).then(p => {
152 provider = p;
153 });
154 }
155 });
156 registerCommand('rust-analyzer.stopCargoWatch', () => {
157 if (provider) {
158 provider.stop();
159 }
160 });
161
162 // Start the language server, finally!
163 try {
164 await startServer();
165 } catch (e) {
166 vscode.window.showErrorMessage(e.message);
167 }
168
169 if (Server.config.displayInlayHints) {
170 const hintsUpdater = new HintsUpdater();
171 hintsUpdater.refreshHintsForVisibleEditors().then(() => {
172 // vscode may ignore top level hintsUpdater.refreshHintsForVisibleEditors()
173 // so update the hints once when the focus changes to guarantee their presence
174 let editorChangeDisposable: vscode.Disposable | null = null;
175 editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
176 _ => {
177 if (editorChangeDisposable !== null) {
178 editorChangeDisposable.dispose();
179 }
180 return hintsUpdater.refreshHintsForVisibleEditors();
181 },
182 );
183
184 disposeOnDeactivation(
185 vscode.window.onDidChangeVisibleTextEditors(_ =>
186 hintsUpdater.refreshHintsForVisibleEditors(),
187 ),
188 );
189 disposeOnDeactivation(
190 vscode.workspace.onDidChangeTextDocument(e =>
191 hintsUpdater.refreshHintsForVisibleEditors(e),
192 ),
193 );
194 disposeOnDeactivation(
195 vscode.workspace.onDidChangeConfiguration(_ =>
196 hintsUpdater.toggleHintsDisplay(
197 Server.config.displayInlayHints,
198 ),
199 ),
200 );
201 });
202 }
203}
204
205export function deactivate(): Thenable<void> {
206 if (!Server.client) {
207 return Promise.resolve();
208 }
209 return Server.client.stop();
210}
211
212async function reloadServer(startServer: () => Promise<void>) {
213 if (Server.client != null) {
214 vscode.window.showInformationMessage('Reloading rust-analyzer...');
215 await Server.client.stop();
216 await startServer();
217 }
218}
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index e1b0d13e7..014e96f75 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,10 +1,69 @@
1import seedrandom = require('seedrandom');
2import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
3import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as seedrandom_ from 'seedrandom';
4const seedrandom = seedrandom_; // https://github.com/jvandemo/generator-angular2-library/issues/221#issuecomment-355945207
5
6import { ColorTheme, TextMateRuleSettings } from './color_theme';
7
8import { Ctx, sendRequestWithRetry } from './ctx';
9
10export function activateHighlighting(ctx: Ctx) {
11 const highlighter = new Highlighter(ctx);
12 ctx.onDidRestart(client => {
13 client.onNotification(
14 'rust-analyzer/publishDecorations',
15 (params: PublishDecorationsParams) => {
16 if (!ctx.config.highlightingOn) return;
17
18 const targetEditor = vscode.window.visibleTextEditors.find(
19 editor => {
20 const unescapedUri = unescape(
21 editor.document.uri.toString(),
22 );
23 // Unescaped URI looks like:
24 // file:///c:/Workspace/ra-test/src/main.rs
25 return unescapedUri === params.uri;
26 },
27 );
28 if (!targetEditor) return;
29
30 highlighter.setHighlights(targetEditor, params.decorations);
31 },
32 );
33 });
34
35 vscode.workspace.onDidChangeConfiguration(
36 _ => highlighter.removeHighlights(),
37 ctx.subscriptions,
38 );
39
40 vscode.window.onDidChangeActiveTextEditor(
41 async (editor: vscode.TextEditor | undefined) => {
42 if (!editor || editor.document.languageId !== 'rust') return;
43 if (!ctx.config.highlightingOn) return;
44 let client = ctx.client;
45 if (!client) return;
46
47 const params: lc.TextDocumentIdentifier = {
48 uri: editor.document.uri.toString(),
49 };
50 const decorations = await sendRequestWithRetry<Decoration[]>(
51 client,
52 'rust-analyzer/decorationsRequest',
53 params,
54 );
55 highlighter.setHighlights(editor, decorations);
56 },
57 ctx.subscriptions,
58 );
59}
4 60
5import { Server } from './server'; 61interface PublishDecorationsParams {
62 uri: string;
63 decorations: Decoration[];
64}
6 65
7export interface Decoration { 66interface Decoration {
8 range: lc.Range; 67 range: lc.Range;
9 tag: string; 68 tag: string;
10 bindingHash?: string; 69 bindingHash?: string;
@@ -23,62 +82,17 @@ function fancify(seed: string, shade: 'light' | 'dark') {
23 return `hsl(${h},${s}%,${l}%)`; 82 return `hsl(${h},${s}%,${l}%)`;
24} 83}
25 84
26export class Highlighter { 85class Highlighter {
27 private static initDecorations(): Map< 86 private ctx: Ctx;
28 string,
29 vscode.TextEditorDecorationType
30 > {
31 const decoration = (
32 tag: string,
33 textDecoration?: string,
34 ): [string, vscode.TextEditorDecorationType] => {
35 const color = new vscode.ThemeColor('ralsp.' + tag);
36 const decor = vscode.window.createTextEditorDecorationType({
37 color,
38 textDecoration,
39 });
40 return [tag, decor];
41 };
42
43 const decorations: Iterable<[
44 string,
45 vscode.TextEditorDecorationType,
46 ]> = [
47 decoration('comment'),
48 decoration('string'),
49 decoration('keyword'),
50 decoration('keyword.control'),
51 decoration('keyword.unsafe'),
52 decoration('function'),
53 decoration('parameter'),
54 decoration('constant'),
55 decoration('type.builtin'),
56 decoration('type.generic'),
57 decoration('type.lifetime'),
58 decoration('type.param'),
59 decoration('type.self'),
60 decoration('type'),
61 decoration('text'),
62 decoration('attribute'),
63 decoration('literal'),
64 decoration('literal.numeric'),
65 decoration('literal.char'),
66 decoration('literal.byte'),
67 decoration('macro'),
68 decoration('variable'),
69 decoration('variable.mut', 'underline'),
70 decoration('field'),
71 decoration('module'),
72 ];
73
74 return new Map<string, vscode.TextEditorDecorationType>(decorations);
75 }
76
77 private decorations: Map< 87 private decorations: Map<
78 string, 88 string,
79 vscode.TextEditorDecorationType 89 vscode.TextEditorDecorationType
80 > | null = null; 90 > | null = null;
81 91
92 constructor(ctx: Ctx) {
93 this.ctx = ctx;
94 }
95
82 public removeHighlights() { 96 public removeHighlights() {
83 if (this.decorations == null) { 97 if (this.decorations == null) {
84 return; 98 return;
@@ -93,12 +107,14 @@ export class Highlighter {
93 } 107 }
94 108
95 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) { 109 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) {
110 let client = this.ctx.client;
111 if (!client) return;
96 // Initialize decorations if necessary 112 // Initialize decorations if necessary
97 // 113 //
98 // Note: decoration objects need to be kept around so we can dispose them 114 // Note: decoration objects need to be kept around so we can dispose them
99 // if the user disables syntax highlighting 115 // if the user disables syntax highlighting
100 if (this.decorations == null) { 116 if (this.decorations == null) {
101 this.decorations = Highlighter.initDecorations(); 117 this.decorations = initDecorations();
102 } 118 }
103 119
104 const byTag: Map<string, vscode.Range[]> = new Map(); 120 const byTag: Map<string, vscode.Range[]> = new Map();
@@ -106,7 +122,7 @@ export class Highlighter {
106 string, 122 string,
107 [vscode.Range[], boolean] 123 [vscode.Range[], boolean]
108 > = new Map(); 124 > = new Map();
109 const rainbowTime = Server.config.rainbowHighlightingOn; 125 const rainbowTime = this.ctx.config.rainbowHighlightingOn;
110 126
111 for (const tag of this.decorations.keys()) { 127 for (const tag of this.decorations.keys()) {
112 byTag.set(tag, []); 128 byTag.set(tag, []);
@@ -125,13 +141,13 @@ export class Highlighter {
125 colorfulIdents 141 colorfulIdents
126 .get(d.bindingHash)![0] 142 .get(d.bindingHash)![0]
127 .push( 143 .push(
128 Server.client.protocol2CodeConverter.asRange(d.range), 144 client.protocol2CodeConverter.asRange(d.range),
129 ); 145 );
130 } else { 146 } else {
131 byTag 147 byTag
132 .get(d.tag)! 148 .get(d.tag)!
133 .push( 149 .push(
134 Server.client.protocol2CodeConverter.asRange(d.range), 150 client.protocol2CodeConverter.asRange(d.range),
135 ); 151 );
136 } 152 }
137 } 153 }
@@ -154,3 +170,80 @@ export class Highlighter {
154 } 170 }
155 } 171 }
156} 172}
173
174function initDecorations(): Map<string, vscode.TextEditorDecorationType> {
175 const theme = ColorTheme.load();
176 const res = new Map();
177 TAG_TO_SCOPES.forEach((scopes, tag) => {
178 if (!scopes) throw `unmapped tag: ${tag}`;
179 let rule = theme.lookup(scopes);
180 const decor = createDecorationFromTextmate(rule);
181 res.set(tag, decor);
182 });
183 return res;
184}
185
186function createDecorationFromTextmate(
187 themeStyle: TextMateRuleSettings,
188): vscode.TextEditorDecorationType {
189 const decorationOptions: vscode.DecorationRenderOptions = {};
190 decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
191
192 if (themeStyle.foreground) {
193 decorationOptions.color = themeStyle.foreground;
194 }
195
196 if (themeStyle.background) {
197 decorationOptions.backgroundColor = themeStyle.background;
198 }
199
200 if (themeStyle.fontStyle) {
201 const parts: string[] = themeStyle.fontStyle.split(' ');
202 parts.forEach(part => {
203 switch (part) {
204 case 'italic':
205 decorationOptions.fontStyle = 'italic';
206 break;
207 case 'bold':
208 decorationOptions.fontWeight = 'bold';
209 break;
210 case 'underline':
211 decorationOptions.textDecoration = 'underline';
212 break;
213 default:
214 break;
215 }
216 });
217 }
218 return vscode.window.createTextEditorDecorationType(decorationOptions);
219}
220
221// sync with tags from `syntax_highlighting.rs`.
222const TAG_TO_SCOPES = new Map<string, string[]>([
223 ["field", ["entity.name.field"]],
224 ["function", ["entity.name.function"]],
225 ["module", ["entity.name.module"]],
226 ["constant", ["entity.name.constant"]],
227 ["macro", ["entity.name.macro"]],
228
229 ["variable", ["variable"]],
230 ["variable.mut", ["variable", "meta.mutable"]],
231
232 ["type", ["entity.name.type"]],
233 ["type.builtin", ["entity.name.type", "support.type.primitive"]],
234 ["type.self", ["entity.name.type.parameter.self"]],
235 ["type.param", ["entity.name.type.parameter"]],
236 ["type.lifetime", ["entity.name.type.lifetime"]],
237
238 ["literal.byte", ["constant.character.byte"]],
239 ["literal.char", ["constant.character"]],
240 ["literal.numeric", ["constant.numeric"]],
241
242 ["comment", ["comment"]],
243 ["string", ["string.quoted"]],
244 ["attribute", ["meta.attribute"]],
245
246 ["keyword", ["keyword"]],
247 ["keyword.unsafe", ["keyword.other.unsafe"]],
248 ["keyword.control", ["keyword.control"]],
249]);
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
new file mode 100644
index 000000000..6dd767d72
--- /dev/null
+++ b/editors/code/src/inlay_hints.ts
@@ -0,0 +1,120 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import { Ctx, sendRequestWithRetry } from './ctx';
5
6export function activateInlayHints(ctx: Ctx) {
7 const hintsUpdater = new HintsUpdater(ctx);
8 vscode.window.onDidChangeVisibleTextEditors(async _ => {
9 await hintsUpdater.refresh();
10 }, ctx.subscriptions);
11
12 vscode.workspace.onDidChangeTextDocument(async e => {
13 if (e.contentChanges.length === 0) return;
14 if (e.document.languageId !== 'rust') return;
15 await hintsUpdater.refresh();
16 }, ctx.subscriptions);
17
18 vscode.workspace.onDidChangeConfiguration(_ => {
19 hintsUpdater.setEnabled(ctx.config.displayInlayHints);
20 }, ctx.subscriptions);
21
22 ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints));
23}
24
25interface InlayHintsParams {
26 textDocument: lc.TextDocumentIdentifier;
27}
28
29interface InlayHint {
30 range: vscode.Range;
31 kind: string;
32 label: string;
33}
34
35const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
36 after: {
37 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
38 },
39});
40
41class HintsUpdater {
42 private pending: Map<string, vscode.CancellationTokenSource> = new Map();
43 private ctx: Ctx;
44 private enabled = true;
45
46 constructor(ctx: Ctx) {
47 this.ctx = ctx;
48 }
49
50 async setEnabled(enabled: boolean) {
51 if (this.enabled == enabled) return;
52 this.enabled = enabled;
53
54 if (this.enabled) {
55 await this.refresh();
56 } else {
57 this.allEditors.forEach(it => this.setDecorations(it, []));
58 }
59 }
60
61 async refresh() {
62 if (!this.enabled) return;
63 const promises = this.allEditors.map(it => this.refreshEditor(it));
64 await Promise.all(promises);
65 }
66
67 private async refreshEditor(editor: vscode.TextEditor): Promise<void> {
68 const newHints = await this.queryHints(editor.document.uri.toString());
69 if (newHints == null) return;
70 const newDecorations = newHints.map(hint => ({
71 range: hint.range,
72 renderOptions: {
73 after: {
74 contentText: `: ${hint.label}`,
75 },
76 },
77 }));
78 this.setDecorations(editor, newDecorations);
79 }
80
81 private get allEditors(): vscode.TextEditor[] {
82 return vscode.window.visibleTextEditors.filter(
83 editor => editor.document.languageId === 'rust',
84 );
85 }
86
87 private setDecorations(
88 editor: vscode.TextEditor,
89 decorations: vscode.DecorationOptions[],
90 ) {
91 editor.setDecorations(
92 typeHintDecorationType,
93 this.enabled ? decorations : [],
94 );
95 }
96
97 private async queryHints(documentUri: string): Promise<InlayHint[] | null> {
98 let client = this.ctx.client;
99 if (!client) return null;
100 const request: InlayHintsParams = {
101 textDocument: { uri: documentUri },
102 };
103 let tokenSource = new vscode.CancellationTokenSource();
104 let prev = this.pending.get(documentUri);
105 if (prev) prev.cancel();
106 this.pending.set(documentUri, tokenSource);
107 try {
108 return await sendRequestWithRetry<InlayHint[] | null>(
109 client,
110 'rust-analyzer/inlayHints',
111 request,
112 tokenSource.token,
113 );
114 } finally {
115 if (!tokenSource.token.isCancellationRequested) {
116 this.pending.delete(documentUri);
117 }
118 }
119 }
120}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
new file mode 100644
index 000000000..51dedd5ef
--- /dev/null
+++ b/editors/code/src/main.ts
@@ -0,0 +1,51 @@
1import * as vscode from 'vscode';
2
3import * as commands from './commands';
4import { activateInlayHints } from './inlay_hints';
5import { activateStatusDisplay } from './status_display';
6import { Ctx } from './ctx';
7import { activateHighlighting } from './highlighting';
8
9let ctx!: Ctx;
10
11export async function activate(context: vscode.ExtensionContext) {
12 ctx = new Ctx(context);
13
14 // Note: we try to start the server before we register various commands, so
15 // that it registers its `onDidChangeDocument` handler before us.
16 //
17 // This a horribly, horribly wrong way to deal with this problem.
18 try {
19 await ctx.restartServer();
20 } catch (e) {
21 vscode.window.showErrorMessage(e.message);
22 }
23
24
25 // Commands which invokes manually via command pallet, shortcut, etc.
26 ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
27 ctx.registerCommand('collectGarbage', commands.collectGarbage);
28 ctx.registerCommand('matchingBrace', commands.matchingBrace);
29 ctx.registerCommand('joinLines', commands.joinLines);
30 ctx.registerCommand('parentModule', commands.parentModule);
31 ctx.registerCommand('syntaxTree', commands.syntaxTree);
32 ctx.registerCommand('expandMacro', commands.expandMacro);
33 ctx.registerCommand('run', commands.run);
34 ctx.registerCommand('reload', commands.reload);
35
36 // Internal commands which are invoked by the server.
37 ctx.registerCommand('runSingle', commands.runSingle);
38 ctx.registerCommand('showReferences', commands.showReferences);
39 ctx.registerCommand('applySourceChange', commands.applySourceChange);
40
41 if (ctx.config.enableEnhancedTyping) {
42 ctx.overrideCommand('type', commands.onEnter);
43 }
44 activateStatusDisplay(ctx);
45 activateHighlighting(ctx);
46 activateInlayHints(ctx);
47}
48
49export async function deactivate() {
50 await ctx?.client?.stop();
51}
diff --git a/editors/code/src/notifications/index.ts b/editors/code/src/notifications/index.ts
deleted file mode 100644
index 74c4c3563..000000000
--- a/editors/code/src/notifications/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
1import * as publishDecorations from './publish_decorations';
2
3export { publishDecorations };
diff --git a/editors/code/src/notifications/publish_decorations.ts b/editors/code/src/notifications/publish_decorations.ts
deleted file mode 100644
index f23e286ad..000000000
--- a/editors/code/src/notifications/publish_decorations.ts
+++ /dev/null
@@ -1,24 +0,0 @@
1import * as vscode from 'vscode';
2
3import { Decoration } from '../highlighting';
4import { Server } from '../server';
5
6export interface PublishDecorationsParams {
7 uri: string;
8 decorations: Decoration[];
9}
10
11export function handle(params: PublishDecorationsParams) {
12 const targetEditor = vscode.window.visibleTextEditors.find(editor => {
13 const unescapedUri = unescape(editor.document.uri.toString());
14 // Unescaped URI looks like:
15 // file:///c:/Workspace/ra-test/src/main.rs
16 return unescapedUri === params.uri;
17 });
18
19 if (!Server.config.highlightingOn || !targetEditor) {
20 return;
21 }
22
23 Server.highlighter.setHighlights(targetEditor, params.decorations);
24}
diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts
deleted file mode 100644
index 5ace1d0fa..000000000
--- a/editors/code/src/server.ts
+++ /dev/null
@@ -1,109 +0,0 @@
1import { lookpath } from 'lookpath';
2import { homedir, platform } from 'os';
3import * as lc from 'vscode-languageclient';
4
5import { window, workspace } from 'vscode';
6import { Config } from './config';
7import { Highlighter } from './highlighting';
8
9function expandPathResolving(path: string) {
10 if (path.startsWith('~/')) {
11 return path.replace('~', homedir());
12 }
13 return path;
14}
15
16export class Server {
17 public static highlighter = new Highlighter();
18 public static config = new Config();
19 public static client: lc.LanguageClient;
20
21 public static async start(
22 notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]>,
23 ) {
24 // '.' Is the fallback if no folder is open
25 // TODO?: Workspace folders support Uri's (eg: file://test.txt). It might be a good idea to test if the uri points to a file.
26 let folder: string = '.';
27 if (workspace.workspaceFolders !== undefined) {
28 folder = workspace.workspaceFolders[0].uri.fsPath.toString();
29 }
30
31 const command = expandPathResolving(this.config.raLspServerPath);
32 // FIXME: remove check when the following issue is fixed:
33 // https://github.com/otiai10/lookpath/issues/4
34 if (platform() !== 'win32') {
35 if (!(await lookpath(command))) {
36 throw new Error(
37 `Cannot find rust-analyzer server \`${command}\` in PATH.`,
38 );
39 }
40 }
41 const run: lc.Executable = {
42 command,
43 options: { cwd: folder },
44 };
45 const serverOptions: lc.ServerOptions = {
46 run,
47 debug: run,
48 };
49 const traceOutputChannel = window.createOutputChannel(
50 'Rust Analyzer Language Server Trace',
51 );
52 const clientOptions: lc.LanguageClientOptions = {
53 documentSelector: [{ scheme: 'file', language: 'rust' }],
54 initializationOptions: {
55 publishDecorations: true,
56 lruCapacity: Server.config.lruCapacity,
57 maxInlayHintLength: Server.config.maxInlayHintLength,
58 excludeGlobs: Server.config.excludeGlobs,
59 useClientWatching: Server.config.useClientWatching,
60 featureFlags: Server.config.featureFlags,
61 withSysroot: Server.config.withSysroot,
62 cargoFeatures: Server.config.cargoFeatures,
63 },
64 traceOutputChannel,
65 };
66
67 Server.client = new lc.LanguageClient(
68 'rust-analyzer',
69 'Rust Analyzer Language Server',
70 serverOptions,
71 clientOptions,
72 );
73 // HACK: This is an awful way of filtering out the decorations notifications
74 // However, pending proper support, this is the most effecitve approach
75 // Proper support for this would entail a change to vscode-languageclient to allow not notifying on certain messages
76 // Or the ability to disable the serverside component of highlighting (but this means that to do tracing we need to disable hihlighting)
77 // This also requires considering our settings strategy, which is work which needs doing
78 // @ts-ignore The tracer is private to vscode-languageclient, but we need access to it to not log publishDecorations requests
79 Server.client._tracer = {
80 log: (messageOrDataObject: string | any, data?: string) => {
81 if (typeof messageOrDataObject === 'string') {
82 if (
83 messageOrDataObject.includes(
84 'rust-analyzer/publishDecorations',
85 ) ||
86 messageOrDataObject.includes(
87 'rust-analyzer/decorationsRequest',
88 )
89 ) {
90 // Don't log publish decorations requests
91 } else {
92 // @ts-ignore This is just a utility function
93 Server.client.logTrace(messageOrDataObject, data);
94 }
95 } else {
96 // @ts-ignore
97 Server.client.logObjectTrace(messageOrDataObject);
98 }
99 },
100 };
101 Server.client.registerProposedFeatures();
102 Server.client.onReady().then(() => {
103 for (const [type, handler] of notificationHandlers) {
104 Server.client.onNotification(type, handler);
105 }
106 });
107 Server.client.start();
108 }
109}
diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/source_change.ts
index 8167398b1..a336269ba 100644
--- a/editors/code/src/commands/apply_source_change.ts
+++ b/editors/code/src/source_change.ts
@@ -1,7 +1,7 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3 3
4import { Server } from '../server'; 4import { Ctx } from './ctx';
5 5
6export interface SourceChange { 6export interface SourceChange {
7 label: string; 7 label: string;
@@ -9,8 +9,11 @@ export interface SourceChange {
9 cursorPosition?: lc.TextDocumentPositionParams; 9 cursorPosition?: lc.TextDocumentPositionParams;
10} 10}
11 11
12export async function handle(change: SourceChange) { 12export async function applySourceChange(ctx: Ctx, change: SourceChange) {
13 const wsEdit = Server.client.protocol2CodeConverter.asWorkspaceEdit( 13 const client = ctx.client;
14 if (!client) return;
15
16 const wsEdit = client.protocol2CodeConverter.asWorkspaceEdit(
14 change.workspaceEdit, 17 change.workspaceEdit,
15 ); 18 );
16 let created; 19 let created;
@@ -32,10 +35,10 @@ export async function handle(change: SourceChange) {
32 const doc = await vscode.workspace.openTextDocument(toOpenUri); 35 const doc = await vscode.workspace.openTextDocument(toOpenUri);
33 await vscode.window.showTextDocument(doc); 36 await vscode.window.showTextDocument(doc);
34 } else if (toReveal) { 37 } else if (toReveal) {
35 const uri = Server.client.protocol2CodeConverter.asUri( 38 const uri = client.protocol2CodeConverter.asUri(
36 toReveal.textDocument.uri, 39 toReveal.textDocument.uri,
37 ); 40 );
38 const position = Server.client.protocol2CodeConverter.asPosition( 41 const position = client.protocol2CodeConverter.asPosition(
39 toReveal.position, 42 toReveal.position,
40 ); 43 );
41 const editor = vscode.window.activeTextEditor; 44 const editor = vscode.window.activeTextEditor;
diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts
new file mode 100644
index 000000000..08cdc8bdf
--- /dev/null
+++ b/editors/code/src/status_display.ts
@@ -0,0 +1,115 @@
1import * as vscode from 'vscode';
2
3import { Ctx } from './ctx';
4
5const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
6
7export function activateStatusDisplay(ctx: Ctx) {
8 const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions.command);
9 ctx.pushCleanup(statusDisplay);
10 ctx.onDidRestart(client => {
11 client.onNotification('$/progress', params => statusDisplay.handleProgressNotification(params));
12 });
13}
14
15class StatusDisplay implements vscode.Disposable {
16 packageName?: string;
17
18 private i = 0;
19 private statusBarItem: vscode.StatusBarItem;
20 private command: string;
21 private timer?: NodeJS.Timeout;
22
23 constructor(command: string) {
24 this.statusBarItem = vscode.window.createStatusBarItem(
25 vscode.StatusBarAlignment.Left,
26 10,
27 );
28 this.command = command;
29 this.statusBarItem.hide();
30 }
31
32 show() {
33 this.packageName = undefined;
34
35 this.timer =
36 this.timer ||
37 setInterval(() => {
38 if (this.packageName) {
39 this.statusBarItem!.text = `cargo ${this.command} [${
40 this.packageName
41 }] ${this.frame()}`;
42 } else {
43 this.statusBarItem!.text = `cargo ${
44 this.command
45 } ${this.frame()}`;
46 }
47 }, 300);
48
49 this.statusBarItem.show();
50 }
51
52 hide() {
53 if (this.timer) {
54 clearInterval(this.timer);
55 this.timer = undefined;
56 }
57
58 this.statusBarItem.hide();
59 }
60
61 dispose() {
62 if (this.timer) {
63 clearInterval(this.timer);
64 this.timer = undefined;
65 }
66
67 this.statusBarItem.dispose();
68 }
69
70 handleProgressNotification(params: ProgressParams) {
71 const { token, value } = params;
72 if (token !== 'rustAnalyzer/cargoWatcher') {
73 return;
74 }
75
76 switch (value.kind) {
77 case 'begin':
78 this.show();
79 break;
80
81 case 'report':
82 if (value.message) {
83 this.packageName = value.message;
84 }
85 break;
86
87 case 'end':
88 this.hide();
89 break;
90 }
91 }
92
93 private frame() {
94 return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)];
95 }
96}
97
98// FIXME: Replace this once vscode-languageclient is updated to LSP 3.15
99interface ProgressParams {
100 token: string;
101 value: WorkDoneProgress;
102}
103
104enum WorkDoneProgressKind {
105 Begin = 'begin',
106 Report = 'report',
107 End = 'end',
108}
109
110interface WorkDoneProgress {
111 kind: WorkDoneProgressKind;
112 message?: string;
113 cancelable?: boolean;
114 percentage?: string;
115}
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json b/editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json
deleted file mode 100644
index d874e99bc..000000000
--- a/editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json
+++ /dev/null
@@ -1,110 +0,0 @@
1{
2 "message": "this argument is passed by reference, but would be more efficient if passed by value",
3 "code": {
4 "code": "clippy::trivially_copy_pass_by_ref",
5 "explanation": null
6 },
7 "level": "warning",
8 "spans": [
9 {
10 "file_name": "compiler/mir/tagset.rs",
11 "byte_start": 941,
12 "byte_end": 946,
13 "line_start": 42,
14 "line_end": 42,
15 "column_start": 24,
16 "column_end": 29,
17 "is_primary": true,
18 "text": [
19 {
20 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
21 "highlight_start": 24,
22 "highlight_end": 29
23 }
24 ],
25 "label": null,
26 "suggested_replacement": null,
27 "suggestion_applicability": null,
28 "expansion": null
29 }
30 ],
31 "children": [
32 {
33 "message": "lint level defined here",
34 "code": null,
35 "level": "note",
36 "spans": [
37 {
38 "file_name": "compiler/lib.rs",
39 "byte_start": 8,
40 "byte_end": 19,
41 "line_start": 1,
42 "line_end": 1,
43 "column_start": 9,
44 "column_end": 20,
45 "is_primary": true,
46 "text": [
47 {
48 "text": "#![warn(clippy::all)]",
49 "highlight_start": 9,
50 "highlight_end": 20
51 }
52 ],
53 "label": null,
54 "suggested_replacement": null,
55 "suggestion_applicability": null,
56 "expansion": null
57 }
58 ],
59 "children": [],
60 "rendered": null
61 },
62 {
63 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
64 "code": null,
65 "level": "note",
66 "spans": [],
67 "children": [],
68 "rendered": null
69 },
70 {
71 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
72 "code": null,
73 "level": "help",
74 "spans": [],
75 "children": [],
76 "rendered": null
77 },
78 {
79 "message": "consider passing by value instead",
80 "code": null,
81 "level": "help",
82 "spans": [
83 {
84 "file_name": "compiler/mir/tagset.rs",
85 "byte_start": 941,
86 "byte_end": 946,
87 "line_start": 42,
88 "line_end": 42,
89 "column_start": 24,
90 "column_end": 29,
91 "is_primary": true,
92 "text": [
93 {
94 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
95 "highlight_start": 24,
96 "highlight_end": 29
97 }
98 ],
99 "label": null,
100 "suggested_replacement": "self",
101 "suggestion_applicability": "Unspecified",
102 "expansion": null
103 }
104 ],
105 "children": [],
106 "rendered": null
107 }
108 ],
109 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
110}
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json
deleted file mode 100644
index ea5c976d1..000000000
--- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json
+++ /dev/null
@@ -1,42 +0,0 @@
1{
2 "message": "method `next` has an incompatible type for trait",
3 "code": {
4 "code": "E0053",
5 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
6 },
7 "level": "error",
8 "spans": [
9 {
10 "file_name": "compiler/ty/list_iter.rs",
11 "byte_start": 1307,
12 "byte_end": 1350,
13 "line_start": 52,
14 "line_end": 52,
15 "column_start": 5,
16 "column_end": 48,
17 "is_primary": true,
18 "text": [
19 {
20 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
21 "highlight_start": 5,
22 "highlight_end": 48
23 }
24 ],
25 "label": "types differ in mutability",
26 "suggested_replacement": null,
27 "suggestion_applicability": null,
28 "expansion": null
29 }
30 ],
31 "children": [
32 {
33 "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`",
34 "code": null,
35 "level": "note",
36 "spans": [],
37 "children": [],
38 "rendered": null
39 }
40 ],
41 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
42}
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json
deleted file mode 100644
index 3154d1098..000000000
--- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json
+++ /dev/null
@@ -1,114 +0,0 @@
1{
2 "message": "this function takes 2 parameters but 3 parameters were supplied",
3 "code": {
4 "code": "E0061",
5 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
6 },
7 "level": "error",
8 "spans": [
9 {
10 "file_name": "compiler/ty/select.rs",
11 "byte_start": 8787,
12 "byte_end": 9241,
13 "line_start": 219,
14 "line_end": 231,
15 "column_start": 5,
16 "column_end": 6,
17 "is_primary": false,
18 "text": [
19 {
20 "text": " pub fn add_evidence(",
21 "highlight_start": 5,
22 "highlight_end": 25
23 },
24 {
25 "text": " &mut self,",
26 "highlight_start": 1,
27 "highlight_end": 19
28 },
29 {
30 "text": " target_poly: &ty::Ref<ty::Poly>,",
31 "highlight_start": 1,
32 "highlight_end": 41
33 },
34 {
35 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
36 "highlight_start": 1,
37 "highlight_end": 43
38 },
39 {
40 "text": " ) {",
41 "highlight_start": 1,
42 "highlight_end": 8
43 },
44 {
45 "text": " match target_poly {",
46 "highlight_start": 1,
47 "highlight_end": 28
48 },
49 {
50 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
51 "highlight_start": 1,
52 "highlight_end": 81
53 },
54 {
55 "text": " ty::Ref::Fixed(target_ty) => {",
56 "highlight_start": 1,
57 "highlight_end": 43
58 },
59 {
60 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
61 "highlight_start": 1,
62 "highlight_end": 65
63 },
64 {
65 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
66 "highlight_start": 1,
67 "highlight_end": 76
68 },
69 {
70 "text": " }",
71 "highlight_start": 1,
72 "highlight_end": 14
73 },
74 {
75 "text": " }",
76 "highlight_start": 1,
77 "highlight_end": 10
78 },
79 {
80 "text": " }",
81 "highlight_start": 1,
82 "highlight_end": 6
83 }
84 ],
85 "label": "defined here",
86 "suggested_replacement": null,
87 "suggestion_applicability": null,
88 "expansion": null
89 },
90 {
91 "file_name": "compiler/ty/select.rs",
92 "byte_start": 4045,
93 "byte_end": 4057,
94 "line_start": 104,
95 "line_end": 104,
96 "column_start": 18,
97 "column_end": 30,
98 "is_primary": true,
99 "text": [
100 {
101 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
102 "highlight_start": 18,
103 "highlight_end": 30
104 }
105 ],
106 "label": "expected 2 parameters",
107 "suggested_replacement": null,
108 "suggestion_applicability": null,
109 "expansion": null
110 }
111 ],
112 "children": [],
113 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
114}
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json
deleted file mode 100644
index bfef33c7d..000000000
--- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json
+++ /dev/null
@@ -1,261 +0,0 @@
1{
2 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
3 "children": [
4 {
5 "children": [],
6 "code": null,
7 "level": "help",
8 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
9 "rendered": null,
10 "spans": []
11 }
12 ],
13 "code": {
14 "code": "E0277",
15 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
16 },
17 "level": "error",
18 "message": "can't compare `{integer}` with `&str`",
19 "spans": [
20 {
21 "byte_end": 155,
22 "byte_start": 153,
23 "column_end": 33,
24 "column_start": 31,
25 "expansion": {
26 "def_site_span": {
27 "byte_end": 940,
28 "byte_start": 0,
29 "column_end": 6,
30 "column_start": 1,
31 "expansion": null,
32 "file_name": "<::core::macros::assert_eq macros>",
33 "is_primary": false,
34 "label": null,
35 "line_end": 36,
36 "line_start": 1,
37 "suggested_replacement": null,
38 "suggestion_applicability": null,
39 "text": [
40 {
41 "highlight_end": 35,
42 "highlight_start": 1,
43 "text": "($ left : expr, $ right : expr) =>"
44 },
45 {
46 "highlight_end": 3,
47 "highlight_start": 1,
48 "text": "({"
49 },
50 {
51 "highlight_end": 33,
52 "highlight_start": 1,
53 "text": " match (& $ left, & $ right)"
54 },
55 {
56 "highlight_end": 7,
57 "highlight_start": 1,
58 "text": " {"
59 },
60 {
61 "highlight_end": 34,
62 "highlight_start": 1,
63 "text": " (left_val, right_val) =>"
64 },
65 {
66 "highlight_end": 11,
67 "highlight_start": 1,
68 "text": " {"
69 },
70 {
71 "highlight_end": 46,
72 "highlight_start": 1,
73 "text": " if ! (* left_val == * right_val)"
74 },
75 {
76 "highlight_end": 15,
77 "highlight_start": 1,
78 "text": " {"
79 },
80 {
81 "highlight_end": 25,
82 "highlight_start": 1,
83 "text": " panic !"
84 },
85 {
86 "highlight_end": 57,
87 "highlight_start": 1,
88 "text": " (r#\"assertion failed: `(left == right)`"
89 },
90 {
91 "highlight_end": 16,
92 "highlight_start": 1,
93 "text": " left: `{:?}`,"
94 },
95 {
96 "highlight_end": 18,
97 "highlight_start": 1,
98 "text": " right: `{:?}`\"#,"
99 },
100 {
101 "highlight_end": 47,
102 "highlight_start": 1,
103 "text": " & * left_val, & * right_val)"
104 },
105 {
106 "highlight_end": 15,
107 "highlight_start": 1,
108 "text": " }"
109 },
110 {
111 "highlight_end": 11,
112 "highlight_start": 1,
113 "text": " }"
114 },
115 {
116 "highlight_end": 7,
117 "highlight_start": 1,
118 "text": " }"
119 },
120 {
121 "highlight_end": 42,
122 "highlight_start": 1,
123 "text": " }) ; ($ left : expr, $ right : expr,) =>"
124 },
125 {
126 "highlight_end": 49,
127 "highlight_start": 1,
128 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
129 },
130 {
131 "highlight_end": 53,
132 "highlight_start": 1,
133 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
134 },
135 {
136 "highlight_end": 3,
137 "highlight_start": 1,
138 "text": "({"
139 },
140 {
141 "highlight_end": 37,
142 "highlight_start": 1,
143 "text": " match (& ($ left), & ($ right))"
144 },
145 {
146 "highlight_end": 7,
147 "highlight_start": 1,
148 "text": " {"
149 },
150 {
151 "highlight_end": 34,
152 "highlight_start": 1,
153 "text": " (left_val, right_val) =>"
154 },
155 {
156 "highlight_end": 11,
157 "highlight_start": 1,
158 "text": " {"
159 },
160 {
161 "highlight_end": 46,
162 "highlight_start": 1,
163 "text": " if ! (* left_val == * right_val)"
164 },
165 {
166 "highlight_end": 15,
167 "highlight_start": 1,
168 "text": " {"
169 },
170 {
171 "highlight_end": 25,
172 "highlight_start": 1,
173 "text": " panic !"
174 },
175 {
176 "highlight_end": 57,
177 "highlight_start": 1,
178 "text": " (r#\"assertion failed: `(left == right)`"
179 },
180 {
181 "highlight_end": 16,
182 "highlight_start": 1,
183 "text": " left: `{:?}`,"
184 },
185 {
186 "highlight_end": 22,
187 "highlight_start": 1,
188 "text": " right: `{:?}`: {}\"#,"
189 },
190 {
191 "highlight_end": 72,
192 "highlight_start": 1,
193 "text": " & * left_val, & * right_val, $ crate :: format_args !"
194 },
195 {
196 "highlight_end": 33,
197 "highlight_start": 1,
198 "text": " ($ ($ arg) +))"
199 },
200 {
201 "highlight_end": 15,
202 "highlight_start": 1,
203 "text": " }"
204 },
205 {
206 "highlight_end": 11,
207 "highlight_start": 1,
208 "text": " }"
209 },
210 {
211 "highlight_end": 7,
212 "highlight_start": 1,
213 "text": " }"
214 },
215 {
216 "highlight_end": 6,
217 "highlight_start": 1,
218 "text": " }) ;"
219 }
220 ]
221 },
222 "macro_decl_name": "assert_eq!",
223 "span": {
224 "byte_end": 38,
225 "byte_start": 16,
226 "column_end": 27,
227 "column_start": 5,
228 "expansion": null,
229 "file_name": "src/main.rs",
230 "is_primary": false,
231 "label": null,
232 "line_end": 2,
233 "line_start": 2,
234 "suggested_replacement": null,
235 "suggestion_applicability": null,
236 "text": [
237 {
238 "highlight_end": 27,
239 "highlight_start": 5,
240 "text": " assert_eq!(1, \"love\");"
241 }
242 ]
243 }
244 },
245 "file_name": "<::core::macros::assert_eq macros>",
246 "is_primary": true,
247 "label": "no implementation for `{integer} == &str`",
248 "line_end": 7,
249 "line_start": 7,
250 "suggested_replacement": null,
251 "suggestion_applicability": null,
252 "text": [
253 {
254 "highlight_end": 33,
255 "highlight_start": 31,
256 "text": " if ! (* left_val == * right_val)"
257 }
258 ]
259 }
260 ]
261}
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json
deleted file mode 100644
index fb23824a3..000000000
--- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json
+++ /dev/null
@@ -1,33 +0,0 @@
1{
2 "message": "mismatched types",
3 "code": {
4 "code": "E0308",
5 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
6 },
7 "level": "error",
8 "spans": [
9 {
10 "file_name": "runtime/compiler_support.rs",
11 "byte_start": 1589,
12 "byte_end": 1594,
13 "line_start": 48,
14 "line_end": 48,
15 "column_start": 65,
16 "column_end": 70,
17 "is_primary": true,
18 "text": [
19 {
20 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
21 "highlight_start": 65,
22 "highlight_end": 70
23 }
24 ],
25 "label": "expected usize, found u32",
26 "suggested_replacement": null,
27 "suggestion_applicability": null,
28 "expansion": null
29 }
30 ],
31 "children": [],
32 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
33}
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json b/editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json
deleted file mode 100644
index d1e2be722..000000000
--- a/editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json
+++ /dev/null
@@ -1,72 +0,0 @@
1{
2 "message": "unused variable: `foo`",
3 "code": {
4 "code": "unused_variables",
5 "explanation": null
6 },
7 "level": "warning",
8 "spans": [
9 {
10 "file_name": "driver/subcommand/repl.rs",
11 "byte_start": 9228,
12 "byte_end": 9231,
13 "line_start": 291,
14 "line_end": 291,
15 "column_start": 9,
16 "column_end": 12,
17 "is_primary": true,
18 "text": [
19 {
20 "text": " let foo = 42;",
21 "highlight_start": 9,
22 "highlight_end": 12
23 }
24 ],
25 "label": null,
26 "suggested_replacement": null,
27 "suggestion_applicability": null,
28 "expansion": null
29 }
30 ],
31 "children": [
32 {
33 "message": "#[warn(unused_variables)] on by default",
34 "code": null,
35 "level": "note",
36 "spans": [],
37 "children": [],
38 "rendered": null
39 },
40 {
41 "message": "consider prefixing with an underscore",
42 "code": null,
43 "level": "help",
44 "spans": [
45 {
46 "file_name": "driver/subcommand/repl.rs",
47 "byte_start": 9228,
48 "byte_end": 9231,
49 "line_start": 291,
50 "line_end": 291,
51 "column_start": 9,
52 "column_end": 12,
53 "is_primary": true,
54 "text": [
55 {
56 "text": " let foo = 42;",
57 "highlight_start": 9,
58 "highlight_end": 12
59 }
60 ],
61 "label": null,
62 "suggested_replacement": "_foo",
63 "suggestion_applicability": "MachineApplicable",
64 "expansion": null
65 }
66 ],
67 "children": [],
68 "rendered": null
69 }
70 ],
71 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
72}
diff --git a/editors/code/src/test/runTest.ts b/editors/code/src/test/runTest.ts
deleted file mode 100644
index d880d47df..000000000
--- a/editors/code/src/test/runTest.ts
+++ /dev/null
@@ -1,22 +0,0 @@
1import * as path from 'path';
2
3import { runTests } from 'vscode-test';
4
5async function main() {
6 try {
7 // The folder containing the Extension Manifest package.json
8 // Passed to `--extensionDevelopmentPath`
9 const extensionDevelopmentPath = path.resolve(__dirname, '../../');
10
11 // The path to the extension test runner script
12 // Passed to --extensionTestsPath
13 const extensionTestsPath = path.resolve(__dirname, './utils/index');
14
15 // Download VS Code, unzip it and run the integration test
16 await runTests({ extensionDevelopmentPath, extensionTestsPath });
17 } catch (err) {
18 process.exit(1);
19 }
20}
21
22main();
diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts
deleted file mode 100644
index 2b25eb705..000000000
--- a/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts
+++ /dev/null
@@ -1,134 +0,0 @@
1import * as assert from 'assert';
2import * as vscode from 'vscode';
3
4import { SuggestionApplicability } from '../../../utils/diagnostics/rust';
5import SuggestedFix from '../../../utils/diagnostics/SuggestedFix';
6
7const location1 = new vscode.Location(
8 vscode.Uri.file('/file/1'),
9 new vscode.Range(new vscode.Position(1, 2), new vscode.Position(3, 4)),
10);
11
12const location2 = new vscode.Location(
13 vscode.Uri.file('/file/2'),
14 new vscode.Range(new vscode.Position(5, 6), new vscode.Position(7, 8)),
15);
16
17describe('SuggestedFix', () => {
18 describe('isEqual', () => {
19 it('should treat identical instances as equal', () => {
20 const suggestion1 = new SuggestedFix(
21 'Replace me!',
22 location1,
23 'With this!',
24 );
25
26 const suggestion2 = new SuggestedFix(
27 'Replace me!',
28 location1,
29 'With this!',
30 );
31
32 assert(suggestion1.isEqual(suggestion2));
33 });
34
35 it('should treat instances with different titles as inequal', () => {
36 const suggestion1 = new SuggestedFix(
37 'Replace me!',
38 location1,
39 'With this!',
40 );
41
42 const suggestion2 = new SuggestedFix(
43 'Not the same title!',
44 location1,
45 'With this!',
46 );
47
48 assert(!suggestion1.isEqual(suggestion2));
49 });
50
51 it('should treat instances with different replacements as inequal', () => {
52 const suggestion1 = new SuggestedFix(
53 'Replace me!',
54 location1,
55 'With this!',
56 );
57
58 const suggestion2 = new SuggestedFix(
59 'Replace me!',
60 location1,
61 'With something else!',
62 );
63
64 assert(!suggestion1.isEqual(suggestion2));
65 });
66
67 it('should treat instances with different locations as inequal', () => {
68 const suggestion1 = new SuggestedFix(
69 'Replace me!',
70 location1,
71 'With this!',
72 );
73
74 const suggestion2 = new SuggestedFix(
75 'Replace me!',
76 location2,
77 'With this!',
78 );
79
80 assert(!suggestion1.isEqual(suggestion2));
81 });
82
83 it('should treat instances with different applicability as inequal', () => {
84 const suggestion1 = new SuggestedFix(
85 'Replace me!',
86 location1,
87 'With this!',
88 SuggestionApplicability.MachineApplicable,
89 );
90
91 const suggestion2 = new SuggestedFix(
92 'Replace me!',
93 location2,
94 'With this!',
95 SuggestionApplicability.HasPlaceholders,
96 );
97
98 assert(!suggestion1.isEqual(suggestion2));
99 });
100 });
101
102 describe('toCodeAction', () => {
103 it('should map a simple suggestion', () => {
104 const suggestion = new SuggestedFix(
105 'Replace me!',
106 location1,
107 'With this!',
108 );
109
110 const codeAction = suggestion.toCodeAction();
111 assert.strictEqual(codeAction.kind, vscode.CodeActionKind.QuickFix);
112 assert.strictEqual(codeAction.title, 'Replace me!');
113 assert.strictEqual(codeAction.isPreferred, false);
114
115 const edit = codeAction.edit;
116 if (!edit) {
117 assert.fail('Code Action edit unexpectedly missing');
118 return;
119 }
120
121 const editEntries = edit.entries();
122 assert.strictEqual(editEntries.length, 1);
123
124 const [[editUri, textEdits]] = editEntries;
125 assert.strictEqual(editUri.toString(), location1.uri.toString());
126
127 assert.strictEqual(textEdits.length, 1);
128 const [textEdit] = textEdits;
129
130 assert(textEdit.range.isEqual(location1.range));
131 assert.strictEqual(textEdit.newText, 'With this!');
132 });
133 });
134});
diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts
deleted file mode 100644
index ef09013f4..000000000
--- a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts
+++ /dev/null
@@ -1,127 +0,0 @@
1import * as assert from 'assert';
2import * as vscode from 'vscode';
3
4import SuggestedFix from '../../../utils/diagnostics/SuggestedFix';
5import SuggestedFixCollection from '../../../utils/diagnostics/SuggestedFixCollection';
6
7const uri1 = vscode.Uri.file('/file/1');
8const uri2 = vscode.Uri.file('/file/2');
9
10const mockDocument1 = ({
11 uri: uri1,
12} as unknown) as vscode.TextDocument;
13
14const mockDocument2 = ({
15 uri: uri2,
16} as unknown) as vscode.TextDocument;
17
18const range1 = new vscode.Range(
19 new vscode.Position(1, 2),
20 new vscode.Position(3, 4),
21);
22const range2 = new vscode.Range(
23 new vscode.Position(5, 6),
24 new vscode.Position(7, 8),
25);
26
27const diagnostic1 = new vscode.Diagnostic(range1, 'First diagnostic');
28const diagnostic2 = new vscode.Diagnostic(range2, 'Second diagnostic');
29
30// This is a mutable object so return a fresh instance every time
31function suggestion1(): SuggestedFix {
32 return new SuggestedFix(
33 'Replace me!',
34 new vscode.Location(uri1, range1),
35 'With this!',
36 );
37}
38
39describe('SuggestedFixCollection', () => {
40 it('should add a suggestion then return it as a code action', () => {
41 const suggestedFixes = new SuggestedFixCollection();
42 suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1);
43
44 // Specify the document and range that exactly matches
45 const codeActions = suggestedFixes.provideCodeActions(
46 mockDocument1,
47 range1,
48 );
49
50 assert.strictEqual(codeActions.length, 1);
51 const [codeAction] = codeActions;
52 assert.strictEqual(codeAction.title, suggestion1().title);
53
54 const { diagnostics } = codeAction;
55 if (!diagnostics) {
56 assert.fail('Diagnostics unexpectedly missing');
57 return;
58 }
59
60 assert.strictEqual(diagnostics.length, 1);
61 assert.strictEqual(diagnostics[0], diagnostic1);
62 });
63
64 it('should not return code actions for different ranges', () => {
65 const suggestedFixes = new SuggestedFixCollection();
66 suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1);
67
68 const codeActions = suggestedFixes.provideCodeActions(
69 mockDocument1,
70 range2,
71 );
72
73 assert(!codeActions || codeActions.length === 0);
74 });
75
76 it('should not return code actions for different documents', () => {
77 const suggestedFixes = new SuggestedFixCollection();
78 suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1);
79
80 const codeActions = suggestedFixes.provideCodeActions(
81 mockDocument2,
82 range1,
83 );
84
85 assert(!codeActions || codeActions.length === 0);
86 });
87
88 it('should not return code actions that have been cleared', () => {
89 const suggestedFixes = new SuggestedFixCollection();
90 suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1);
91 suggestedFixes.clear();
92
93 const codeActions = suggestedFixes.provideCodeActions(
94 mockDocument1,
95 range1,
96 );
97
98 assert(!codeActions || codeActions.length === 0);
99 });
100
101 it('should merge identical suggestions together', () => {
102 const suggestedFixes = new SuggestedFixCollection();
103
104 // Add the same suggestion for two diagnostics
105 suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1);
106 suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic2);
107
108 const codeActions = suggestedFixes.provideCodeActions(
109 mockDocument1,
110 range1,
111 );
112
113 assert.strictEqual(codeActions.length, 1);
114 const [codeAction] = codeActions;
115 const { diagnostics } = codeAction;
116
117 if (!diagnostics) {
118 assert.fail('Diagnostics unexpectedly missing');
119 return;
120 }
121
122 // We should be associated with both diagnostics
123 assert.strictEqual(diagnostics.length, 2);
124 assert.strictEqual(diagnostics[0], diagnostic1);
125 assert.strictEqual(diagnostics[1], diagnostic2);
126 });
127});
diff --git a/editors/code/src/test/utils/diagnotics/rust.test.ts b/editors/code/src/test/utils/diagnotics/rust.test.ts
deleted file mode 100644
index 358325cc8..000000000
--- a/editors/code/src/test/utils/diagnotics/rust.test.ts
+++ /dev/null
@@ -1,236 +0,0 @@
1import * as assert from 'assert';
2import * as fs from 'fs';
3import * as vscode from 'vscode';
4
5import {
6 MappedRustDiagnostic,
7 mapRustDiagnosticToVsCode,
8 RustDiagnostic,
9 SuggestionApplicability,
10} from '../../../utils/diagnostics/rust';
11
12function loadDiagnosticFixture(name: string): RustDiagnostic {
13 const jsonText = fs
14 .readFileSync(
15 // We're actually in our JavaScript output directory, climb out
16 `${__dirname}/../../../../src/test/fixtures/rust-diagnostics/${name}.json`,
17 )
18 .toString();
19
20 return JSON.parse(jsonText);
21}
22
23function mapFixtureToVsCode(name: string): MappedRustDiagnostic {
24 const rd = loadDiagnosticFixture(name);
25 const mapResult = mapRustDiagnosticToVsCode(rd);
26
27 if (!mapResult) {
28 return assert.fail('Mapping unexpectedly failed');
29 }
30 return mapResult;
31}
32
33describe('mapRustDiagnosticToVsCode', () => {
34 it('should map an incompatible type for trait error', () => {
35 const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
36 'error/E0053',
37 );
38
39 assert.strictEqual(
40 diagnostic.severity,
41 vscode.DiagnosticSeverity.Error,
42 );
43 assert.strictEqual(diagnostic.source, 'rustc');
44 assert.strictEqual(
45 diagnostic.message,
46 [
47 `method \`next\` has an incompatible type for trait`,
48 `expected type \`fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>\``,
49 ` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>\``,
50 ].join('\n'),
51 );
52 assert.strictEqual(diagnostic.code, 'E0053');
53 assert.deepStrictEqual(diagnostic.tags, []);
54
55 // No related information
56 assert.deepStrictEqual(diagnostic.relatedInformation, []);
57
58 // There are no suggested fixes
59 assert.strictEqual(suggestedFixes.length, 0);
60 });
61
62 it('should map an unused variable warning', () => {
63 const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
64 'warning/unused_variables',
65 );
66
67 assert.strictEqual(
68 diagnostic.severity,
69 vscode.DiagnosticSeverity.Warning,
70 );
71 assert.strictEqual(
72 diagnostic.message,
73 [
74 'unused variable: `foo`',
75 '#[warn(unused_variables)] on by default',
76 ].join('\n'),
77 );
78 assert.strictEqual(diagnostic.code, 'unused_variables');
79 assert.strictEqual(diagnostic.source, 'rustc');
80 assert.deepStrictEqual(diagnostic.tags, [
81 vscode.DiagnosticTag.Unnecessary,
82 ]);
83
84 // No related information
85 assert.deepStrictEqual(diagnostic.relatedInformation, []);
86
87 // One suggested fix available to prefix the variable
88 assert.strictEqual(suggestedFixes.length, 1);
89 const [suggestedFix] = suggestedFixes;
90 assert.strictEqual(
91 suggestedFix.title,
92 'consider prefixing with an underscore: `_foo`',
93 );
94 assert.strictEqual(
95 suggestedFix.applicability,
96 SuggestionApplicability.MachineApplicable,
97 );
98 });
99
100 it('should map a wrong number of parameters error', () => {
101 const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
102 'error/E0061',
103 );
104
105 assert.strictEqual(
106 diagnostic.severity,
107 vscode.DiagnosticSeverity.Error,
108 );
109 assert.strictEqual(
110 diagnostic.message,
111 [
112 'this function takes 2 parameters but 3 parameters were supplied',
113 'expected 2 parameters',
114 ].join('\n'),
115 );
116 assert.strictEqual(diagnostic.code, 'E0061');
117 assert.strictEqual(diagnostic.source, 'rustc');
118 assert.deepStrictEqual(diagnostic.tags, []);
119
120 // One related information for the original definition
121 const relatedInformation = diagnostic.relatedInformation;
122 if (!relatedInformation) {
123 assert.fail('Related information unexpectedly undefined');
124 return;
125 }
126 assert.strictEqual(relatedInformation.length, 1);
127 const [related] = relatedInformation;
128 assert.strictEqual(related.message, 'defined here');
129
130 // There are no suggested fixes
131 assert.strictEqual(suggestedFixes.length, 0);
132 });
133
134 it('should map a Clippy copy pass by ref warning', () => {
135 const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
136 'clippy/trivially_copy_pass_by_ref',
137 );
138
139 assert.strictEqual(
140 diagnostic.severity,
141 vscode.DiagnosticSeverity.Warning,
142 );
143 assert.strictEqual(diagnostic.source, 'clippy');
144 assert.strictEqual(
145 diagnostic.message,
146 [
147 'this argument is passed by reference, but would be more efficient if passed by value',
148 '#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]',
149 'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref',
150 ].join('\n'),
151 );
152 assert.strictEqual(diagnostic.code, 'trivially_copy_pass_by_ref');
153 assert.deepStrictEqual(diagnostic.tags, []);
154
155 // One related information for the lint definition
156 const relatedInformation = diagnostic.relatedInformation;
157 if (!relatedInformation) {
158 assert.fail('Related information unexpectedly undefined');
159 return;
160 }
161 assert.strictEqual(relatedInformation.length, 1);
162 const [related] = relatedInformation;
163 assert.strictEqual(related.message, 'lint level defined here');
164
165 // One suggested fix to pass by value
166 assert.strictEqual(suggestedFixes.length, 1);
167 const [suggestedFix] = suggestedFixes;
168 assert.strictEqual(
169 suggestedFix.title,
170 'consider passing by value instead: `self`',
171 );
172 // Clippy does not mark this with any applicability
173 assert.strictEqual(
174 suggestedFix.applicability,
175 SuggestionApplicability.Unspecified,
176 );
177 });
178
179 it('should map a mismatched type error', () => {
180 const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
181 'error/E0308',
182 );
183
184 assert.strictEqual(
185 diagnostic.severity,
186 vscode.DiagnosticSeverity.Error,
187 );
188 assert.strictEqual(
189 diagnostic.message,
190 ['mismatched types', 'expected usize, found u32'].join('\n'),
191 );
192 assert.strictEqual(diagnostic.code, 'E0308');
193 assert.strictEqual(diagnostic.source, 'rustc');
194 assert.deepStrictEqual(diagnostic.tags, []);
195
196 // No related information
197 assert.deepStrictEqual(diagnostic.relatedInformation, []);
198
199 // There are no suggested fixes
200 assert.strictEqual(suggestedFixes.length, 0);
201 });
202
203 it('should map a macro invocation location to normal file path', () => {
204 const { location, diagnostic, suggestedFixes } = mapFixtureToVsCode(
205 'error/E0277',
206 );
207
208 assert.strictEqual(
209 diagnostic.severity,
210 vscode.DiagnosticSeverity.Error,
211 );
212 assert.strictEqual(
213 diagnostic.message,
214 [
215 "can't compare `{integer}` with `&str`",
216 'the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`',
217 ].join('\n'),
218 );
219 assert.strictEqual(diagnostic.code, 'E0277');
220 assert.strictEqual(diagnostic.source, 'rustc');
221 assert.deepStrictEqual(diagnostic.tags, []);
222
223 // No related information
224 assert.deepStrictEqual(diagnostic.relatedInformation, []);
225
226 // There are no suggested fixes
227 assert.strictEqual(suggestedFixes.length, 0);
228
229 // The file url should be normal file
230 // Ignore the first part because it depends on vs workspace location
231 assert.strictEqual(
232 location.uri.path.substr(-'src/main.rs'.length),
233 'src/main.rs',
234 );
235 });
236});
diff --git a/editors/code/src/test/utils/diagnotics/vscode.test.ts b/editors/code/src/test/utils/diagnotics/vscode.test.ts
deleted file mode 100644
index 4944dd032..000000000
--- a/editors/code/src/test/utils/diagnotics/vscode.test.ts
+++ /dev/null
@@ -1,98 +0,0 @@
1import * as assert from 'assert';
2import * as vscode from 'vscode';
3
4import { areDiagnosticsEqual } from '../../../utils/diagnostics/vscode';
5
6const range1 = new vscode.Range(
7 new vscode.Position(1, 2),
8 new vscode.Position(3, 4),
9);
10
11const range2 = new vscode.Range(
12 new vscode.Position(5, 6),
13 new vscode.Position(7, 8),
14);
15
16describe('areDiagnosticsEqual', () => {
17 it('should treat identical diagnostics as equal', () => {
18 const diagnostic1 = new vscode.Diagnostic(
19 range1,
20 'Hello, world!',
21 vscode.DiagnosticSeverity.Error,
22 );
23
24 const diagnostic2 = new vscode.Diagnostic(
25 range1,
26 'Hello, world!',
27 vscode.DiagnosticSeverity.Error,
28 );
29
30 assert(areDiagnosticsEqual(diagnostic1, diagnostic2));
31 });
32
33 it('should treat diagnostics with different sources as inequal', () => {
34 const diagnostic1 = new vscode.Diagnostic(
35 range1,
36 'Hello, world!',
37 vscode.DiagnosticSeverity.Error,
38 );
39 diagnostic1.source = 'rustc';
40
41 const diagnostic2 = new vscode.Diagnostic(
42 range1,
43 'Hello, world!',
44 vscode.DiagnosticSeverity.Error,
45 );
46 diagnostic2.source = 'clippy';
47
48 assert(!areDiagnosticsEqual(diagnostic1, diagnostic2));
49 });
50
51 it('should treat diagnostics with different ranges as inequal', () => {
52 const diagnostic1 = new vscode.Diagnostic(
53 range1,
54 'Hello, world!',
55 vscode.DiagnosticSeverity.Error,
56 );
57
58 const diagnostic2 = new vscode.Diagnostic(
59 range2,
60 'Hello, world!',
61 vscode.DiagnosticSeverity.Error,
62 );
63
64 assert(!areDiagnosticsEqual(diagnostic1, diagnostic2));
65 });
66
67 it('should treat diagnostics with different messages as inequal', () => {
68 const diagnostic1 = new vscode.Diagnostic(
69 range1,
70 'Hello, world!',
71 vscode.DiagnosticSeverity.Error,
72 );
73
74 const diagnostic2 = new vscode.Diagnostic(
75 range1,
76 'Goodbye!, world!',
77 vscode.DiagnosticSeverity.Error,
78 );
79
80 assert(!areDiagnosticsEqual(diagnostic1, diagnostic2));
81 });
82
83 it('should treat diagnostics with different severities as inequal', () => {
84 const diagnostic1 = new vscode.Diagnostic(
85 range1,
86 'Hello, world!',
87 vscode.DiagnosticSeverity.Warning,
88 );
89
90 const diagnostic2 = new vscode.Diagnostic(
91 range1,
92 'Hello, world!',
93 vscode.DiagnosticSeverity.Error,
94 );
95
96 assert(!areDiagnosticsEqual(diagnostic1, diagnostic2));
97 });
98});
diff --git a/editors/code/src/test/utils/index.ts b/editors/code/src/test/utils/index.ts
deleted file mode 100644
index 9927daaf6..000000000
--- a/editors/code/src/test/utils/index.ts
+++ /dev/null
@@ -1,49 +0,0 @@
1//
2// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
3//
4// This file is providing the test runner to use when running extension tests.
5// By default the test runner in use is Mocha based.
6//
7// You can provide your own test runner if you want to override it by exporting
8// a function run(testRoot: string, clb: (error:Error) => void) that the extension
9// host can call to run the tests. The test runner is expected to use console.log
10// to report the results back to the caller. When the tests are finished, return
11// a possible error to the callback or null if none.
12
13import * as glob from 'glob';
14import * as Mocha from 'mocha';
15import * as path from 'path';
16
17export function run(): Promise<void> {
18 // Create the mocha test
19 const mocha = new Mocha({
20 ui: 'bdd',
21 });
22 mocha.useColors(true);
23
24 const testsRoot = __dirname;
25
26 return new Promise((c, e) => {
27 glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
28 if (err) {
29 return e(err);
30 }
31
32 // Add files to the test suite
33 files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
34
35 try {
36 // Run the mocha test
37 mocha.run(failures => {
38 if (failures > 0) {
39 e(new Error(`${failures} tests failed.`));
40 } else {
41 c();
42 }
43 });
44 } catch (err) {
45 e(err);
46 }
47 });
48 });
49}
diff --git a/editors/code/src/utils/diagnostics/SuggestedFix.ts b/editors/code/src/utils/diagnostics/SuggestedFix.ts
deleted file mode 100644
index 6e660bb61..000000000
--- a/editors/code/src/utils/diagnostics/SuggestedFix.ts
+++ /dev/null
@@ -1,67 +0,0 @@
1import * as vscode from 'vscode';
2
3import { SuggestionApplicability } from './rust';
4
5/**
6 * Model object for text replacements suggested by the Rust compiler
7 *
8 * This is an intermediate form between the raw `rustc` JSON and a
9 * `vscode.CodeAction`. It's optimised for the use-cases of
10 * `SuggestedFixCollection`.
11 */
12export default class SuggestedFix {
13 public readonly title: string;
14 public readonly location: vscode.Location;
15 public readonly replacement: string;
16 public readonly applicability: SuggestionApplicability;
17
18 /**
19 * Diagnostics this suggested fix could resolve
20 */
21 public diagnostics: vscode.Diagnostic[];
22
23 constructor(
24 title: string,
25 location: vscode.Location,
26 replacement: string,
27 applicability: SuggestionApplicability = SuggestionApplicability.Unspecified,
28 ) {
29 this.title = title;
30 this.location = location;
31 this.replacement = replacement;
32 this.applicability = applicability;
33 this.diagnostics = [];
34 }
35
36 /**
37 * Determines if this suggested fix is equivalent to another instance
38 */
39 public isEqual(other: SuggestedFix): boolean {
40 return (
41 this.title === other.title &&
42 this.location.range.isEqual(other.location.range) &&
43 this.replacement === other.replacement &&
44 this.applicability === other.applicability
45 );
46 }
47
48 /**
49 * Converts this suggested fix to a VS Code Quick Fix code action
50 */
51 public toCodeAction(): vscode.CodeAction {
52 const codeAction = new vscode.CodeAction(
53 this.title,
54 vscode.CodeActionKind.QuickFix,
55 );
56
57 const edit = new vscode.WorkspaceEdit();
58 edit.replace(this.location.uri, this.location.range, this.replacement);
59 codeAction.edit = edit;
60
61 codeAction.isPreferred =
62 this.applicability === SuggestionApplicability.MachineApplicable;
63
64 codeAction.diagnostics = [...this.diagnostics];
65 return codeAction;
66 }
67}
diff --git a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts b/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts
deleted file mode 100644
index 57c9856cf..000000000
--- a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts
+++ /dev/null
@@ -1,77 +0,0 @@
1import * as vscode from 'vscode';
2import SuggestedFix from './SuggestedFix';
3
4/**
5 * Collection of suggested fixes across multiple documents
6 *
7 * This stores `SuggestedFix` model objects and returns them via the
8 * `vscode.CodeActionProvider` interface.
9 */
10export default class SuggestedFixCollection
11 implements vscode.CodeActionProvider {
12 public static PROVIDED_CODE_ACTION_KINDS = [vscode.CodeActionKind.QuickFix];
13
14 /**
15 * Map of document URI strings to suggested fixes
16 */
17 private suggestedFixes: Map<string, SuggestedFix[]>;
18
19 constructor() {
20 this.suggestedFixes = new Map();
21 }
22
23 /**
24 * Clears all suggested fixes across all documents
25 */
26 public clear(): void {
27 this.suggestedFixes = new Map();
28 }
29
30 /**
31 * Adds a suggested fix for the given diagnostic
32 *
33 * Some suggested fixes will appear in multiple diagnostics. For example,
34 * forgetting a `mut` on a variable will suggest changing the delaration on
35 * every mutable usage site. If the suggested fix has already been added
36 * this method will instead associate the existing fix with the new
37 * diagnostic.
38 */
39 public addSuggestedFixForDiagnostic(
40 suggestedFix: SuggestedFix,
41 diagnostic: vscode.Diagnostic,
42 ): void {
43 const fileUriString = suggestedFix.location.uri.toString();
44 const fileSuggestions = this.suggestedFixes.get(fileUriString) || [];
45
46 const existingSuggestion = fileSuggestions.find(s =>
47 s.isEqual(suggestedFix),
48 );
49
50 if (existingSuggestion) {
51 // The existing suggestion also applies to this new diagnostic
52 existingSuggestion.diagnostics.push(diagnostic);
53 } else {
54 // We haven't seen this suggestion before
55 suggestedFix.diagnostics.push(diagnostic);
56 fileSuggestions.push(suggestedFix);
57 }
58
59 this.suggestedFixes.set(fileUriString, fileSuggestions);
60 }
61
62 /**
63 * Filters suggested fixes by their document and range and converts them to
64 * code actions
65 */
66 public provideCodeActions(
67 document: vscode.TextDocument,
68 range: vscode.Range,
69 ): vscode.CodeAction[] {
70 const documentUriString = document.uri.toString();
71
72 const suggestedFixes = this.suggestedFixes.get(documentUriString);
73 return (suggestedFixes || [])
74 .filter(({ location }) => location.range.intersection(range))
75 .map(suggestedEdit => suggestedEdit.toCodeAction());
76 }
77}
diff --git a/editors/code/src/utils/diagnostics/rust.ts b/editors/code/src/utils/diagnostics/rust.ts
deleted file mode 100644
index 1f0c0d3e4..000000000
--- a/editors/code/src/utils/diagnostics/rust.ts
+++ /dev/null
@@ -1,299 +0,0 @@
1import * as path from 'path';
2import * as vscode from 'vscode';
3
4import SuggestedFix from './SuggestedFix';
5
6export enum SuggestionApplicability {
7 MachineApplicable = 'MachineApplicable',
8 HasPlaceholders = 'HasPlaceholders',
9 MaybeIncorrect = 'MaybeIncorrect',
10 Unspecified = 'Unspecified',
11}
12
13export interface RustDiagnosticSpanMacroExpansion {
14 span: RustDiagnosticSpan;
15 macro_decl_name: string;
16 def_site_span?: RustDiagnosticSpan;
17}
18
19// Reference:
20// https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs
21export interface RustDiagnosticSpan {
22 line_start: number;
23 line_end: number;
24 column_start: number;
25 column_end: number;
26 is_primary: boolean;
27 file_name: string;
28 label?: string;
29 expansion?: RustDiagnosticSpanMacroExpansion;
30 suggested_replacement?: string;
31 suggestion_applicability?: SuggestionApplicability;
32}
33
34export interface RustDiagnostic {
35 spans: RustDiagnosticSpan[];
36 rendered: string;
37 message: string;
38 level: string;
39 code?: {
40 code: string;
41 };
42 children: RustDiagnostic[];
43}
44
45export interface MappedRustDiagnostic {
46 location: vscode.Location;
47 diagnostic: vscode.Diagnostic;
48 suggestedFixes: SuggestedFix[];
49}
50
51interface MappedRustChildDiagnostic {
52 related?: vscode.DiagnosticRelatedInformation;
53 suggestedFix?: SuggestedFix;
54 messageLine?: string;
55}
56
57/**
58 * Converts a Rust level string to a VsCode severity
59 */
60function mapLevelToSeverity(s: string): vscode.DiagnosticSeverity {
61 if (s === 'error') {
62 return vscode.DiagnosticSeverity.Error;
63 }
64 if (s.startsWith('warn')) {
65 return vscode.DiagnosticSeverity.Warning;
66 }
67 return vscode.DiagnosticSeverity.Information;
68}
69
70/**
71 * Check whether a file name is from macro invocation
72 */
73function isFromMacro(fileName: string): boolean {
74 return fileName.startsWith('<') && fileName.endsWith('>');
75}
76
77/**
78 * Converts a Rust macro span to a VsCode location recursively
79 */
80function mapMacroSpanToLocation(
81 spanMacro: RustDiagnosticSpanMacroExpansion,
82): vscode.Location | undefined {
83 if (!isFromMacro(spanMacro.span.file_name)) {
84 return mapSpanToLocation(spanMacro.span);
85 }
86
87 if (spanMacro.span.expansion) {
88 return mapMacroSpanToLocation(spanMacro.span.expansion);
89 }
90
91 return;
92}
93
94/**
95 * Converts a Rust span to a VsCode location
96 */
97function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location {
98 if (isFromMacro(span.file_name) && span.expansion) {
99 const macroLoc = mapMacroSpanToLocation(span.expansion);
100 if (macroLoc) {
101 return macroLoc;
102 }
103 }
104
105 const fileName = path.join(vscode.workspace.rootPath || '', span.file_name);
106 const fileUri = vscode.Uri.file(fileName);
107
108 const range = new vscode.Range(
109 new vscode.Position(span.line_start - 1, span.column_start - 1),
110 new vscode.Position(span.line_end - 1, span.column_end - 1),
111 );
112
113 return new vscode.Location(fileUri, range);
114}
115
116/**
117 * Converts a secondary Rust span to a VsCode related information
118 *
119 * If the span is unlabelled this will return `undefined`.
120 */
121function mapSecondarySpanToRelated(
122 span: RustDiagnosticSpan,
123): vscode.DiagnosticRelatedInformation | undefined {
124 if (!span.label) {
125 // Nothing to label this with
126 return;
127 }
128
129 const location = mapSpanToLocation(span);
130 return new vscode.DiagnosticRelatedInformation(location, span.label);
131}
132
133/**
134 * Determines if diagnostic is related to unused code
135 */
136function isUnusedOrUnnecessary(rd: RustDiagnostic): boolean {
137 if (!rd.code) {
138 return false;
139 }
140
141 return [
142 'dead_code',
143 'unknown_lints',
144 'unreachable_code',
145 'unused_attributes',
146 'unused_imports',
147 'unused_macros',
148 'unused_variables',
149 ].includes(rd.code.code);
150}
151
152/**
153 * Determines if diagnostic is related to deprecated code
154 */
155function isDeprecated(rd: RustDiagnostic): boolean {
156 if (!rd.code) {
157 return false;
158 }
159
160 return ['deprecated'].includes(rd.code.code);
161}
162
163/**
164 * Converts a Rust child diagnostic to a VsCode related information
165 *
166 * This can have three outcomes:
167 *
168 * 1. If this is no primary span this will return a `noteLine`
169 * 2. If there is a primary span with a suggested replacement it will return a
170 * `codeAction`.
171 * 3. If there is a primary span without a suggested replacement it will return
172 * a `related`.
173 */
174function mapRustChildDiagnostic(rd: RustDiagnostic): MappedRustChildDiagnostic {
175 const span = rd.spans.find(s => s.is_primary);
176
177 if (!span) {
178 // `rustc` uses these spanless children as a way to print multi-line
179 // messages
180 return { messageLine: rd.message };
181 }
182
183 // If we have a primary span use its location, otherwise use the parent
184 const location = mapSpanToLocation(span);
185
186 // We need to distinguish `null` from an empty string
187 if (span && typeof span.suggested_replacement === 'string') {
188 // Include our replacement in the title unless it's empty
189 const title = span.suggested_replacement
190 ? `${rd.message}: \`${span.suggested_replacement}\``
191 : rd.message;
192
193 return {
194 suggestedFix: new SuggestedFix(
195 title,
196 location,
197 span.suggested_replacement,
198 span.suggestion_applicability,
199 ),
200 };
201 } else {
202 const related = new vscode.DiagnosticRelatedInformation(
203 location,
204 rd.message,
205 );
206
207 return { related };
208 }
209}
210
211/**
212 * Converts a Rust root diagnostic to VsCode form
213 *
214 * This flattens the Rust diagnostic by:
215 *
216 * 1. Creating a `vscode.Diagnostic` with the root message and primary span.
217 * 2. Adding any labelled secondary spans to `relatedInformation`
218 * 3. Categorising child diagnostics as either `SuggestedFix`es,
219 * `relatedInformation` or additional message lines.
220 *
221 * If the diagnostic has no primary span this will return `undefined`
222 */
223export function mapRustDiagnosticToVsCode(
224 rd: RustDiagnostic,
225): MappedRustDiagnostic | undefined {
226 const primarySpan = rd.spans.find(s => s.is_primary);
227 if (!primarySpan) {
228 return;
229 }
230
231 const location = mapSpanToLocation(primarySpan);
232 const secondarySpans = rd.spans.filter(s => !s.is_primary);
233
234 const severity = mapLevelToSeverity(rd.level);
235 let primarySpanLabel = primarySpan.label;
236
237 const vd = new vscode.Diagnostic(location.range, rd.message, severity);
238
239 let source = 'rustc';
240 let code = rd.code && rd.code.code;
241 if (code) {
242 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
243 const scopedCode = code.split('::');
244 if (scopedCode.length === 2) {
245 [source, code] = scopedCode;
246 }
247 }
248
249 vd.source = source;
250 vd.code = code;
251 vd.relatedInformation = [];
252 vd.tags = [];
253
254 for (const secondarySpan of secondarySpans) {
255 const related = mapSecondarySpanToRelated(secondarySpan);
256 if (related) {
257 vd.relatedInformation.push(related);
258 }
259 }
260
261 const suggestedFixes = [];
262 for (const child of rd.children) {
263 const { related, suggestedFix, messageLine } = mapRustChildDiagnostic(
264 child,
265 );
266
267 if (related) {
268 vd.relatedInformation.push(related);
269 }
270 if (suggestedFix) {
271 suggestedFixes.push(suggestedFix);
272 }
273 if (messageLine) {
274 vd.message += `\n${messageLine}`;
275
276 // These secondary messages usually duplicate the content of the
277 // primary span label.
278 primarySpanLabel = undefined;
279 }
280 }
281
282 if (primarySpanLabel) {
283 vd.message += `\n${primarySpanLabel}`;
284 }
285
286 if (isUnusedOrUnnecessary(rd)) {
287 vd.tags.push(vscode.DiagnosticTag.Unnecessary);
288 }
289
290 if (isDeprecated(rd)) {
291 vd.tags.push(vscode.DiagnosticTag.Deprecated);
292 }
293
294 return {
295 location,
296 diagnostic: vd,
297 suggestedFixes,
298 };
299}
diff --git a/editors/code/src/utils/diagnostics/vscode.ts b/editors/code/src/utils/diagnostics/vscode.ts
deleted file mode 100644
index f4a5450e2..000000000
--- a/editors/code/src/utils/diagnostics/vscode.ts
+++ /dev/null
@@ -1,14 +0,0 @@
1import * as vscode from 'vscode';
2
3/** Compares two `vscode.Diagnostic`s for equality */
4export function areDiagnosticsEqual(
5 left: vscode.Diagnostic,
6 right: vscode.Diagnostic,
7): boolean {
8 return (
9 left.source === right.source &&
10 left.severity === right.severity &&
11 left.range.isEqual(right.range) &&
12 left.message === right.message
13 );
14}
diff --git a/editors/code/src/utils/processes.ts b/editors/code/src/utils/processes.ts
deleted file mode 100644
index a1d6b7eaf..000000000
--- a/editors/code/src/utils/processes.ts
+++ /dev/null
@@ -1,51 +0,0 @@
1'use strict';
2
3import * as cp from 'child_process';
4import ChildProcess = cp.ChildProcess;
5
6import { join } from 'path';
7
8const isWindows = process.platform === 'win32';
9const isMacintosh = process.platform === 'darwin';
10const isLinux = process.platform === 'linux';
11
12// this is very complex, but is basically copy-pased from VSCode implementation here:
13// https://github.com/Microsoft/vscode-languageserver-node/blob/dbfd37e35953ad0ee14c4eeced8cfbc41697b47e/client/src/utils/processes.ts#L15
14
15// And see discussion at
16// https://github.com/rust-analyzer/rust-analyzer/pull/1079#issuecomment-478908109
17
18export function terminate(process: ChildProcess, cwd?: string): boolean {
19 if (isWindows) {
20 try {
21 // This we run in Atom execFileSync is available.
22 // Ignore stderr since this is otherwise piped to parent.stderr
23 // which might be already closed.
24 const options: any = {
25 stdio: ['pipe', 'pipe', 'ignore'],
26 };
27 if (cwd) {
28 options.cwd = cwd;
29 }
30 cp.execFileSync(
31 'taskkill',
32 ['/T', '/F', '/PID', process.pid.toString()],
33 options,
34 );
35 return true;
36 } catch (err) {
37 return false;
38 }
39 } else if (isLinux || isMacintosh) {
40 try {
41 const cmd = join(__dirname, 'terminateProcess.sh');
42 const result = cp.spawnSync(cmd, [process.pid.toString()]);
43 return result.error ? false : true;
44 } catch (err) {
45 return false;
46 }
47 } else {
48 process.kill('SIGKILL');
49 return true;
50 }
51}
diff --git a/editors/code/src/utils/terminateProcess.sh b/editors/code/src/utils/terminateProcess.sh
deleted file mode 100644
index 2ec9e1c2e..000000000
--- a/editors/code/src/utils/terminateProcess.sh
+++ /dev/null
@@ -1,12 +0,0 @@
1#!/bin/bash
2
3terminateTree() {
4 for cpid in $(pgrep -P $1); do
5 terminateTree $cpid
6 done
7 kill -9 $1 > /dev/null 2>&1
8}
9
10for pid in $*; do
11 terminateTree $pid
12done \ No newline at end of file
diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json
index 5e11c3775..e60eb8e5e 100644
--- a/editors/code/tsconfig.json
+++ b/editors/code/tsconfig.json
@@ -3,14 +3,19 @@
3 "module": "commonjs", 3 "module": "commonjs",
4 "target": "es2018", 4 "target": "es2018",
5 "outDir": "out", 5 "outDir": "out",
6 "lib": ["es2018"], 6 "lib": [
7 "es2019"
8 ],
7 "sourceMap": true, 9 "sourceMap": true,
8 "rootDir": "src", 10 "rootDir": "src",
9 "strict": true, 11 "strict": true,
10 "noUnusedLocals": true, 12 "noUnusedLocals": true,
11 "noUnusedParameters": true, 13 "noUnusedParameters": true,
12 "noImplicitReturns": true, 14 "noImplicitReturns": true,
13 "noFallthroughCasesInSwitch": true 15 "noFallthroughCasesInSwitch": true,
16 "newLine": "LF"
14 }, 17 },
15 "exclude": ["node_modules", ".vscode-test"] 18 "exclude": [
19 "node_modules"
20 ]
16} 21}
diff --git a/editors/code/tslint.json b/editors/code/tslint.json
index f06fa5fab..318e02b4b 100644
--- a/editors/code/tslint.json
+++ b/editors/code/tslint.json
@@ -1,15 +1,8 @@
1{ 1{
2 "defaultSeverity": "error",
3 "extends": [
4 "tslint:recommended",
5 "tslint-config-prettier",
6 "tslint-plugin-prettier"
7 ],
8 "rules": { 2 "rules": {
9 "interface-name": false, 3 "semicolon": [
10 "prettier": true, 4 true,
11 "object-literal-sort-keys": false, 5 "always"
12 // Allow `_bar` to sort with tsc's `noUnusedParameters` option 6 ]
13 "variable-name": [true, "allow-leading-underscore"]
14 } 7 }
15} 8}
diff --git a/xtask/tests/tidy-tests/cli.rs b/xtask/tests/tidy-tests/cli.rs
index 573ffadbf..f9ca45292 100644
--- a/xtask/tests/tidy-tests/cli.rs
+++ b/xtask/tests/tidy-tests/cli.rs
@@ -43,7 +43,7 @@ fn no_todo() {
43 return; 43 return;
44 } 44 }
45 let text = std::fs::read_to_string(e.path()).unwrap(); 45 let text = std::fs::read_to_string(e.path()).unwrap();
46 if text.contains("TODO") || text.contains("TOOD") { 46 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
47 panic!( 47 panic!(
48 "\nTODO markers should not be committed to the master branch,\n\ 48 "\nTODO markers should not be committed to the master branch,\n\
49 use FIXME instead\n\ 49 use FIXME instead\n\