aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/actions/github-release/main.js2
-rw-r--r--.github/workflows/ci.yaml17
-rw-r--r--.github/workflows/release.yaml5
-rw-r--r--.vscode/launch.json34
-rw-r--r--.vscode/tasks.json12
-rw-r--r--Cargo.lock79
-rw-r--r--bors.toml4
-rw-r--r--crates/ra_assists/src/assist_context.rs27
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs18
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs6
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs8
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs22
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs30
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs18
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs14
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs2
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs14
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs47
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs112
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs6
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs22
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs25
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs8
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs33
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs26
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs39
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs5
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs8
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs29
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs5
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs38
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs41
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs6
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs24
-rw-r--r--crates/ra_assists/src/tests.rs18
-rw-r--r--crates/ra_assists/src/tests/generated.rs4
-rw-r--r--crates/ra_cfg/src/cfg_expr.rs10
-rw-r--r--crates/ra_db/src/fixture.rs70
-rw-r--r--crates/ra_db/src/input.rs15
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir_def/src/attr.rs2
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/db.rs4
-rw-r--r--crates/ra_hir_def/src/find_path.rs7
-rw-r--r--crates/ra_hir_def/src/lang_item.rs14
-rw-r--r--crates/ra_hir_ty/Cargo.toml6
-rw-r--r--crates/ra_hir_ty/src/db.rs7
-rw-r--r--crates/ra_hir_ty/src/lib.rs8
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs152
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs3
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs899
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs353
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs701
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs83
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs29
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs4
-rw-r--r--crates/ra_ide/src/diagnostics.rs103
-rw-r--r--crates/ra_ide/src/display.rs7
-rw-r--r--crates/ra_ide/src/hover.rs21
-rw-r--r--crates/ra_ide/src/join_lines.rs24
-rw-r--r--crates/ra_ide/src/lib.rs34
-rw-r--r--crates/ra_ide/src/mock_analysis.rs123
-rw-r--r--crates/ra_ide/src/references/rename.rs18
-rw-r--r--crates/ra_ide/src/runnables.rs189
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html13
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html1
-rw-r--r--crates/ra_ide/src/ssr.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs36
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs10
-rw-r--r--crates/ra_ide/src/test_utils.rs25
-rw-r--r--crates/ra_ide/src/typing.rs54
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs31
-rw-r--r--crates/ra_ide_db/src/lib.rs1
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs302
-rw-r--r--crates/ra_ide_db/src/source_change.rs101
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs5
-rw-r--r--crates/ra_syntax/src/validation.rs20
-rw-r--r--crates/ra_text_edit/src/lib.rs45
-rw-r--r--crates/rust-analyzer/Cargo.toml3
-rw-r--r--crates/rust-analyzer/src/bin/main.rs17
-rw-r--r--crates/rust-analyzer/src/caps.rs72
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs8
-rw-r--r--crates/rust-analyzer/src/config.rs15
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap1
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap1
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs1
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs58
-rw-r--r--crates/rust-analyzer/src/main_loop.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs246
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs3
-rw-r--r--crates/rust-analyzer/src/to_proto.rs111
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs64
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs2
-rw-r--r--crates/test_utils/Cargo.toml4
-rw-r--r--crates/test_utils/src/lib.rs160
-rw-r--r--docs/dev/README.md4
-rw-r--r--docs/dev/debugging.md6
-rw-r--r--docs/dev/lsp-extensions.md346
-rw-r--r--docs/user/assists.md4
-rw-r--r--docs/user/features.md6
-rw-r--r--docs/user/readme.adoc2
-rw-r--r--editors/code/.vscodeignore2
-rw-r--r--editors/code/package-lock.json1314
-rw-r--r--editors/code/package.json70
-rw-r--r--editors/code/rollup.config.js4
-rw-r--r--editors/code/rust.tmGrammar.json7
-rw-r--r--editors/code/src/ast_inspector.ts (renamed from editors/code/src/commands/syntax_tree.ts)88
-rw-r--r--editors/code/src/cargo.ts57
-rw-r--r--editors/code/src/client.ts41
-rw-r--r--editors/code/src/commands.ts370
-rw-r--r--editors/code/src/commands/analyzer_status.ts51
-rw-r--r--editors/code/src/commands/expand_macro.ts66
-rw-r--r--editors/code/src/commands/index.ts88
-rw-r--r--editors/code/src/commands/join_lines.ts18
-rw-r--r--editors/code/src/commands/matching_brace.ts27
-rw-r--r--editors/code/src/commands/on_enter.ts34
-rw-r--r--editors/code/src/commands/parent_module.ts29
-rw-r--r--editors/code/src/commands/server_version.ts15
-rw-r--r--editors/code/src/commands/ssr.ts32
-rw-r--r--editors/code/src/config.ts2
-rw-r--r--editors/code/src/debug.ts2
-rw-r--r--editors/code/src/inlay_hints.ts2
-rw-r--r--editors/code/src/lsp_ext.ts84
-rw-r--r--editors/code/src/main.ts55
-rw-r--r--editors/code/src/run.ts (renamed from editors/code/src/commands/runnables.ts)26
-rw-r--r--editors/code/src/rust-analyzer-api.ts125
-rw-r--r--editors/code/src/snippets.ts55
-rw-r--r--editors/code/src/source_change.ts54
-rw-r--r--editors/code/tests/runTests.ts43
-rw-r--r--editors/code/tests/unit/index.ts38
-rw-r--r--editors/code/tests/unit/launch_config.test.ts52
-rw-r--r--editors/code/tsconfig.json9
136 files changed, 5156 insertions, 3196 deletions
diff --git a/.github/actions/github-release/main.js b/.github/actions/github-release/main.js
index 2c7eedbe2..b499cd0fd 100644
--- a/.github/actions/github-release/main.js
+++ b/.github/actions/github-release/main.js
@@ -16,7 +16,7 @@ async function runOnce() {
16 const slug = process.env.GITHUB_REPOSITORY; 16 const slug = process.env.GITHUB_REPOSITORY;
17 const owner = slug.split('/')[0]; 17 const owner = slug.split('/')[0];
18 const repo = slug.split('/')[1]; 18 const repo = slug.split('/')[1];
19 const sha = process.env.GITHUB_SHA; 19 const sha = process.env.HEAD_SHA;
20 20
21 core.info(`files: ${files}`); 21 core.info(`files: ${files}`);
22 core.info(`name: ${name}`); 22 core.info(`name: ${name}`);
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 00f299ff1..ed9191c49 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -97,7 +97,13 @@ jobs:
97 97
98 typescript: 98 typescript:
99 name: TypeScript 99 name: TypeScript
100 runs-on: ubuntu-latest 100 strategy:
101 fail-fast: false
102 matrix:
103 os: [ubuntu-latest, windows-latest, macos-latest]
104
105 runs-on: ${{ matrix.os }}
106
101 steps: 107 steps:
102 - name: Checkout repository 108 - name: Checkout repository
103 uses: actions/checkout@v2 109 uses: actions/checkout@v2
@@ -111,10 +117,19 @@ jobs:
111 working-directory: ./editors/code 117 working-directory: ./editors/code
112 118
113 - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; } 119 - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
120 if: runner.os == 'Linux'
114 working-directory: ./editors/code 121 working-directory: ./editors/code
115 122
116 - run: npm run lint 123 - run: npm run lint
117 working-directory: ./editors/code 124 working-directory: ./editors/code
118 125
126 - name: Run vscode tests
127 uses: GabrielBB/[email protected]
128 env:
129 VSCODE_CLI: 1
130 with:
131 run: npm --prefix ./editors/code test
132 # working-directory: ./editors/code # does not work: https://github.com/GabrielBB/xvfb-action/issues/8
133
119 - run: npm run package --scripts-prepend-node-path 134 - run: npm run package --scripts-prepend-node-path
120 working-directory: ./editors/code 135 working-directory: ./editors/code
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 29ac89549..1ae8ed1b6 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -6,7 +6,7 @@ on:
6 push: 6 push:
7 branches: 7 branches:
8 - release 8 - release
9 - nightly 9 - trigger-nightly
10 10
11env: 11env:
12 CARGO_INCREMENTAL: 0 12 CARGO_INCREMENTAL: 0
@@ -88,6 +88,9 @@ jobs:
88 - name: Checkout repository 88 - name: Checkout repository
89 uses: actions/checkout@v2 89 uses: actions/checkout@v2
90 90
91 - run: echo "::set-env name=HEAD_SHA::$(git rev-parse HEAD)"
92 - run: 'echo "HEAD_SHA: $HEAD_SHA"'
93
91 - uses: actions/download-artifact@v1 94 - uses: actions/download-artifact@v1
92 with: 95 with:
93 name: dist-macos-latest 96 name: dist-macos-latest
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6a2fff906..8ca27d878 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -71,6 +71,28 @@
71 } 71 }
72 }, 72 },
73 { 73 {
74 // Used for testing the extension with a local build of the LSP server (in `target/release`)
75 // with all other extendions loaded.
76 "name": "Run With Extensions",
77 "type": "extensionHost",
78 "request": "launch",
79 "runtimeExecutable": "${execPath}",
80 "args": [
81 "--disable-extension", "matklad.rust-analyzer",
82 "--extensionDevelopmentPath=${workspaceFolder}/editors/code"
83 ],
84 "outFiles": [
85 "${workspaceFolder}/editors/code/out/**/*.js"
86 ],
87 "preLaunchTask": "Build Server (Release) and Extension",
88 "skipFiles": [
89 "<node_internals>/**/*.js"
90 ],
91 "env": {
92 "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/release/rust-analyzer"
93 }
94 },
95 {
74 // Used to attach LLDB to a running LSP server. 96 // Used to attach LLDB to a running LSP server.
75 // NOTE: Might require root permissions. For this run: 97 // NOTE: Might require root permissions. For this run:
76 // 98 //
@@ -87,5 +109,17 @@
87 "rust" 109 "rust"
88 ] 110 ]
89 }, 111 },
112 {
113 "name": "Run Unit Tests",
114 "type": "extensionHost",
115 "request": "launch",
116 "runtimeExecutable": "${execPath}",
117 "args": [
118 "--extensionDevelopmentPath=${workspaceFolder}/editors/code",
119 "--extensionTestsPath=${workspaceFolder}/editors/code/out/tests/unit" ],
120 "sourceMaps": true,
121 "outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ],
122 "preLaunchTask": "Pretest"
123 }
90 ] 124 ]
91} 125}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 0969ce89a..a25dff19e 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -40,6 +40,18 @@
40 "command": "cargo build --release --package rust-analyzer", 40 "command": "cargo build --release --package rust-analyzer",
41 "problemMatcher": "$rustc" 41 "problemMatcher": "$rustc"
42 }, 42 },
43 {
44 "label": "Pretest",
45 "group": "build",
46 "isBackground": false,
47 "type": "npm",
48 "script": "pretest",
49 "path": "editors/code/",
50 "problemMatcher": {
51 "base": "$tsc",
52 "fileLocation": ["relative", "${workspaceFolder}/editors/code/"]
53 }
54 },
43 55
44 { 56 {
45 "label": "Build Server and Extension", 57 "label": "Build Server and Extension",
diff --git a/Cargo.lock b/Cargo.lock
index c06236692..1f4c0cb53 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -101,9 +101,9 @@ dependencies = [
101 101
102[[package]] 102[[package]]
103name = "cc" 103name = "cc"
104version = "1.0.53" 104version = "1.0.54"
105source = "registry+https://github.com/rust-lang/crates.io-index" 105source = "registry+https://github.com/rust-lang/crates.io-index"
106checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" 106checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
107 107
108[[package]] 108[[package]]
109name = "cfg-if" 109name = "cfg-if"
@@ -114,7 +114,7 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
114[[package]] 114[[package]]
115name = "chalk-derive" 115name = "chalk-derive"
116version = "0.10.1-dev" 116version = "0.10.1-dev"
117source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 117source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
118dependencies = [ 118dependencies = [
119 "proc-macro2", 119 "proc-macro2",
120 "quote", 120 "quote",
@@ -125,7 +125,7 @@ dependencies = [
125[[package]] 125[[package]]
126name = "chalk-engine" 126name = "chalk-engine"
127version = "0.10.1-dev" 127version = "0.10.1-dev"
128source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 128source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
129dependencies = [ 129dependencies = [
130 "chalk-macros", 130 "chalk-macros",
131 "rustc-hash", 131 "rustc-hash",
@@ -134,7 +134,7 @@ dependencies = [
134[[package]] 134[[package]]
135name = "chalk-ir" 135name = "chalk-ir"
136version = "0.10.1-dev" 136version = "0.10.1-dev"
137source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 137source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
138dependencies = [ 138dependencies = [
139 "chalk-derive", 139 "chalk-derive",
140 "chalk-engine", 140 "chalk-engine",
@@ -144,7 +144,7 @@ dependencies = [
144[[package]] 144[[package]]
145name = "chalk-macros" 145name = "chalk-macros"
146version = "0.10.1-dev" 146version = "0.10.1-dev"
147source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 147source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
148dependencies = [ 148dependencies = [
149 "lazy_static", 149 "lazy_static",
150] 150]
@@ -152,7 +152,7 @@ dependencies = [
152[[package]] 152[[package]]
153name = "chalk-rust-ir" 153name = "chalk-rust-ir"
154version = "0.10.1-dev" 154version = "0.10.1-dev"
155source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 155source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
156dependencies = [ 156dependencies = [
157 "chalk-derive", 157 "chalk-derive",
158 "chalk-engine", 158 "chalk-engine",
@@ -163,14 +163,14 @@ dependencies = [
163[[package]] 163[[package]]
164name = "chalk-solve" 164name = "chalk-solve"
165version = "0.10.1-dev" 165version = "0.10.1-dev"
166source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" 166source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
167dependencies = [ 167dependencies = [
168 "chalk-derive", 168 "chalk-derive",
169 "chalk-engine", 169 "chalk-engine",
170 "chalk-ir", 170 "chalk-ir",
171 "chalk-macros", 171 "chalk-macros",
172 "chalk-rust-ir", 172 "chalk-rust-ir",
173 "ena 0.13.1", 173 "ena",
174 "itertools", 174 "itertools",
175 "petgraph", 175 "petgraph",
176 "rustc-hash", 176 "rustc-hash",
@@ -309,15 +309,6 @@ checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
309 309
310[[package]] 310[[package]]
311name = "ena" 311name = "ena"
312version = "0.13.1"
313source = "registry+https://github.com/rust-lang/crates.io-index"
314checksum = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36"
315dependencies = [
316 "log",
317]
318
319[[package]]
320name = "ena"
321version = "0.14.0" 312version = "0.14.0"
322source = "registry+https://github.com/rust-lang/crates.io-index" 313source = "registry+https://github.com/rust-lang/crates.io-index"
323checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" 314checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
@@ -354,9 +345,9 @@ dependencies = [
354 345
355[[package]] 346[[package]]
356name = "fixedbitset" 347name = "fixedbitset"
357version = "0.1.9" 348version = "0.2.0"
358source = "registry+https://github.com/rust-lang/crates.io-index" 349source = "registry+https://github.com/rust-lang/crates.io-index"
359checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" 350checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
360 351
361[[package]] 352[[package]]
362name = "fnv" 353name = "fnv"
@@ -391,9 +382,9 @@ dependencies = [
391 382
392[[package]] 383[[package]]
393name = "fst" 384name = "fst"
394version = "0.4.3" 385version = "0.4.4"
395source = "registry+https://github.com/rust-lang/crates.io-index" 386source = "registry+https://github.com/rust-lang/crates.io-index"
396checksum = "81f9cac32c1741cdf6b66be7dcf0d9c7f25ccf12f8aa84c16cfa31f9f14513b3" 387checksum = "a7293de202dbfe786c0b3fe6110a027836c5438ed06db7b715c9955ff4bfea51"
397 388
398[[package]] 389[[package]]
399name = "fuchsia-zircon" 390name = "fuchsia-zircon"
@@ -658,9 +649,9 @@ dependencies = [
658 649
659[[package]] 650[[package]]
660name = "lsp-server" 651name = "lsp-server"
661version = "0.3.1" 652version = "0.3.2"
662source = "registry+https://github.com/rust-lang/crates.io-index" 653source = "registry+https://github.com/rust-lang/crates.io-index"
663checksum = "5383e043329615624bbf45e1ba27bd75c176762b2592855c659bc28ac580a06b" 654checksum = "dccec31bfd027ac0dd288a78e19005fd89624d9099456e284b5241316a6c3072"
664dependencies = [ 655dependencies = [
665 "crossbeam-channel", 656 "crossbeam-channel",
666 "log", 657 "log",
@@ -814,12 +805,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
814checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 805checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
815 806
816[[package]] 807[[package]]
817name = "ordermap"
818version = "0.3.5"
819source = "registry+https://github.com/rust-lang/crates.io-index"
820checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
821
822[[package]]
823name = "parking_lot" 808name = "parking_lot"
824version = "0.10.2" 809version = "0.10.2"
825source = "registry+https://github.com/rust-lang/crates.io-index" 810source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -845,9 +830,9 @@ dependencies = [
845 830
846[[package]] 831[[package]]
847name = "paste" 832name = "paste"
848version = "0.1.12" 833version = "0.1.13"
849source = "registry+https://github.com/rust-lang/crates.io-index" 834source = "registry+https://github.com/rust-lang/crates.io-index"
850checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476" 835checksum = "678f27e19361472a23717f11d229a7522ef64605baf0715c896a94b8b6b13a06"
851dependencies = [ 836dependencies = [
852 "paste-impl", 837 "paste-impl",
853 "proc-macro-hack", 838 "proc-macro-hack",
@@ -855,9 +840,9 @@ dependencies = [
855 840
856[[package]] 841[[package]]
857name = "paste-impl" 842name = "paste-impl"
858version = "0.1.12" 843version = "0.1.13"
859source = "registry+https://github.com/rust-lang/crates.io-index" 844source = "registry+https://github.com/rust-lang/crates.io-index"
860checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c" 845checksum = "149089128a45d8e377677b08873b4bad2a56618f80e4f28a83862ba250994a30"
861dependencies = [ 846dependencies = [
862 "proc-macro-hack", 847 "proc-macro-hack",
863 "proc-macro2", 848 "proc-macro2",
@@ -873,12 +858,12 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
873 858
874[[package]] 859[[package]]
875name = "petgraph" 860name = "petgraph"
876version = "0.4.13" 861version = "0.5.1"
877source = "registry+https://github.com/rust-lang/crates.io-index" 862source = "registry+https://github.com/rust-lang/crates.io-index"
878checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" 863checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
879dependencies = [ 864dependencies = [
880 "fixedbitset", 865 "fixedbitset",
881 "ordermap", 866 "indexmap",
882] 867]
883 868
884[[package]] 869[[package]]
@@ -907,9 +892,9 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
907 892
908[[package]] 893[[package]]
909name = "proc-macro2" 894name = "proc-macro2"
910version = "1.0.13" 895version = "1.0.17"
911source = "registry+https://github.com/rust-lang/crates.io-index" 896source = "registry+https://github.com/rust-lang/crates.io-index"
912checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" 897checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
913dependencies = [ 898dependencies = [
914 "unicode-xid", 899 "unicode-xid",
915] 900]
@@ -1053,7 +1038,7 @@ dependencies = [
1053 "chalk-ir", 1038 "chalk-ir",
1054 "chalk-rust-ir", 1039 "chalk-rust-ir",
1055 "chalk-solve", 1040 "chalk-solve",
1056 "ena 0.14.0", 1041 "ena",
1057 "insta", 1042 "insta",
1058 "itertools", 1043 "itertools",
1059 "log", 1044 "log",
@@ -1387,17 +1372,20 @@ dependencies = [
1387 "lsp-types", 1372 "lsp-types",
1388 "parking_lot", 1373 "parking_lot",
1389 "pico-args", 1374 "pico-args",
1375 "ra_cfg",
1390 "ra_db", 1376 "ra_db",
1391 "ra_flycheck", 1377 "ra_flycheck",
1392 "ra_hir", 1378 "ra_hir",
1393 "ra_hir_def", 1379 "ra_hir_def",
1394 "ra_hir_ty", 1380 "ra_hir_ty",
1395 "ra_ide", 1381 "ra_ide",
1382 "ra_mbe",
1396 "ra_proc_macro_srv", 1383 "ra_proc_macro_srv",
1397 "ra_prof", 1384 "ra_prof",
1398 "ra_project_model", 1385 "ra_project_model",
1399 "ra_syntax", 1386 "ra_syntax",
1400 "ra_text_edit", 1387 "ra_text_edit",
1388 "ra_tt",
1401 "ra_vfs", 1389 "ra_vfs",
1402 "rand", 1390 "rand",
1403 "relative-path", 1391 "relative-path",
@@ -1413,9 +1401,9 @@ dependencies = [
1413 1401
1414[[package]] 1402[[package]]
1415name = "rustc-ap-rustc_lexer" 1403name = "rustc-ap-rustc_lexer"
1416version = "656.0.0" 1404version = "660.0.0"
1417source = "registry+https://github.com/rust-lang/crates.io-index" 1405source = "registry+https://github.com/rust-lang/crates.io-index"
1418checksum = "9cbba98ec46e96a4663197dfa8c0378752de2006e314e5400c0ca74929d6692f" 1406checksum = "30760dbcc7667c9e0da561e980e24867ca7f4526ce060a3d7e6d9dcfeaae88d1"
1419dependencies = [ 1407dependencies = [
1420 "unicode-xid", 1408 "unicode-xid",
1421] 1409]
@@ -1610,9 +1598,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1610 1598
1611[[package]] 1599[[package]]
1612name = "syn" 1600name = "syn"
1613version = "1.0.22" 1601version = "1.0.23"
1614source = "registry+https://github.com/rust-lang/crates.io-index" 1602source = "registry+https://github.com/rust-lang/crates.io-index"
1615checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" 1603checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
1616dependencies = [ 1604dependencies = [
1617 "proc-macro2", 1605 "proc-macro2",
1618 "quote", 1606 "quote",
@@ -1669,6 +1657,9 @@ name = "test_utils"
1669version = "0.1.0" 1657version = "0.1.0"
1670dependencies = [ 1658dependencies = [
1671 "difference", 1659 "difference",
1660 "ra_cfg",
1661 "relative-path",
1662 "rustc-hash",
1672 "serde_json", 1663 "serde_json",
1673 "text-size", 1664 "text-size",
1674] 1665]
diff --git a/bors.toml b/bors.toml
index 0bc71860f..13ce236df 100644
--- a/bors.toml
+++ b/bors.toml
@@ -2,6 +2,8 @@ status = [
2 "Rust (ubuntu-latest)", 2 "Rust (ubuntu-latest)",
3 "Rust (windows-latest)", 3 "Rust (windows-latest)",
4 "Rust (macos-latest)", 4 "Rust (macos-latest)",
5 "TypeScript" 5 "TypeScript (ubuntu-latest)",
6 "TypeScript (windows-latest)",
7 "TypeScript (macos-latest)",
6] 8]
7delete_merged_branches = true 9delete_merged_branches = true
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index 005c17776..5b1a4680b 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -5,7 +5,7 @@ use hir::Semantics;
5use ra_db::{FileId, FileRange}; 5use ra_db::{FileId, FileRange};
6use ra_fmt::{leading_indent, reindent}; 6use ra_fmt::{leading_indent, reindent};
7use ra_ide_db::{ 7use ra_ide_db::{
8 source_change::{SingleFileChange, SourceChange}, 8 source_change::{SourceChange, SourceFileEdit},
9 RootDatabase, 9 RootDatabase,
10}; 10};
11use ra_syntax::{ 11use ra_syntax::{
@@ -150,11 +150,10 @@ impl Assists {
150 self.add_impl(label, f) 150 self.add_impl(label, f)
151 } 151 }
152 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 152 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
153 let change_label = label.label.clone();
154 let source_change = if self.resolve { 153 let source_change = if self.resolve {
155 let mut builder = AssistBuilder::new(self.file); 154 let mut builder = AssistBuilder::new(self.file);
156 f(&mut builder); 155 f(&mut builder);
157 Some(builder.finish(change_label)) 156 Some(builder.finish())
158 } else { 157 } else {
159 None 158 None
160 }; 159 };
@@ -171,19 +170,13 @@ impl Assists {
171 170
172pub(crate) struct AssistBuilder { 171pub(crate) struct AssistBuilder {
173 edit: TextEditBuilder, 172 edit: TextEditBuilder,
174 cursor_position: Option<TextSize>,
175 file: FileId, 173 file: FileId,
176 is_snippet: bool, 174 is_snippet: bool,
177} 175}
178 176
179impl AssistBuilder { 177impl AssistBuilder {
180 pub(crate) fn new(file: FileId) -> AssistBuilder { 178 pub(crate) fn new(file: FileId) -> AssistBuilder {
181 AssistBuilder { 179 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
182 edit: TextEditBuilder::default(),
183 cursor_position: None,
184 file,
185 is_snippet: false,
186 }
187 } 180 }
188 181
189 /// Remove specified `range` of text. 182 /// Remove specified `range` of text.
@@ -241,10 +234,6 @@ impl AssistBuilder {
241 algo::diff(&node, &new).into_text_edit(&mut self.edit) 234 algo::diff(&node, &new).into_text_edit(&mut self.edit)
242 } 235 }
243 236
244 /// Specify desired position of the cursor after the assist is applied.
245 pub(crate) fn set_cursor(&mut self, offset: TextSize) {
246 self.cursor_position = Some(offset)
247 }
248 // FIXME: better API 237 // FIXME: better API
249 pub(crate) fn set_file(&mut self, assist_file: FileId) { 238 pub(crate) fn set_file(&mut self, assist_file: FileId) {
250 self.file = assist_file; 239 self.file = assist_file;
@@ -256,14 +245,10 @@ impl AssistBuilder {
256 &mut self.edit 245 &mut self.edit
257 } 246 }
258 247
259 fn finish(self, change_label: String) -> SourceChange { 248 fn finish(self) -> SourceChange {
260 let edit = self.edit.finish(); 249 let edit = self.edit.finish();
261 if edit.is_empty() && self.cursor_position.is_none() { 250 let source_file_edit = SourceFileEdit { file_id: self.file, edit };
262 panic!("Only call `add_assist` if the assist can be applied") 251 let mut res: SourceChange = source_file_edit.into();
263 }
264 let mut res =
265 SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position }
266 .into_source_change(self.file);
267 if self.is_snippet { 252 if self.is_snippet {
268 res.is_snippet = true; 253 res.is_snippet = true;
269 } 254 }
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 770049212..ab20c6649 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -86,11 +86,7 @@ mod tests {
86 86
87 #[test] 87 #[test]
88 fn add_explicit_type_works_for_simple_expr() { 88 fn add_explicit_type_works_for_simple_expr() {
89 check_assist( 89 check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }");
90 add_explicit_type,
91 "fn f() { let a<|> = 1; }",
92 "fn f() { let a<|>: i32 = 1; }",
93 );
94 } 90 }
95 91
96 #[test] 92 #[test]
@@ -98,7 +94,7 @@ mod tests {
98 check_assist( 94 check_assist(
99 add_explicit_type, 95 add_explicit_type,
100 "fn f() { let a<|>: _ = 1; }", 96 "fn f() { let a<|>: _ = 1; }",
101 "fn f() { let a<|>: i32 = 1; }", 97 "fn f() { let a: i32 = 1; }",
102 ); 98 );
103 } 99 }
104 100
@@ -122,7 +118,7 @@ mod tests {
122 } 118 }
123 119
124 fn f() { 120 fn f() {
125 let a<|>: Option<i32> = Option::Some(1); 121 let a: Option<i32> = Option::Some(1);
126 }"#, 122 }"#,
127 ); 123 );
128 } 124 }
@@ -132,7 +128,7 @@ mod tests {
132 check_assist( 128 check_assist(
133 add_explicit_type, 129 add_explicit_type,
134 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", 130 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
135 r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", 131 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
136 ); 132 );
137 } 133 }
138 134
@@ -140,8 +136,8 @@ mod tests {
140 fn add_explicit_type_works_for_macro_call_recursive() { 136 fn add_explicit_type_works_for_macro_call_recursive() {
141 check_assist( 137 check_assist(
142 add_explicit_type, 138 add_explicit_type,
143 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", 139 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#,
144 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", 140 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
145 ); 141 );
146 } 142 }
147 143
@@ -208,7 +204,7 @@ struct Test<K, T = u8> {
208} 204}
209 205
210fn main() { 206fn main() {
211 let test<|>: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23, k: 33 };
212}"#, 208}"#,
213 ); 209 );
214 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index 5f6c8b19a..6a675e812 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -101,7 +101,7 @@ mod tests {
101 check_assist( 101 check_assist(
102 add_from_impl_for_enum, 102 add_from_impl_for_enum,
103 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
104 r#"enum A { <|>One(u32) } 104 r#"enum A { One(u32) }
105 105
106impl From<u32> for A { 106impl From<u32> for A {
107 fn from(v: u32) -> Self { 107 fn from(v: u32) -> Self {
@@ -116,7 +116,7 @@ impl From<u32> for A {
116 check_assist( 116 check_assist(
117 add_from_impl_for_enum, 117 add_from_impl_for_enum,
118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
119 r#"enum A { <|>One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
120 120
121impl From<foo::bar::baz::Boo> for A { 121impl From<foo::bar::baz::Boo> for A {
122 fn from(v: foo::bar::baz::Boo) -> Self { 122 fn from(v: foo::bar::baz::Boo) -> Self {
@@ -178,7 +178,7 @@ impl From<String> for A {
178pub trait From<T> { 178pub trait From<T> {
179 fn from(T) -> Self; 179 fn from(T) -> Self;
180}"#, 180}"#,
181 r#"enum A { <|>One(u32), Two(String), } 181 r#"enum A { One(u32), Two(String), }
182 182
183impl From<u32> for A { 183impl From<u32> for A {
184 fn from(v: u32) -> Self { 184 fn from(v: u32) -> Self {
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 0feba5e11..233e8fb8e 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -63,22 +63,22 @@ mod tests {
63 63
64 #[test] 64 #[test]
65 fn demorgan_turns_and_into_or() { 65 fn demorgan_turns_and_into_or() {
66 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") 66 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }")
67 } 67 }
68 68
69 #[test] 69 #[test]
70 fn demorgan_turns_or_into_and() { 70 fn demorgan_turns_or_into_and() {
71 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") 71 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }")
72 } 72 }
73 73
74 #[test] 74 #[test]
75 fn demorgan_removes_inequality() { 75 fn demorgan_removes_inequality() {
76 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") 76 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }")
77 } 77 }
78 78
79 #[test] 79 #[test]
80 fn demorgan_general_case() { 80 fn demorgan_general_case() {
81 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }") 81 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }")
82 } 82 }
83 83
84 #[test] 84 #[test]
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index f6d25579e..edf96d50e 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -298,7 +298,7 @@ mod tests {
298 } 298 }
299 ", 299 ",
300 r" 300 r"
301 <|>use PubMod::PubStruct; 301 use PubMod::PubStruct;
302 302
303 PubStruct 303 PubStruct
304 304
@@ -329,7 +329,7 @@ mod tests {
329 macro_rules! foo { 329 macro_rules! foo {
330 ($i:ident) => { fn foo(a: $i) {} } 330 ($i:ident) => { fn foo(a: $i) {} }
331 } 331 }
332 foo!(Pub<|>Struct); 332 foo!(PubStruct);
333 333
334 pub mod PubMod { 334 pub mod PubMod {
335 pub struct PubStruct; 335 pub struct PubStruct;
@@ -360,7 +360,7 @@ mod tests {
360 use PubMod::{PubStruct2, PubStruct1}; 360 use PubMod::{PubStruct2, PubStruct1};
361 361
362 struct Test { 362 struct Test {
363 test: Pub<|>Struct2<u8>, 363 test: PubStruct2<u8>,
364 } 364 }
365 365
366 pub mod PubMod { 366 pub mod PubMod {
@@ -393,7 +393,7 @@ mod tests {
393 r" 393 r"
394 use PubMod3::PubStruct; 394 use PubMod3::PubStruct;
395 395
396 PubSt<|>ruct 396 PubStruct
397 397
398 pub mod PubMod1 { 398 pub mod PubMod1 {
399 pub struct PubStruct; 399 pub struct PubStruct;
@@ -474,7 +474,7 @@ mod tests {
474 r" 474 r"
475 use PubMod::test_function; 475 use PubMod::test_function;
476 476
477 test_function<|> 477 test_function
478 478
479 pub mod PubMod { 479 pub mod PubMod {
480 pub fn test_function() {}; 480 pub fn test_function() {};
@@ -501,7 +501,7 @@ mod tests {
501 r"use crate_with_macro::foo; 501 r"use crate_with_macro::foo;
502 502
503fn main() { 503fn main() {
504 foo<|> 504 foo
505} 505}
506", 506",
507 ); 507 );
@@ -587,7 +587,7 @@ fn main() {
587 } 587 }
588 588
589 fn main() { 589 fn main() {
590 TestStruct::test_function<|> 590 TestStruct::test_function
591 } 591 }
592 ", 592 ",
593 ); 593 );
@@ -620,7 +620,7 @@ fn main() {
620 } 620 }
621 621
622 fn main() { 622 fn main() {
623 TestStruct::TEST_CONST<|> 623 TestStruct::TEST_CONST
624 } 624 }
625 ", 625 ",
626 ); 626 );
@@ -659,7 +659,7 @@ fn main() {
659 } 659 }
660 660
661 fn main() { 661 fn main() {
662 test_mod::TestStruct::test_function<|> 662 test_mod::TestStruct::test_function
663 } 663 }
664 ", 664 ",
665 ); 665 );
@@ -730,7 +730,7 @@ fn main() {
730 } 730 }
731 731
732 fn main() { 732 fn main() {
733 test_mod::TestStruct::TEST_CONST<|> 733 test_mod::TestStruct::TEST_CONST
734 } 734 }
735 ", 735 ",
736 ); 736 );
@@ -803,7 +803,7 @@ fn main() {
803 803
804 fn main() { 804 fn main() {
805 let test_struct = test_mod::TestStruct {}; 805 let test_struct = test_mod::TestStruct {};
806 test_struct.test_meth<|>od() 806 test_struct.test_method()
807 } 807 }
808 ", 808 ",
809 ); 809 );
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index fbe459c9c..c21d75be0 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -118,17 +118,13 @@ mod tests {
118 118
119 #[test] 119 #[test]
120 fn change_visibility_adds_pub_crate_to_items() { 120 fn change_visibility_adds_pub_crate_to_items() {
121 check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}"); 121 check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}");
122 check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) f<|>n foo() {}"); 122 check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}");
123 check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}"); 123 check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}");
124 check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}"); 124 check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}");
125 check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}"); 125 check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}");
126 check_assist(change_visibility, "m<|>od {}", "pub(crate) m<|>od {}"); 126 check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}");
127 check_assist( 127 check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}");
128 change_visibility,
129 "unsafe f<|>n foo() {}",
130 "pub(crate) unsafe f<|>n foo() {}",
131 );
132 } 128 }
133 129
134 #[test] 130 #[test]
@@ -136,9 +132,9 @@ mod tests {
136 check_assist( 132 check_assist(
137 change_visibility, 133 change_visibility,
138 r"struct S { <|>field: u32 }", 134 r"struct S { <|>field: u32 }",
139 r"struct S { <|>pub(crate) field: u32 }", 135 r"struct S { pub(crate) field: u32 }",
140 ); 136 );
141 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); 137 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )");
142 } 138 }
143 139
144 #[test] 140 #[test]
@@ -152,17 +148,17 @@ mod tests {
152 148
153 #[test] 149 #[test]
154 fn change_visibility_pub_to_pub_crate() { 150 fn change_visibility_pub_to_pub_crate() {
155 check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}") 151 check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}")
156 } 152 }
157 153
158 #[test] 154 #[test]
159 fn change_visibility_pub_crate_to_pub() { 155 fn change_visibility_pub_crate_to_pub() {
160 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}") 156 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}")
161 } 157 }
162 158
163 #[test] 159 #[test]
164 fn change_visibility_const() { 160 fn change_visibility_const() {
165 check_assist(change_visibility, "<|>const FOO = 3u8;", "<|>pub(crate) const FOO = 3u8;"); 161 check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;");
166 } 162 }
167 163
168 #[test] 164 #[test]
@@ -183,7 +179,7 @@ mod tests {
183 // comments 179 // comments
184 180
185 #[derive(Debug)] 181 #[derive(Debug)]
186 <|>pub(crate) struct Foo; 182 pub(crate) struct Foo;
187 ", 183 ",
188 ) 184 )
189 } 185 }
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 66b296081..4cc75a7ce 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -97,7 +97,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
97 } 97 }
98 98
99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
100 let cursor_position = ctx.offset();
101 100
102 let target = if_expr.syntax().text_range(); 101 let target = if_expr.syntax().text_range();
103 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 102 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
@@ -148,7 +147,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
148 } 147 }
149 }; 148 };
150 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
151 edit.set_cursor(cursor_position);
152 150
153 fn replace( 151 fn replace(
154 new_expr: &SyntaxNode, 152 new_expr: &SyntaxNode,
@@ -207,7 +205,7 @@ mod tests {
207 r#" 205 r#"
208 fn main() { 206 fn main() {
209 bar(); 207 bar();
210 if<|> !true { 208 if !true {
211 return; 209 return;
212 } 210 }
213 foo(); 211 foo();
@@ -237,7 +235,7 @@ mod tests {
237 r#" 235 r#"
238 fn main(n: Option<String>) { 236 fn main(n: Option<String>) {
239 bar(); 237 bar();
240 le<|>t n = match n { 238 let n = match n {
241 Some(it) => it, 239 Some(it) => it,
242 _ => return, 240 _ => return,
243 }; 241 };
@@ -263,7 +261,7 @@ mod tests {
263 "#, 261 "#,
264 r#" 262 r#"
265 fn main() { 263 fn main() {
266 le<|>t x = match Err(92) { 264 let x = match Err(92) {
267 Ok(it) => it, 265 Ok(it) => it,
268 _ => return, 266 _ => return,
269 }; 267 };
@@ -291,7 +289,7 @@ mod tests {
291 r#" 289 r#"
292 fn main(n: Option<String>) { 290 fn main(n: Option<String>) {
293 bar(); 291 bar();
294 le<|>t n = match n { 292 let n = match n {
295 Ok(it) => it, 293 Ok(it) => it,
296 _ => return, 294 _ => return,
297 }; 295 };
@@ -321,7 +319,7 @@ mod tests {
321 r#" 319 r#"
322 fn main() { 320 fn main() {
323 while true { 321 while true {
324 if<|> !true { 322 if !true {
325 continue; 323 continue;
326 } 324 }
327 foo(); 325 foo();
@@ -349,7 +347,7 @@ mod tests {
349 r#" 347 r#"
350 fn main() { 348 fn main() {
351 while true { 349 while true {
352 le<|>t n = match n { 350 let n = match n {
353 Some(it) => it, 351 Some(it) => it,
354 _ => continue, 352 _ => continue,
355 }; 353 };
@@ -378,7 +376,7 @@ mod tests {
378 r#" 376 r#"
379 fn main() { 377 fn main() {
380 loop { 378 loop {
381 if<|> !true { 379 if !true {
382 continue; 380 continue;
383 } 381 }
384 foo(); 382 foo();
@@ -406,7 +404,7 @@ mod tests {
406 r#" 404 r#"
407 fn main() { 405 fn main() {
408 loop { 406 loop {
409 le<|>t n = match n { 407 let n = match n {
410 Some(it) => it, 408 Some(it) => it,
411 _ => continue, 409 _ => continue,
412 }; 410 };
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index 692ba4895..573196576 100644
--- a/crates/ra_assists/src/handlers/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
@@ -85,17 +85,13 @@ mod tests {
85 check_assist( 85 check_assist(
86 flip_binexpr, 86 flip_binexpr,
87 "fn f() { let res = 1 ==<|> 2; }", 87 "fn f() { let res = 1 ==<|> 2; }",
88 "fn f() { let res = 2 ==<|> 1; }", 88 "fn f() { let res = 2 == 1; }",
89 ) 89 )
90 } 90 }
91 91
92 #[test] 92 #[test]
93 fn flip_binexpr_works_for_gt() { 93 fn flip_binexpr_works_for_gt() {
94 check_assist( 94 check_assist(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", "fn f() { let res = 2 < 1; }")
95 flip_binexpr,
96 "fn f() { let res = 1 ><|> 2; }",
97 "fn f() { let res = 2 <<|> 1; }",
98 )
99 } 95 }
100 96
101 #[test] 97 #[test]
@@ -103,7 +99,7 @@ mod tests {
103 check_assist( 99 check_assist(
104 flip_binexpr, 100 flip_binexpr,
105 "fn f() { let res = 1 <=<|> 2; }", 101 "fn f() { let res = 1 <=<|> 2; }",
106 "fn f() { let res = 2 >=<|> 1; }", 102 "fn f() { let res = 2 >= 1; }",
107 ) 103 )
108 } 104 }
109 105
@@ -112,7 +108,7 @@ mod tests {
112 check_assist( 108 check_assist(
113 flip_binexpr, 109 flip_binexpr,
114 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", 110 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
115 "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", 111 "fn f() { let res = (2 + 2) == (1 + 1); }",
116 ) 112 )
117 } 113 }
118 114
@@ -132,7 +128,7 @@ mod tests {
132 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { 128 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
133 match other.downcast_ref::<Self>() { 129 match other.downcast_ref::<Self>() {
134 None => false, 130 None => false,
135 Some(it) => self ==<|> it, 131 Some(it) => self == it,
136 } 132 }
137 } 133 }
138 "#, 134 "#,
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index dfe2a7fed..a57a1c463 100644
--- a/crates/ra_assists/src/handlers/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
@@ -45,7 +45,7 @@ mod tests {
45 check_assist( 45 check_assist(
46 flip_comma, 46 flip_comma,
47 "fn foo(x: i32,<|> y: Result<(), ()>) {}", 47 "fn foo(x: i32,<|> y: Result<(), ()>) {}",
48 "fn foo(y: Result<(), ()>,<|> x: i32) {}", 48 "fn foo(y: Result<(), ()>, x: i32) {}",
49 ) 49 )
50 } 50 }
51 51
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index 8a08702ab..0115adc8b 100644
--- a/crates/ra_assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
@@ -60,7 +60,7 @@ mod tests {
60 check_assist( 60 check_assist(
61 flip_trait_bound, 61 flip_trait_bound,
62 "struct S<T> where T: A <|>+ B { }", 62 "struct S<T> where T: A <|>+ B { }",
63 "struct S<T> where T: B <|>+ A { }", 63 "struct S<T> where T: B + A { }",
64 ) 64 )
65 } 65 }
66 66
@@ -69,13 +69,13 @@ mod tests {
69 check_assist( 69 check_assist(
70 flip_trait_bound, 70 flip_trait_bound,
71 "impl X for S<T> where T: A +<|> B { }", 71 "impl X for S<T> where T: A +<|> B { }",
72 "impl X for S<T> where T: B +<|> A { }", 72 "impl X for S<T> where T: B + A { }",
73 ) 73 )
74 } 74 }
75 75
76 #[test] 76 #[test]
77 fn flip_trait_bound_works_for_fn() { 77 fn flip_trait_bound_works_for_fn() {
78 check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B <|>+ A>(t: T) { }") 78 check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }")
79 } 79 }
80 80
81 #[test] 81 #[test]
@@ -83,7 +83,7 @@ mod tests {
83 check_assist( 83 check_assist(
84 flip_trait_bound, 84 flip_trait_bound,
85 "fn f<T>(t: T) where T: A +<|> B { }", 85 "fn f<T>(t: T) where T: A +<|> B { }",
86 "fn f<T>(t: T) where T: B +<|> A { }", 86 "fn f<T>(t: T) where T: B + A { }",
87 ) 87 )
88 } 88 }
89 89
@@ -92,7 +92,7 @@ mod tests {
92 check_assist( 92 check_assist(
93 flip_trait_bound, 93 flip_trait_bound,
94 "fn f<T>(t: T) where T: A <|>+ 'static { }", 94 "fn f<T>(t: T) where T: A <|>+ 'static { }",
95 "fn f<T>(t: T) where T: 'static <|>+ A { }", 95 "fn f<T>(t: T) where T: 'static + A { }",
96 ) 96 )
97 } 97 }
98 98
@@ -101,7 +101,7 @@ mod tests {
101 check_assist( 101 check_assist(
102 flip_trait_bound, 102 flip_trait_bound,
103 "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }", 103 "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }",
104 "struct S<T> where T: b_mod::B<T> <|>+ A<T> + C<T> { }", 104 "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }",
105 ) 105 )
106 } 106 }
107 107
@@ -110,7 +110,7 @@ mod tests {
110 check_assist( 110 check_assist(
111 flip_trait_bound, 111 flip_trait_bound,
112 "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }", 112 "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }",
113 "struct S<T> where T: A + B + C + D + E + G +<|> F + H + I + J { }", 113 "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }",
114 ) 114 )
115 } 115 }
116} 116}
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 46d675a4e..d26e68847 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -116,7 +116,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() };
117 builder.replace(desc.file_range.range, replacement) 117 builder.replace(desc.file_range.range, replacement)
118 } 118 }
119 builder.set_cursor(delete_range.start())
120 }) 119 })
121} 120}
122 121
@@ -149,7 +148,7 @@ fn foo() {
149 r" 148 r"
150fn bar(a: usize) {} 149fn bar(a: usize) {}
151fn foo() { 150fn foo() {
152 <|>1 + 1; 151 1 + 1;
153 if 1 > 10 { 152 if 1 > 10 {
154 } 153 }
155 154
@@ -183,7 +182,7 @@ fn foo() {
183 r" 182 r"
184fn bar(a: usize) {} 183fn bar(a: usize) {}
185fn foo() { 184fn foo() {
186 <|>(1 + 1) + 1; 185 (1 + 1) + 1;
187 if (1 + 1) > 10 { 186 if (1 + 1) > 10 {
188 } 187 }
189 188
@@ -217,7 +216,7 @@ fn foo() {
217 r" 216 r"
218fn bar(a: usize) {} 217fn bar(a: usize) {}
219fn foo() { 218fn foo() {
220 <|>bar(1) + 1; 219 bar(1) + 1;
221 if bar(1) > 10 { 220 if bar(1) > 10 {
222 } 221 }
223 222
@@ -251,7 +250,7 @@ fn foo() {
251 r" 250 r"
252fn bar(a: usize): usize { a } 251fn bar(a: usize): usize { a }
253fn foo() { 252fn foo() {
254 <|>(bar(1) as u64) + 1; 253 (bar(1) as u64) + 1;
255 if (bar(1) as u64) > 10 { 254 if (bar(1) as u64) > 10 {
256 } 255 }
257 256
@@ -283,7 +282,7 @@ fn foo() {
283}", 282}",
284 r" 283 r"
285fn foo() { 284fn foo() {
286 <|>{ 10 + 1 } + 1; 285 { 10 + 1 } + 1;
287 if { 10 + 1 } > 10 { 286 if { 10 + 1 } > 10 {
288 } 287 }
289 288
@@ -315,7 +314,7 @@ fn foo() {
315}", 314}",
316 r" 315 r"
317fn foo() { 316fn foo() {
318 <|>( 10 + 1 ) + 1; 317 ( 10 + 1 ) + 1;
319 if ( 10 + 1 ) > 10 { 318 if ( 10 + 1 ) > 10 {
320 } 319 }
321 320
@@ -353,7 +352,7 @@ fn foo() {
353}", 352}",
354 r" 353 r"
355fn foo() { 354fn foo() {
356 <|>let b = bar(10 + 1) * 10; 355 let b = bar(10 + 1) * 10;
357 let c = bar(10 + 1) as usize; 356 let c = bar(10 + 1) as usize;
358}", 357}",
359 ); 358 );
@@ -373,7 +372,7 @@ fn foo() {
373 r" 372 r"
374fn foo() { 373fn foo() {
375 let x = vec![1, 2, 3]; 374 let x = vec![1, 2, 3];
376 <|>let b = x[0] * 10; 375 let b = x[0] * 10;
377 let c = x[0] as usize; 376 let c = x[0] as usize;
378}", 377}",
379 ); 378 );
@@ -393,7 +392,7 @@ fn foo() {
393 r" 392 r"
394fn foo() { 393fn foo() {
395 let bar = vec![1]; 394 let bar = vec![1];
396 <|>let b = bar.len() * 10; 395 let b = bar.len() * 10;
397 let c = bar.len() as usize; 396 let c = bar.len() as usize;
398}", 397}",
399 ); 398 );
@@ -421,7 +420,7 @@ struct Bar {
421 420
422fn foo() { 421fn foo() {
423 let bar = Bar { foo: 1 }; 422 let bar = Bar { foo: 1 };
424 <|>let b = bar.foo * 10; 423 let b = bar.foo * 10;
425 let c = bar.foo as usize; 424 let c = bar.foo as usize;
426}", 425}",
427 ); 426 );
@@ -442,7 +441,7 @@ fn foo() -> Option<usize> {
442 r" 441 r"
443fn foo() -> Option<usize> { 442fn foo() -> Option<usize> {
444 let bar = Some(1); 443 let bar = Some(1);
445 <|>let b = bar? * 10; 444 let b = bar? * 10;
446 let c = bar? as usize; 445 let c = bar? as usize;
447 None 446 None
448}", 447}",
@@ -462,7 +461,7 @@ fn foo() {
462 r" 461 r"
463fn foo() { 462fn foo() {
464 let bar = 10; 463 let bar = 10;
465 <|>let b = &bar * 10; 464 let b = &bar * 10;
466}", 465}",
467 ); 466 );
468 } 467 }
@@ -478,7 +477,7 @@ fn foo() {
478}", 477}",
479 r" 478 r"
480fn foo() { 479fn foo() {
481 <|>let b = (10, 20)[0]; 480 let b = (10, 20)[0];
482}", 481}",
483 ); 482 );
484 } 483 }
@@ -494,7 +493,7 @@ fn foo() {
494}", 493}",
495 r" 494 r"
496fn foo() { 495fn foo() {
497 <|>let b = [1, 2, 3].len(); 496 let b = [1, 2, 3].len();
498}", 497}",
499 ); 498 );
500 } 499 }
@@ -511,7 +510,7 @@ fn foo() {
511}", 510}",
512 r" 511 r"
513fn foo() { 512fn foo() {
514 <|>let b = (10 + 20) * 10; 513 let b = (10 + 20) * 10;
515 let c = (10 + 20) as usize; 514 let c = (10 + 20) as usize;
516}", 515}",
517 ); 516 );
@@ -531,7 +530,7 @@ fn foo() {
531 r" 530 r"
532fn foo() { 531fn foo() {
533 let d = 10; 532 let d = 10;
534 <|>let b = d * 10; 533 let b = d * 10;
535 let c = d as usize; 534 let c = d as usize;
536}", 535}",
537 ); 536 );
@@ -549,7 +548,7 @@ fn foo() {
549}", 548}",
550 r" 549 r"
551fn foo() { 550fn foo() {
552 <|>let b = { 10 } * 10; 551 let b = { 10 } * 10;
553 let c = { 10 } as usize; 552 let c = { 10 } as usize;
554}", 553}",
555 ); 554 );
@@ -569,7 +568,7 @@ fn foo() {
569}", 568}",
570 r" 569 r"
571fn foo() { 570fn foo() {
572 <|>let b = (10 + 20) * 10; 571 let b = (10 + 20) * 10;
573 let c = (10 + 20, 20); 572 let c = (10 + 20, 20);
574 let d = [10 + 20, 10]; 573 let d = [10 + 20, 10];
575 let e = (10 + 20); 574 let e = (10 + 20);
@@ -588,7 +587,7 @@ fn foo() {
588}", 587}",
589 r" 588 r"
590fn foo() { 589fn foo() {
591 <|>for i in vec![10, 20] {} 590 for i in vec![10, 20] {}
592}", 591}",
593 ); 592 );
594 } 593 }
@@ -604,7 +603,7 @@ fn foo() {
604}", 603}",
605 r" 604 r"
606fn foo() { 605fn foo() {
607 <|>while 1 > 0 {} 606 while 1 > 0 {}
608}", 607}",
609 ); 608 );
610 } 609 }
@@ -622,7 +621,7 @@ fn foo() {
622}", 621}",
623 r" 622 r"
624fn foo() { 623fn foo() {
625 <|>loop { 624 loop {
626 break 1 + 1; 625 break 1 + 1;
627 } 626 }
628}", 627}",
@@ -640,7 +639,7 @@ fn foo() {
640}", 639}",
641 r" 640 r"
642fn foo() { 641fn foo() {
643 <|>return 1 > 0; 642 return 1 > 0;
644}", 643}",
645 ); 644 );
646 } 645 }
@@ -656,7 +655,7 @@ fn foo() {
656}", 655}",
657 r" 656 r"
658fn foo() { 657fn foo() {
659 <|>match 1 > 0 {} 658 match 1 > 0 {}
660}", 659}",
661 ); 660 );
662 } 661 }
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index 56c610fed..31d6539f7 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, 4 BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
5 WHITESPACE, 5 WHITESPACE,
6 }, 6 },
7 SyntaxNode, TextSize, 7 SyntaxNode,
8}; 8};
9use stdx::format_to; 9use stdx::format_to;
10use test_utils::mark; 10use test_utils::mark;
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
23// -> 23// ->
24// ``` 24// ```
25// fn main() { 25// fn main() {
26// let var_name = (1 + 2); 26// let $0var_name = (1 + 2);
27// var_name * 4; 27// var_name * 4;
28// } 28// }
29// ``` 29// ```
@@ -46,14 +46,13 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
46 acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { 46 acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| {
47 let mut buf = String::new(); 47 let mut buf = String::new();
48 48
49 let cursor_offset = if wrap_in_block { 49 if wrap_in_block {
50 buf.push_str("{ let var_name = "); 50 buf.push_str("{ let var_name = ");
51 TextSize::of("{ let ")
52 } else { 51 } else {
53 buf.push_str("let var_name = "); 52 buf.push_str("let var_name = ");
54 TextSize::of("let ")
55 }; 53 };
56 format_to!(buf, "{}", expr.syntax()); 54 format_to!(buf, "{}", expr.syntax());
55
57 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 56 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
58 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 57 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
59 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 58 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
@@ -65,28 +64,43 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
65 if full_stmt.unwrap().semicolon_token().is_none() { 64 if full_stmt.unwrap().semicolon_token().is_none() {
66 buf.push_str(";"); 65 buf.push_str(";");
67 } 66 }
68 edit.replace(expr.syntax().text_range(), buf); 67 let offset = expr.syntax().text_range();
69 } else { 68 match ctx.config.snippet_cap {
70 buf.push_str(";"); 69 Some(cap) => {
71 70 let snip = buf.replace("let var_name", "let $0var_name");
72 // We want to maintain the indent level, 71 edit.replace_snippet(cap, offset, snip)
73 // but we do not want to duplicate possible 72 }
74 // extra newlines in the indent block 73 None => edit.replace(offset, buf),
75 let text = indent.text();
76 if text.starts_with('\n') {
77 buf.push_str("\n");
78 buf.push_str(text.trim_start_matches('\n'));
79 } else {
80 buf.push_str(text);
81 } 74 }
75 return;
76 }
82 77
83 edit.replace(expr.syntax().text_range(), "var_name".to_string()); 78 buf.push_str(";");
84 edit.insert(anchor_stmt.text_range().start(), buf); 79
85 if wrap_in_block { 80 // We want to maintain the indent level,
86 edit.insert(anchor_stmt.text_range().end(), " }"); 81 // but we do not want to duplicate possible
82 // extra newlines in the indent block
83 let text = indent.text();
84 if text.starts_with('\n') {
85 buf.push_str("\n");
86 buf.push_str(text.trim_start_matches('\n'));
87 } else {
88 buf.push_str(text);
89 }
90
91 edit.replace(expr.syntax().text_range(), "var_name".to_string());
92 let offset = anchor_stmt.text_range().start();
93 match ctx.config.snippet_cap {
94 Some(cap) => {
95 let snip = buf.replace("let var_name", "let $0var_name");
96 edit.insert_snippet(cap, offset, snip)
87 } 97 }
98 None => edit.insert(offset, buf),
99 }
100
101 if wrap_in_block {
102 edit.insert(anchor_stmt.text_range().end(), " }");
88 } 103 }
89 edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset);
90 }) 104 })
91} 105}
92 106
@@ -144,15 +158,15 @@ mod tests {
144 fn test_introduce_var_simple() { 158 fn test_introduce_var_simple() {
145 check_assist( 159 check_assist(
146 introduce_variable, 160 introduce_variable,
147 " 161 r#"
148fn foo() { 162fn foo() {
149 foo(<|>1 + 1<|>); 163 foo(<|>1 + 1<|>);
150}", 164}"#,
151 " 165 r#"
152fn foo() { 166fn foo() {
153 let <|>var_name = 1 + 1; 167 let $0var_name = 1 + 1;
154 foo(var_name); 168 foo(var_name);
155}", 169}"#,
156 ); 170 );
157 } 171 }
158 172
@@ -167,14 +181,14 @@ fn foo() {
167 mark::check!(test_introduce_var_expr_stmt); 181 mark::check!(test_introduce_var_expr_stmt);
168 check_assist( 182 check_assist(
169 introduce_variable, 183 introduce_variable,
170 " 184 r#"
171fn foo() { 185fn foo() {
172 <|>1 + 1<|>; 186 <|>1 + 1<|>;
173}", 187}"#,
174 " 188 r#"
175fn foo() { 189fn foo() {
176 let <|>var_name = 1 + 1; 190 let $0var_name = 1 + 1;
177}", 191}"#,
178 ); 192 );
179 check_assist( 193 check_assist(
180 introduce_variable, 194 introduce_variable,
@@ -185,7 +199,7 @@ fn foo() {
185}", 199}",
186 " 200 "
187fn foo() { 201fn foo() {
188 let <|>var_name = { let x = 0; x }; 202 let $0var_name = { let x = 0; x };
189 something_else(); 203 something_else();
190}", 204}",
191 ); 205 );
@@ -201,7 +215,7 @@ fn foo() {
201}", 215}",
202 " 216 "
203fn foo() { 217fn foo() {
204 let <|>var_name = 1; 218 let $0var_name = 1;
205 var_name + 1; 219 var_name + 1;
206}", 220}",
207 ); 221 );
@@ -218,7 +232,7 @@ fn foo() {
218}", 232}",
219 " 233 "
220fn foo() { 234fn foo() {
221 let <|>var_name = 1 + 1; 235 let $0var_name = 1 + 1;
222 bar(var_name) 236 bar(var_name)
223}", 237}",
224 ); 238 );
@@ -230,7 +244,7 @@ fn foo() {
230}", 244}",
231 " 245 "
232fn foo() { 246fn foo() {
233 let <|>var_name = bar(1 + 1); 247 let $0var_name = bar(1 + 1);
234 var_name 248 var_name
235}", 249}",
236 ) 250 )
@@ -253,7 +267,7 @@ fn main() {
253fn main() { 267fn main() {
254 let x = true; 268 let x = true;
255 let tuple = match x { 269 let tuple = match x {
256 true => { let <|>var_name = 2 + 2; (var_name, true) } 270 true => { let $0var_name = 2 + 2; (var_name, true) }
257 _ => (0, false) 271 _ => (0, false)
258 }; 272 };
259} 273}
@@ -283,7 +297,7 @@ fn main() {
283 let tuple = match x { 297 let tuple = match x {
284 true => { 298 true => {
285 let y = 1; 299 let y = 1;
286 let <|>var_name = 2 + y; 300 let $0var_name = 2 + y;
287 (var_name, true) 301 (var_name, true)
288 } 302 }
289 _ => (0, false) 303 _ => (0, false)
@@ -304,7 +318,7 @@ fn main() {
304", 318",
305 " 319 "
306fn main() { 320fn main() {
307 let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; 321 let lambda = |x: u32| { let $0var_name = x * 2; var_name };
308} 322}
309", 323",
310 ); 324 );
@@ -321,7 +335,7 @@ fn main() {
321", 335",
322 " 336 "
323fn main() { 337fn main() {
324 let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; 338 let lambda = |x: u32| { let $0var_name = x * 2; var_name };
325} 339}
326", 340",
327 ); 341 );
@@ -338,7 +352,7 @@ fn main() {
338", 352",
339 " 353 "
340fn main() { 354fn main() {
341 let <|>var_name = Some(true); 355 let $0var_name = Some(true);
342 let o = var_name; 356 let o = var_name;
343} 357}
344", 358",
@@ -356,7 +370,7 @@ fn main() {
356", 370",
357 " 371 "
358fn main() { 372fn main() {
359 let <|>var_name = bar.foo(); 373 let $0var_name = bar.foo();
360 let v = var_name; 374 let v = var_name;
361} 375}
362", 376",
@@ -374,7 +388,7 @@ fn foo() -> u32 {
374", 388",
375 " 389 "
376fn foo() -> u32 { 390fn foo() -> u32 {
377 let <|>var_name = 2 + 2; 391 let $0var_name = 2 + 2;
378 return var_name; 392 return var_name;
379} 393}
380", 394",
@@ -396,7 +410,7 @@ fn foo() -> u32 {
396fn foo() -> u32 { 410fn foo() -> u32 {
397 411
398 412
399 let <|>var_name = 2 + 2; 413 let $0var_name = 2 + 2;
400 return var_name; 414 return var_name;
401} 415}
402", 416",
@@ -413,7 +427,7 @@ fn foo() -> u32 {
413 " 427 "
414fn foo() -> u32 { 428fn foo() -> u32 {
415 429
416 let <|>var_name = 2 + 2; 430 let $0var_name = 2 + 2;
417 return var_name; 431 return var_name;
418} 432}
419", 433",
@@ -438,7 +452,7 @@ fn foo() -> u32 {
438 // bar 452 // bar
439 453
440 454
441 let <|>var_name = 2 + 2; 455 let $0var_name = 2 + 2;
442 return var_name; 456 return var_name;
443} 457}
444", 458",
@@ -459,7 +473,7 @@ fn main() {
459 " 473 "
460fn main() { 474fn main() {
461 let result = loop { 475 let result = loop {
462 let <|>var_name = 2 + 2; 476 let $0var_name = 2 + 2;
463 break var_name; 477 break var_name;
464 }; 478 };
465} 479}
@@ -478,7 +492,7 @@ fn main() {
478", 492",
479 " 493 "
480fn main() { 494fn main() {
481 let <|>var_name = 0f32 as u32; 495 let $0var_name = 0f32 as u32;
482 let v = var_name; 496 let v = var_name;
483} 497}
484", 498",
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs
index 527c7caef..59d278eb9 100644
--- a/crates/ra_assists/src/handlers/invert_if.rs
+++ b/crates/ra_assists/src/handlers/invert_if.rs
@@ -72,7 +72,7 @@ mod tests {
72 check_assist( 72 check_assist(
73 invert_if, 73 invert_if,
74 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }", 74 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }",
75 "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }", 75 "fn f() { if x == 3 { 3 + 2 } else { 1 } }",
76 ) 76 )
77 } 77 }
78 78
@@ -81,7 +81,7 @@ mod tests {
81 check_assist( 81 check_assist(
82 invert_if, 82 invert_if,
83 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }", 83 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }",
84 "fn f() { <|>if cond { 1 } else { 3 * 2 } }", 84 "fn f() { if cond { 1 } else { 3 * 2 } }",
85 ) 85 )
86 } 86 }
87 87
@@ -90,7 +90,7 @@ mod tests {
90 check_assist( 90 check_assist(
91 invert_if, 91 invert_if,
92 "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", 92 "fn f() { i<|>f cond { 3 * 2 } else { 1 } }",
93 "fn f() { i<|>f !cond { 1 } else { 3 * 2 } }", 93 "fn f() { if !cond { 1 } else { 3 * 2 } }",
94 ) 94 )
95 } 95 }
96 96
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index ac3e53c27..972d16241 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -58,8 +58,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
58 let target = tree.syntax().text_range(); 58 let target = tree.syntax().text_range();
59 acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { 59 acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| {
60 builder.rewrite(rewriter); 60 builder.rewrite(rewriter);
61 // FIXME: we only need because our diff is imprecise
62 builder.set_cursor(offset);
63 }) 61 })
64} 62}
65 63
@@ -142,7 +140,7 @@ use std::fmt<|>::Debug;
142use std::fmt::Display; 140use std::fmt::Display;
143", 141",
144 r" 142 r"
145use std::fmt<|>::{Debug, Display}; 143use std::fmt::{Debug, Display};
146", 144",
147 ) 145 )
148 } 146 }
@@ -156,7 +154,7 @@ use std::fmt::Debug;
156use std::fmt<|>::Display; 154use std::fmt<|>::Display;
157", 155",
158 r" 156 r"
159use std::fmt:<|>:{Display, Debug}; 157use std::fmt::{Display, Debug};
160", 158",
161 ); 159 );
162 } 160 }
@@ -169,7 +167,7 @@ use std::fmt:<|>:{Display, Debug};
169use std::{fmt<|>::Debug, fmt::Display}; 167use std::{fmt<|>::Debug, fmt::Display};
170", 168",
171 r" 169 r"
172use std::{fmt<|>::{Debug, Display}}; 170use std::{fmt::{Debug, Display}};
173", 171",
174 ); 172 );
175 check_assist( 173 check_assist(
@@ -178,7 +176,7 @@ use std::{fmt<|>::{Debug, Display}};
178use std::{fmt::Debug, fmt<|>::Display}; 176use std::{fmt::Debug, fmt<|>::Display};
179", 177",
180 r" 178 r"
181use std::{fmt::<|>{Display, Debug}}; 179use std::{fmt::{Display, Debug}};
182", 180",
183 ); 181 );
184 } 182 }
@@ -192,7 +190,7 @@ use std<|>::cell::*;
192use std::str; 190use std::str;
193", 191",
194 r" 192 r"
195use std<|>::{cell::*, str}; 193use std::{cell::*, str};
196", 194",
197 ) 195 )
198 } 196 }
@@ -206,7 +204,7 @@ use std<|>::cell::*;
206use std::str::*; 204use std::str::*;
207", 205",
208 r" 206 r"
209use std<|>::{cell::*, str::*}; 207use std::{cell::*, str::*};
210", 208",
211 ) 209 )
212 } 210 }
@@ -222,7 +220,7 @@ use foo::baz;
222/// Doc comment 220/// Doc comment
223", 221",
224 r" 222 r"
225use foo<|>::{bar, baz}; 223use foo::{bar, baz};
226 224
227/// Doc comment 225/// Doc comment
228", 226",
@@ -241,7 +239,7 @@ use {
241", 239",
242 r" 240 r"
243use { 241use {
244 foo<|>::{bar, baz}, 242 foo::{bar, baz},
245}; 243};
246", 244",
247 ); 245 );
@@ -255,7 +253,7 @@ use {
255", 253",
256 r" 254 r"
257use { 255use {
258 foo::{bar<|>, baz}, 256 foo::{bar, baz},
259}; 257};
260", 258",
261 ); 259 );
@@ -272,7 +270,7 @@ use foo::<|>{
272}; 270};
273", 271",
274 r" 272 r"
275use foo::{<|> 273use foo::{
276 FooBar, 274 FooBar,
277bar::baz}; 275bar::baz};
278", 276",
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index d4e38aa6a..ca04ec671 100644
--- a/crates/ra_assists/src/handlers/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
@@ -3,7 +3,7 @@ use std::iter::successors;
3use ra_syntax::{ 3use ra_syntax::{
4 algo::neighbor, 4 algo::neighbor,
5 ast::{self, AstNode}, 5 ast::{self, AstNode},
6 Direction, TextSize, 6 Direction,
7}; 7};
8 8
9use crate::{AssistContext, AssistId, Assists, TextRange}; 9use crate::{AssistContext, AssistId, Assists, TextRange};
@@ -41,17 +41,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
41 let current_expr = current_arm.expr()?; 41 let current_expr = current_arm.expr()?;
42 let current_text_range = current_arm.syntax().text_range(); 42 let current_text_range = current_arm.syntax().text_range();
43 43
44 enum CursorPos {
45 InExpr(TextSize),
46 InPat(TextSize),
47 }
48 let cursor_pos = ctx.offset();
49 let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) {
50 CursorPos::InExpr(current_text_range.end() - cursor_pos)
51 } else {
52 CursorPos::InPat(cursor_pos)
53 };
54
55 // We check if the following match arms match this one. We could, but don't, 44 // We check if the following match arms match this one. We could, but don't,
56 // compare to the previous match arm as well. 45 // compare to the previous match arm as well.
57 let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next)) 46 let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
@@ -87,10 +76,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
87 let start = arms_to_merge.first().unwrap().syntax().text_range().start(); 76 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
88 let end = arms_to_merge.last().unwrap().syntax().text_range().end(); 77 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
89 78
90 edit.set_cursor(match cursor_pos {
91 CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset,
92 CursorPos::InPat(offset) => offset,
93 });
94 edit.replace(TextRange::new(start, end), arm); 79 edit.replace(TextRange::new(start, end), arm);
95 }) 80 })
96} 81}
@@ -132,7 +117,7 @@ mod tests {
132 fn main() { 117 fn main() {
133 let x = X::A; 118 let x = X::A;
134 let y = match x { 119 let y = match x {
135 X::A | X::B => { 1i32<|> } 120 X::A | X::B => { 1i32 }
136 X::C => { 2i32 } 121 X::C => { 2i32 }
137 } 122 }
138 } 123 }
@@ -164,7 +149,7 @@ mod tests {
164 fn main() { 149 fn main() {
165 let x = X::A; 150 let x = X::A;
166 let y = match x { 151 let y = match x {
167 X::A | X::B | X::C | X::D => {<|> 1i32 }, 152 X::A | X::B | X::C | X::D => { 1i32 },
168 X::E => { 2i32 }, 153 X::E => { 2i32 },
169 } 154 }
170 } 155 }
@@ -197,7 +182,7 @@ mod tests {
197 let x = X::A; 182 let x = X::A;
198 let y = match x { 183 let y = match x {
199 X::A => { 1i32 }, 184 X::A => { 1i32 },
200 _ => { 2i<|>32 } 185 _ => { 2i32 }
201 } 186 }
202 } 187 }
203 "#, 188 "#,
@@ -226,7 +211,7 @@ mod tests {
226 211
227 fn main() { 212 fn main() {
228 match X::A { 213 match X::A {
229 X::A<|> | X::B | X::C => 92, 214 X::A | X::B | X::C => 92,
230 X::D => 62, 215 X::D => 62,
231 _ => panic!(), 216 _ => panic!(),
232 } 217 }
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
index a41aacfc3..be2a7eddc 100644
--- a/crates/ra_assists/src/handlers/move_bounds.rs
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
@@ -99,7 +99,7 @@ mod tests {
99 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {} 99 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {}
100 "#, 100 "#,
101 r#" 101 r#"
102 fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {} 102 fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}
103 "#, 103 "#,
104 ); 104 );
105 } 105 }
@@ -112,7 +112,7 @@ mod tests {
112 impl<U: u32, <|>T> A<U, T> {} 112 impl<U: u32, <|>T> A<U, T> {}
113 "#, 113 "#,
114 r#" 114 r#"
115 impl<U, <|>T> A<U, T> where U: u32 {} 115 impl<U, T> A<U, T> where U: u32 {}
116 "#, 116 "#,
117 ); 117 );
118 } 118 }
@@ -125,7 +125,7 @@ mod tests {
125 struct A<<|>T: Iterator<Item = u32>> {} 125 struct A<<|>T: Iterator<Item = u32>> {}
126 "#, 126 "#,
127 r#" 127 r#"
128 struct A<<|>T> where T: Iterator<Item = u32> {} 128 struct A<T> where T: Iterator<Item = u32> {}
129 "#, 129 "#,
130 ); 130 );
131 } 131 }
@@ -138,7 +138,7 @@ mod tests {
138 struct Pair<<|>T: u32>(T, T); 138 struct Pair<<|>T: u32>(T, T);
139 "#, 139 "#,
140 r#" 140 r#"
141 struct Pair<<|>T>(T, T) where T: u32; 141 struct Pair<T>(T, T) where T: u32;
142 "#, 142 "#,
143 ); 143 );
144 } 144 }
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs
index fc0335b57..7edcf0748 100644
--- a/crates/ra_assists/src/handlers/move_guard.rs
+++ b/crates/ra_assists/src/handlers/move_guard.rs
@@ -1,7 +1,6 @@
1use ra_syntax::{ 1use ra_syntax::{
2 ast, 2 ast::{AstNode, IfExpr, MatchArm},
3 ast::{AstNode, AstToken, IfExpr, MatchArm}, 3 SyntaxKind::WHITESPACE,
4 TextSize,
5}; 4};
6 5
7use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, Assists};
@@ -42,24 +41,15 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
42 41
43 let target = guard.syntax().text_range(); 42 let target = guard.syntax().text_range();
44 acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { 43 acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
45 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { 44 match space_before_guard {
46 Some(tok) => { 45 Some(element) if element.kind() == WHITESPACE => {
47 if ast::Whitespace::cast(tok.clone()).is_some() { 46 edit.delete(element.text_range());
48 let ele = tok.text_range();
49 edit.delete(ele);
50 ele.len()
51 } else {
52 TextSize::from(0)
53 }
54 } 47 }
55 _ => TextSize::from(0), 48 _ => (),
56 }; 49 };
57 50
58 edit.delete(guard.syntax().text_range()); 51 edit.delete(guard.syntax().text_range());
59 edit.replace_node_and_indent(arm_expr.syntax(), buf); 52 edit.replace_node_and_indent(arm_expr.syntax(), buf);
60 edit.set_cursor(
61 arm_expr.syntax().text_range().start() + TextSize::from(3) - offseting_amount,
62 );
63 }) 53 })
64} 54}
65 55
@@ -124,7 +114,6 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
124 } 114 }
125 115
126 edit.insert(match_pat.syntax().text_range().end(), buf); 116 edit.insert(match_pat.syntax().text_range().end(), buf);
127 edit.set_cursor(match_pat.syntax().text_range().end() + TextSize::from(1));
128 }, 117 },
129 ) 118 )
130} 119}
@@ -172,7 +161,7 @@ mod tests {
172 let t = 'a'; 161 let t = 'a';
173 let chars = "abcd"; 162 let chars = "abcd";
174 match t { 163 match t {
175 '\r' => if chars.clone().next() == Some('\n') { <|>false }, 164 '\r' => if chars.clone().next() == Some('\n') { false },
176 _ => true 165 _ => true
177 } 166 }
178 } 167 }
@@ -195,7 +184,7 @@ mod tests {
195 r#" 184 r#"
196 fn f() { 185 fn f() {
197 match x { 186 match x {
198 y @ 4 | y @ 5 => if y > 5 { <|>true }, 187 y @ 4 | y @ 5 => if y > 5 { true },
199 _ => false 188 _ => false
200 } 189 }
201 } 190 }
@@ -222,7 +211,7 @@ mod tests {
222 let t = 'a'; 211 let t = 'a';
223 let chars = "abcd"; 212 let chars = "abcd";
224 match t { 213 match t {
225 '\r' <|>if chars.clone().next() == Some('\n') => false, 214 '\r' if chars.clone().next() == Some('\n') => false,
226 _ => true 215 _ => true
227 } 216 }
228 } 217 }
@@ -266,7 +255,7 @@ mod tests {
266 let t = 'a'; 255 let t = 'a';
267 let chars = "abcd"; 256 let chars = "abcd";
268 match t { 257 match t {
269 '\r' <|>if chars.clone().next().is_some() => { }, 258 '\r' if chars.clone().next().is_some() => { },
270 _ => true 259 _ => true
271 } 260 }
272 } 261 }
@@ -296,7 +285,7 @@ mod tests {
296 let mut t = 'a'; 285 let mut t = 'a';
297 let chars = "abcd"; 286 let chars = "abcd";
298 match t { 287 match t {
299 '\r' <|>if chars.clone().next().is_some() => { 288 '\r' if chars.clone().next().is_some() => {
300 t = 'e'; 289 t = 'e';
301 false 290 false
302 }, 291 },
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index c20ffe0b3..16002d2ac 100644
--- a/crates/ra_assists/src/handlers/raw_string.rs
+++ b/crates/ra_assists/src/handlers/raw_string.rs
@@ -164,7 +164,7 @@ mod test {
164 "#, 164 "#,
165 r##" 165 r##"
166 fn f() { 166 fn f() {
167 let s = <|>r#"random 167 let s = r#"random
168string"#; 168string"#;
169 } 169 }
170 "##, 170 "##,
@@ -182,7 +182,7 @@ string"#;
182 "#, 182 "#,
183 r##" 183 r##"
184 fn f() { 184 fn f() {
185 format!(<|>r#"x = {}"#, 92) 185 format!(r#"x = {}"#, 92)
186 } 186 }
187 "##, 187 "##,
188 ) 188 )
@@ -199,7 +199,7 @@ string"#;
199 "###, 199 "###,
200 r####" 200 r####"
201 fn f() { 201 fn f() {
202 let s = <|>r#"#random## 202 let s = r#"#random##
203string"#; 203string"#;
204 } 204 }
205 "####, 205 "####,
@@ -217,7 +217,7 @@ string"#;
217 "###, 217 "###,
218 r####" 218 r####"
219 fn f() { 219 fn f() {
220 let s = <|>r###"#random"## 220 let s = r###"#random"##
221string"###; 221string"###;
222 } 222 }
223 "####, 223 "####,
@@ -235,7 +235,7 @@ string"###;
235 "#, 235 "#,
236 r##" 236 r##"
237 fn f() { 237 fn f() {
238 let s = <|>r#"random string"#; 238 let s = r#"random string"#;
239 } 239 }
240 "##, 240 "##,
241 ) 241 )
@@ -289,7 +289,7 @@ string"###;
289 "#, 289 "#,
290 r##" 290 r##"
291 fn f() { 291 fn f() {
292 let s = <|>r#"random string"#; 292 let s = r#"random string"#;
293 } 293 }
294 "##, 294 "##,
295 ) 295 )
@@ -306,7 +306,7 @@ string"###;
306 "##, 306 "##,
307 r###" 307 r###"
308 fn f() { 308 fn f() {
309 let s = <|>r##"random"string"##; 309 let s = r##"random"string"##;
310 } 310 }
311 "###, 311 "###,
312 ) 312 )
@@ -348,7 +348,7 @@ string"###;
348 "##, 348 "##,
349 r#" 349 r#"
350 fn f() { 350 fn f() {
351 let s = <|>r"random string"; 351 let s = r"random string";
352 } 352 }
353 "#, 353 "#,
354 ) 354 )
@@ -365,7 +365,7 @@ string"###;
365 "##, 365 "##,
366 r#" 366 r#"
367 fn f() { 367 fn f() {
368 let s = <|>r"random\"str\"ing"; 368 let s = r"random\"str\"ing";
369 } 369 }
370 "#, 370 "#,
371 ) 371 )
@@ -382,7 +382,7 @@ string"###;
382 "###, 382 "###,
383 r##" 383 r##"
384 fn f() { 384 fn f() {
385 let s = <|>r#"random string"#; 385 let s = r#"random string"#;
386 } 386 }
387 "##, 387 "##,
388 ) 388 )
@@ -436,7 +436,7 @@ string"###;
436 "##, 436 "##,
437 r#" 437 r#"
438 fn f() { 438 fn f() {
439 let s = <|>"random string"; 439 let s = "random string";
440 } 440 }
441 "#, 441 "#,
442 ) 442 )
@@ -453,7 +453,7 @@ string"###;
453 "##, 453 "##,
454 r#" 454 r#"
455 fn f() { 455 fn f() {
456 let s = <|>"random\"str\"ing"; 456 let s = "random\"str\"ing";
457 } 457 }
458 "#, 458 "#,
459 ) 459 )
@@ -470,7 +470,7 @@ string"###;
470 "###, 470 "###,
471 r##" 471 r##"
472 fn f() { 472 fn f() {
473 let s = <|>"random string"; 473 let s = "random string";
474 } 474 }
475 "##, 475 "##,
476 ) 476 )
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs
index 8eef578cf..961ee1731 100644
--- a/crates/ra_assists/src/handlers/remove_dbg.rs
+++ b/crates/ra_assists/src/handlers/remove_dbg.rs
@@ -29,26 +29,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 29
30 let macro_range = macro_call.syntax().text_range(); 30 let macro_range = macro_call.syntax().text_range();
31 31
32 // If the cursor is inside the macro call, we'll try to maintain the cursor
33 // position by subtracting the length of dbg!( from the start of the file
34 // range, otherwise we'll default to using the start of the macro call
35 let cursor_pos = {
36 let file_range = ctx.frange.range;
37
38 let offset_start = file_range
39 .start()
40 .checked_sub(macro_range.start())
41 .unwrap_or_else(|| TextSize::from(0));
42
43 let dbg_size = TextSize::of("dbg!(");
44
45 if offset_start > dbg_size {
46 file_range.start() - dbg_size
47 } else {
48 macro_range.start()
49 }
50 };
51
52 let macro_content = { 32 let macro_content = {
53 let macro_args = macro_call.token_tree()?.syntax().clone(); 33 let macro_args = macro_call.token_tree()?.syntax().clone();
54 34
@@ -58,9 +38,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
58 }; 38 };
59 39
60 let target = macro_call.syntax().text_range(); 40 let target = macro_call.syntax().text_range();
61 acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { 41 acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| {
62 edit.replace(macro_range, macro_content); 42 builder.replace(macro_range, macro_content);
63 edit.set_cursor(cursor_pos);
64 }) 43 })
65} 44}
66 45
@@ -94,13 +73,13 @@ mod tests {
94 73
95 #[test] 74 #[test]
96 fn test_remove_dbg() { 75 fn test_remove_dbg() {
97 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1"); 76 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");
98 77
99 check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)"); 78 check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");
100 79
101 check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1"); 80 check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");
102 81
103 check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1"); 82 check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");
104 83
105 check_assist( 84 check_assist(
106 remove_dbg, 85 remove_dbg,
@@ -113,7 +92,7 @@ fn foo(n: usize) {
113", 92",
114 " 93 "
115fn foo(n: usize) { 94fn foo(n: usize) {
116 if let Some(_) = n.<|>checked_sub(4) { 95 if let Some(_) = n.checked_sub(4) {
117 // ... 96 // ...
118 } 97 }
119} 98}
@@ -122,8 +101,8 @@ fn foo(n: usize) {
122 } 101 }
123 #[test] 102 #[test]
124 fn test_remove_dbg_with_brackets_and_braces() { 103 fn test_remove_dbg_with_brackets_and_braces() {
125 check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1"); 104 check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
126 check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1"); 105 check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
127 } 106 }
128 107
129 #[test] 108 #[test]
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs
index dce546db7..fe4eada03 100644
--- a/crates/ra_assists/src/handlers/remove_mut.rs
+++ b/crates/ra_assists/src/handlers/remove_mut.rs
@@ -26,8 +26,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 }; 26 };
27 27
28 let target = mut_token.text_range(); 28 let target = mut_token.text_range();
29 acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { 29 acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| {
30 edit.set_cursor(delete_from); 30 builder.delete(TextRange::new(delete_from, delete_to));
31 edit.delete(TextRange::new(delete_from, delete_to));
32 }) 31 })
33} 32}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 757f6406e..897da2832 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
23// ``` 23// ```
24// 24//
25pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 25pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 reorder::<ast::RecordLit>(acc, ctx.clone()).or_else(|| reorder::<ast::RecordPat>(acc, ctx)) 26 reorder::<ast::RecordLit>(acc, ctx).or_else(|| reorder::<ast::RecordPat>(acc, ctx))
27} 27}
28 28
29fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -140,7 +140,7 @@ mod tests {
140 "#, 140 "#,
141 r#" 141 r#"
142 struct Foo {foo: i32, bar: i32}; 142 struct Foo {foo: i32, bar: i32};
143 const test: Foo = <|>Foo {foo: 1, bar: 0} 143 const test: Foo = Foo {foo: 1, bar: 0}
144 "#, 144 "#,
145 ) 145 )
146 } 146 }
@@ -164,7 +164,7 @@ mod tests {
164 164
165 fn f(f: Foo) -> { 165 fn f(f: Foo) -> {
166 match f { 166 match f {
167 <|>Foo { ref mut bar, baz: 0, .. } => (), 167 Foo { ref mut bar, baz: 0, .. } => (),
168 _ => () 168 _ => ()
169 } 169 }
170 } 170 }
@@ -202,7 +202,7 @@ mod tests {
202 impl Foo { 202 impl Foo {
203 fn new() -> Foo { 203 fn new() -> Foo {
204 let foo = String::new(); 204 let foo = String::new();
205 <|>Foo { 205 Foo {
206 foo, 206 foo,
207 bar: foo.clone(), 207 bar: foo.clone(),
208 extra: "Extra field", 208 extra: "Extra field",
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index 65f5fc6ab..e016f51c3 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -68,7 +68,6 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
68 .indent(IndentLevel::from_node(if_expr.syntax())) 68 .indent(IndentLevel::from_node(if_expr.syntax()))
69 }; 69 };
70 70
71 edit.set_cursor(if_expr.syntax().text_range().start());
72 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 71 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
73 }) 72 })
74} 73}
@@ -83,7 +82,7 @@ mod tests {
83 fn test_replace_if_let_with_match_unwraps_simple_expressions() { 82 fn test_replace_if_let_with_match_unwraps_simple_expressions() {
84 check_assist( 83 check_assist(
85 replace_if_let_with_match, 84 replace_if_let_with_match,
86 " 85 r#"
87impl VariantData { 86impl VariantData {
88 pub fn is_struct(&self) -> bool { 87 pub fn is_struct(&self) -> bool {
89 if <|>let VariantData::Struct(..) = *self { 88 if <|>let VariantData::Struct(..) = *self {
@@ -92,16 +91,16 @@ impl VariantData {
92 false 91 false
93 } 92 }
94 } 93 }
95} ", 94} "#,
96 " 95 r#"
97impl VariantData { 96impl VariantData {
98 pub fn is_struct(&self) -> bool { 97 pub fn is_struct(&self) -> bool {
99 <|>match *self { 98 match *self {
100 VariantData::Struct(..) => true, 99 VariantData::Struct(..) => true,
101 _ => false, 100 _ => false,
102 } 101 }
103 } 102 }
104} ", 103} "#,
105 ) 104 )
106 } 105 }
107 106
@@ -109,7 +108,7 @@ impl VariantData {
109 fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() { 108 fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() {
110 check_assist( 109 check_assist(
111 replace_if_let_with_match, 110 replace_if_let_with_match,
112 " 111 r#"
113fn foo() { 112fn foo() {
114 if <|>let VariantData::Struct(..) = a { 113 if <|>let VariantData::Struct(..) = a {
115 bar( 114 bar(
@@ -118,10 +117,10 @@ fn foo() {
118 } else { 117 } else {
119 false 118 false
120 } 119 }
121} ", 120} "#,
122 " 121 r#"
123fn foo() { 122fn foo() {
124 <|>match a { 123 match a {
125 VariantData::Struct(..) => { 124 VariantData::Struct(..) => {
126 bar( 125 bar(
127 123 126 123
@@ -129,7 +128,7 @@ fn foo() {
129 } 128 }
130 _ => false, 129 _ => false,
131 } 130 }
132} ", 131} "#,
133 ) 132 )
134 } 133 }
135 134
@@ -137,7 +136,7 @@ fn foo() {
137 fn replace_if_let_with_match_target() { 136 fn replace_if_let_with_match_target() {
138 check_assist_target( 137 check_assist_target(
139 replace_if_let_with_match, 138 replace_if_let_with_match,
140 " 139 r#"
141impl VariantData { 140impl VariantData {
142 pub fn is_struct(&self) -> bool { 141 pub fn is_struct(&self) -> bool {
143 if <|>let VariantData::Struct(..) = *self { 142 if <|>let VariantData::Struct(..) = *self {
@@ -146,7 +145,7 @@ impl VariantData {
146 false 145 false
147 } 146 }
148 } 147 }
149} ", 148} "#,
150 "if let VariantData::Struct(..) = *self { 149 "if let VariantData::Struct(..) = *self {
151 true 150 true
152 } else { 151 } else {
@@ -176,7 +175,7 @@ enum Option<T> { Some(T), None }
176use Option::*; 175use Option::*;
177 176
178fn foo(x: Option<i32>) { 177fn foo(x: Option<i32>) {
179 <|>match x { 178 match x {
180 Some(x) => println!("{}", x), 179 Some(x) => println!("{}", x),
181 None => println!("none"), 180 None => println!("none"),
182 } 181 }
@@ -206,7 +205,7 @@ enum Result<T, E> { Ok(T), Err(E) }
206use Result::*; 205use Result::*;
207 206
208fn foo(x: Result<i32, ()>) { 207fn foo(x: Result<i32, ()>) {
209 <|>match x { 208 match x {
210 Ok(x) => println!("{}", x), 209 Ok(x) => println!("{}", x),
211 Err(_) => println!("none"), 210 Err(_) => println!("none"),
212 } 211 }
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index 482957dc6..761557ac0 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -58,12 +58,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
58 let stmt = make::expr_stmt(if_); 58 let stmt = make::expr_stmt(if_);
59 59
60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); 60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
61 let target_offset =
62 let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start();
63 let stmt = stmt.replace_descendant(placeholder.into(), original_pat); 61 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
64 62
65 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); 63 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
66 edit.set_cursor(target_offset);
67 }) 64 })
68} 65}
69 66
@@ -88,7 +85,7 @@ fn main() {
88enum E<T> { X(T), Y(T) } 85enum E<T> { X(T), Y(T) }
89 86
90fn main() { 87fn main() {
91 if let <|>x = E::X(92) { 88 if let x = E::X(92) {
92 } 89 }
93} 90}
94 ", 91 ",
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index d9f84208d..0197a8cf0 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -89,7 +89,7 @@ std::fmt::Debug<|>
89 " 89 "
90use std::fmt::Debug; 90use std::fmt::Debug;
91 91
92Debug<|> 92Debug
93 ", 93 ",
94 ); 94 );
95 } 95 }
@@ -106,7 +106,7 @@ fn main() {
106 " 106 "
107use std::fmt::Debug; 107use std::fmt::Debug;
108 108
109Debug<|> 109Debug
110 110
111fn main() { 111fn main() {
112} 112}
@@ -130,7 +130,7 @@ use std::fmt::Debug;
130fn main() { 130fn main() {
131} 131}
132 132
133Debug<|> 133Debug
134 ", 134 ",
135 ); 135 );
136 } 136 }
@@ -145,7 +145,7 @@ std::fmt<|>::Debug
145 " 145 "
146use std::fmt; 146use std::fmt;
147 147
148fmt<|>::Debug 148fmt::Debug
149 ", 149 ",
150 ); 150 );
151 } 151 }
@@ -164,7 +164,7 @@ impl std::fmt::Debug<|> for Foo {
164use stdx; 164use stdx;
165use std::fmt::Debug; 165use std::fmt::Debug;
166 166
167impl Debug<|> for Foo { 167impl Debug for Foo {
168} 168}
169 ", 169 ",
170 ); 170 );
@@ -181,7 +181,7 @@ impl std::fmt::Debug<|> for Foo {
181 " 181 "
182use std::fmt::Debug; 182use std::fmt::Debug;
183 183
184impl Debug<|> for Foo { 184impl Debug for Foo {
185} 185}
186 ", 186 ",
187 ); 187 );
@@ -198,7 +198,7 @@ impl Debug<|> for Foo {
198 " 198 "
199 use std::fmt::Debug; 199 use std::fmt::Debug;
200 200
201 impl Debug<|> for Foo { 201 impl Debug for Foo {
202 } 202 }
203 ", 203 ",
204 ); 204 );
@@ -217,7 +217,7 @@ impl std::io<|> for Foo {
217 " 217 "
218use std::{io, fmt}; 218use std::{io, fmt};
219 219
220impl io<|> for Foo { 220impl io for Foo {
221} 221}
222 ", 222 ",
223 ); 223 );
@@ -236,7 +236,7 @@ impl std::fmt::Debug<|> for Foo {
236 " 236 "
237use std::fmt::{self, Debug, }; 237use std::fmt::{self, Debug, };
238 238
239impl Debug<|> for Foo { 239impl Debug for Foo {
240} 240}
241 ", 241 ",
242 ); 242 );
@@ -255,7 +255,7 @@ impl std::fmt<|> for Foo {
255 " 255 "
256use std::fmt::{self, Debug}; 256use std::fmt::{self, Debug};
257 257
258impl fmt<|> for Foo { 258impl fmt for Foo {
259} 259}
260 ", 260 ",
261 ); 261 );
@@ -274,7 +274,7 @@ impl std::fmt::nested<|> for Foo {
274 " 274 "
275use std::fmt::{Debug, nested::{Display, self}}; 275use std::fmt::{Debug, nested::{Display, self}};
276 276
277impl nested<|> for Foo { 277impl nested for Foo {
278} 278}
279", 279",
280 ); 280 );
@@ -293,7 +293,7 @@ impl std::fmt::nested<|> for Foo {
293 " 293 "
294use std::fmt::{Debug, nested::{self, Display}}; 294use std::fmt::{Debug, nested::{self, Display}};
295 295
296impl nested<|> for Foo { 296impl nested for Foo {
297} 297}
298", 298",
299 ); 299 );
@@ -312,7 +312,7 @@ impl std::fmt::nested::Debug<|> for Foo {
312 " 312 "
313use std::fmt::{Debug, nested::{Display, Debug}}; 313use std::fmt::{Debug, nested::{Display, Debug}};
314 314
315impl Debug<|> for Foo { 315impl Debug for Foo {
316} 316}
317", 317",
318 ); 318 );
@@ -331,7 +331,7 @@ impl std::fmt::nested::Display<|> for Foo {
331 " 331 "
332use std::fmt::{nested::Display, Debug}; 332use std::fmt::{nested::Display, Debug};
333 333
334impl Display<|> for Foo { 334impl Display for Foo {
335} 335}
336", 336",
337 ); 337 );
@@ -350,7 +350,7 @@ impl std::fmt::Display<|> for Foo {
350 " 350 "
351use std::fmt::{Display, nested::Debug}; 351use std::fmt::{Display, nested::Debug};
352 352
353impl Display<|> for Foo { 353impl Display for Foo {
354} 354}
355", 355",
356 ); 356 );
@@ -374,7 +374,7 @@ use crate::{
374 AssocItem, 374 AssocItem,
375}; 375};
376 376
377fn foo() { lower<|>::trait_env() } 377fn foo() { lower::trait_env() }
378", 378",
379 ); 379 );
380 } 380 }
@@ -392,7 +392,7 @@ impl foo::Debug<|> for Foo {
392 " 392 "
393use std::fmt as foo; 393use std::fmt as foo;
394 394
395impl Debug<|> for Foo { 395impl Debug for Foo {
396} 396}
397", 397",
398 ); 398 );
@@ -435,7 +435,7 @@ mod foo {
435 mod bar { 435 mod bar {
436 use std::fmt::Debug; 436 use std::fmt::Debug;
437 437
438 Debug<|> 438 Debug
439 } 439 }
440} 440}
441 ", 441 ",
@@ -458,7 +458,7 @@ fn main() {
458use std::fmt::Debug; 458use std::fmt::Debug;
459 459
460fn main() { 460fn main() {
461 Debug<|> 461 Debug
462} 462}
463 ", 463 ",
464 ); 464 );
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index b379b55a8..cff7dfb81 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -9,7 +9,10 @@ use ra_syntax::{
9 AstNode, 9 AstNode,
10}; 10};
11 11
12use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; 12use crate::{
13 utils::{render_snippet, Cursor, TryEnum},
14 AssistContext, AssistId, Assists,
15};
13 16
14// Assist: replace_unwrap_with_match 17// Assist: replace_unwrap_with_match
15// 18//
@@ -29,7 +32,7 @@ use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
29// let x: Result<i32, i32> = Result::Ok(92); 32// let x: Result<i32, i32> = Result::Ok(92);
30// let y = match x { 33// let y = match x {
31// Ok(a) => a, 34// Ok(a) => a,
32// _ => unreachable!(), 35// $0_ => unreachable!(),
33// }; 36// };
34// } 37// }
35// ``` 38// ```
@@ -43,7 +46,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
43 let ty = ctx.sema.type_of_expr(&caller)?; 46 let ty = ctx.sema.type_of_expr(&caller)?;
44 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); 47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
45 let target = method_call.syntax().text_range(); 48 let target = method_call.syntax().text_range();
46 acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { 49 acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| {
47 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 50 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
48 let it = make::bind_pat(make::name("a")).into(); 51 let it = make::bind_pat(make::name("a")).into();
49 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); 52 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
@@ -58,16 +61,30 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
58 let match_expr = make::expr_match(caller.clone(), match_arm_list) 61 let match_expr = make::expr_match(caller.clone(), match_arm_list)
59 .indent(IndentLevel::from_node(method_call.syntax())); 62 .indent(IndentLevel::from_node(method_call.syntax()));
60 63
61 edit.set_cursor(caller.syntax().text_range().start()); 64 let range = method_call.syntax().text_range();
62 edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); 65 match ctx.config.snippet_cap {
66 Some(cap) => {
67 let err_arm = match_expr
68 .syntax()
69 .descendants()
70 .filter_map(ast::MatchArm::cast)
71 .last()
72 .unwrap();
73 let snippet =
74 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
75 builder.replace_snippet(cap, range, snippet)
76 }
77 None => builder.replace(range, match_expr.to_string()),
78 }
63 }) 79 })
64} 80}
65 81
66#[cfg(test)] 82#[cfg(test)]
67mod tests { 83mod tests {
68 use super::*;
69 use crate::tests::{check_assist, check_assist_target}; 84 use crate::tests::{check_assist, check_assist_target};
70 85
86 use super::*;
87
71 #[test] 88 #[test]
72 fn test_replace_result_unwrap_with_match() { 89 fn test_replace_result_unwrap_with_match() {
73 check_assist( 90 check_assist(
@@ -85,9 +102,9 @@ enum Result<T, E> { Ok(T), Err(E) }
85fn i<T>(a: T) -> T { a } 102fn i<T>(a: T) -> T { a }
86fn main() { 103fn main() {
87 let x: Result<i32, i32> = Result::Ok(92); 104 let x: Result<i32, i32> = Result::Ok(92);
88 let y = <|>match i(x) { 105 let y = match i(x) {
89 Ok(a) => a, 106 Ok(a) => a,
90 _ => unreachable!(), 107 $0_ => unreachable!(),
91 }; 108 };
92} 109}
93 ", 110 ",
@@ -111,9 +128,9 @@ enum Option<T> { Some(T), None }
111fn i<T>(a: T) -> T { a } 128fn i<T>(a: T) -> T { a }
112fn main() { 129fn main() {
113 let x = Option::Some(92); 130 let x = Option::Some(92);
114 let y = <|>match i(x) { 131 let y = match i(x) {
115 Some(a) => a, 132 Some(a) => a,
116 _ => unreachable!(), 133 $0_ => unreachable!(),
117 }; 134 };
118} 135}
119 ", 136 ",
@@ -137,9 +154,9 @@ enum Result<T, E> { Ok(T), Err(E) }
137fn i<T>(a: T) -> T { a } 154fn i<T>(a: T) -> T { a }
138fn main() { 155fn main() {
139 let x: Result<i32, i32> = Result::Ok(92); 156 let x: Result<i32, i32> = Result::Ok(92);
140 let y = <|>match i(x) { 157 let y = match i(x) {
141 Ok(a) => a, 158 Ok(a) => a,
142 _ => unreachable!(), 159 $0_ => unreachable!(),
143 }.count_zeroes(); 160 }.count_zeroes();
144} 161}
145 ", 162 ",
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index b2757e50c..c7a874480 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -26,12 +26,10 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
26 if new_tree == use_tree { 26 if new_tree == use_tree {
27 return None; 27 return None;
28 } 28 }
29 let cursor = ctx.offset();
30 29
31 let target = colon_colon.text_range(); 30 let target = colon_colon.text_range();
32 acc.add(AssistId("split_import"), "Split import", target, |edit| { 31 acc.add(AssistId("split_import"), "Split import", target, |edit| {
33 edit.replace_ast(use_tree, new_tree); 32 edit.replace_ast(use_tree, new_tree);
34 edit.set_cursor(cursor);
35 }) 33 })
36} 34}
37 35
@@ -46,7 +44,7 @@ mod tests {
46 check_assist( 44 check_assist(
47 split_import, 45 split_import,
48 "use crate::<|>db::RootDatabase;", 46 "use crate::<|>db::RootDatabase;",
49 "use crate::<|>{db::RootDatabase};", 47 "use crate::{db::RootDatabase};",
50 ) 48 )
51 } 49 }
52 50
@@ -55,7 +53,7 @@ mod tests {
55 check_assist( 53 check_assist(
56 split_import, 54 split_import,
57 "use crate:<|>:db::{RootDatabase, FileSymbol}", 55 "use crate:<|>:db::{RootDatabase, FileSymbol}",
58 "use crate:<|>:{db::{RootDatabase, FileSymbol}}", 56 "use crate::{db::{RootDatabase, FileSymbol}}",
59 ) 57 )
60 } 58 }
61 59
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index b76182d79..8440c7d0f 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -62,7 +62,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); 62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); 63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
64 64
65 edit.set_cursor(ancestor_then_branch.syntax().text_range().end());
66 edit.delete(range_to_del_rest); 65 edit.delete(range_to_del_rest);
67 edit.delete(range_to_del_else_if); 66 edit.delete(range_to_del_else_if);
68 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); 67 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
@@ -79,7 +78,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
79 return acc.add(assist_id, assist_label, target, |edit| { 78 return acc.add(assist_id, assist_label, target, |edit| {
80 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); 79 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
81 80
82 edit.set_cursor(then_branch.syntax().text_range().end());
83 edit.delete(range_to_del); 81 edit.delete(range_to_del);
84 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); 82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
85 }); 83 });
@@ -97,8 +95,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
97 95
98 let target = expr_to_unwrap.syntax().text_range(); 96 let target = expr_to_unwrap.syntax().text_range();
99 acc.add(assist_id, assist_label, target, |edit| { 97 acc.add(assist_id, assist_label, target, |edit| {
100 edit.set_cursor(expr.syntax().text_range().start());
101
102 edit.replace( 98 edit.replace(
103 expr.syntax().text_range(), 99 expr.syntax().text_range(),
104 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), 100 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']),
@@ -154,7 +150,7 @@ mod tests {
154 r#" 150 r#"
155 fn main() { 151 fn main() {
156 bar(); 152 bar();
157 <|>foo(); 153 foo();
158 154
159 //comment 155 //comment
160 bar(); 156 bar();
@@ -188,7 +184,7 @@ mod tests {
188 184
189 //comment 185 //comment
190 bar(); 186 bar();
191 }<|> 187 }
192 println!("bar"); 188 println!("bar");
193 } 189 }
194 "#, 190 "#,
@@ -222,7 +218,7 @@ mod tests {
222 218
223 //comment 219 //comment
224 //bar(); 220 //bar();
225 }<|> 221 }
226 println!("bar"); 222 println!("bar");
227 } 223 }
228 "#, 224 "#,
@@ -258,7 +254,7 @@ mod tests {
258 //bar(); 254 //bar();
259 } else if false { 255 } else if false {
260 println!("bar"); 256 println!("bar");
261 }<|> 257 }
262 println!("foo"); 258 println!("foo");
263 } 259 }
264 "#, 260 "#,
@@ -298,7 +294,7 @@ mod tests {
298 println!("bar"); 294 println!("bar");
299 } else if true { 295 } else if true {
300 println!("foo"); 296 println!("foo");
301 }<|> 297 }
302 println!("else"); 298 println!("else");
303 } 299 }
304 "#, 300 "#,
@@ -336,7 +332,7 @@ mod tests {
336 //bar(); 332 //bar();
337 } else if false { 333 } else if false {
338 println!("bar"); 334 println!("bar");
339 }<|> 335 }
340 println!("foo"); 336 println!("foo");
341 } 337 }
342 "#, 338 "#,
@@ -383,7 +379,7 @@ mod tests {
383 "#, 379 "#,
384 r#" 380 r#"
385 fn main() { 381 fn main() {
386 <|>if true { 382 if true {
387 foo(); 383 foo();
388 384
389 //comment 385 //comment
@@ -417,7 +413,7 @@ mod tests {
417 r#" 413 r#"
418 fn main() { 414 fn main() {
419 for i in 0..5 { 415 for i in 0..5 {
420 <|>foo(); 416 foo();
421 417
422 //comment 418 //comment
423 bar(); 419 bar();
@@ -447,7 +443,7 @@ mod tests {
447 "#, 443 "#,
448 r#" 444 r#"
449 fn main() { 445 fn main() {
450 <|>if true { 446 if true {
451 foo(); 447 foo();
452 448
453 //comment 449 //comment
@@ -480,7 +476,7 @@ mod tests {
480 "#, 476 "#,
481 r#" 477 r#"
482 fn main() { 478 fn main() {
483 <|>if true { 479 if true {
484 foo(); 480 foo();
485 481
486 //comment 482 //comment
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index 9ba3da786..62dd3547f 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -7,8 +7,7 @@ use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
7use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 7use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
8use ra_syntax::TextRange; 8use ra_syntax::TextRange;
9use test_utils::{ 9use test_utils::{
10 add_cursor, assert_eq_text, extract_offset, extract_range, extract_range_or_offset, 10 assert_eq_text, extract_offset, extract_range, extract_range_or_offset, RangeOrOffset,
11 RangeOrOffset,
12}; 11};
13 12
14use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; 13use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists};
@@ -103,21 +102,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
103 102
104 let mut actual = db.file_text(change.file_id).as_ref().to_owned(); 103 let mut actual = db.file_text(change.file_id).as_ref().to_owned();
105 change.edit.apply(&mut actual); 104 change.edit.apply(&mut actual);
106
107 if !source_change.is_snippet {
108 match source_change.cursor_position {
109 None => {
110 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
111 let off = change
112 .edit
113 .apply_to_offset(before_cursor_pos)
114 .expect("cursor position is affected by the edit");
115 actual = add_cursor(&actual, off)
116 }
117 }
118 Some(off) => actual = add_cursor(&actual, off.offset),
119 };
120 }
121 assert_eq_text!(after, &actual); 105 assert_eq_text!(after, &actual);
122 } 106 }
123 (Some(assist), ExpectedResult::Target(target)) => { 107 (Some(assist), ExpectedResult::Target(target)) => {
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index 3e6654c17..250e56a69 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -443,7 +443,7 @@ fn main() {
443"#####, 443"#####,
444 r#####" 444 r#####"
445fn main() { 445fn main() {
446 let var_name = (1 + 2); 446 let $0var_name = (1 + 2);
447 var_name * 4; 447 var_name * 4;
448} 448}
449"#####, 449"#####,
@@ -764,7 +764,7 @@ fn main() {
764 let x: Result<i32, i32> = Result::Ok(92); 764 let x: Result<i32, i32> = Result::Ok(92);
765 let y = match x { 765 let y = match x {
766 Ok(a) => a, 766 Ok(a) => a,
767 _ => unreachable!(), 767 $0_ => unreachable!(),
768 }; 768 };
769} 769}
770"#####, 770"#####,
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs
index 39d71851c..85b100c6a 100644
--- a/crates/ra_cfg/src/cfg_expr.rs
+++ b/crates/ra_cfg/src/cfg_expr.rs
@@ -88,13 +88,17 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
88mod tests { 88mod tests {
89 use super::*; 89 use super::*;
90 90
91 use mbe::ast_to_token_tree; 91 use mbe::{ast_to_token_tree, TokenMap};
92 use ra_syntax::ast::{self, AstNode}; 92 use ra_syntax::ast::{self, AstNode};
93 93
94 fn assert_parse_result(input: &str, expected: CfgExpr) { 94 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
95 let source_file = ast::SourceFile::parse(input).ok().unwrap(); 95 let source_file = ast::SourceFile::parse(input).ok().unwrap();
96 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); 96 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
97 let (tt, _) = ast_to_token_tree(&tt).unwrap(); 97 ast_to_token_tree(&tt).unwrap()
98 }
99
100 fn assert_parse_result(input: &str, expected: CfgExpr) {
101 let (tt, _) = get_token_tree_generated(input);
98 assert_eq!(parse_cfg(&tt), expected); 102 assert_eq!(parse_cfg(&tt), expected);
99 } 103 }
100 104
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index f8f767091..482a2f3e6 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -63,7 +63,7 @@ use std::sync::Arc;
63 63
64use ra_cfg::CfgOptions; 64use ra_cfg::CfgOptions;
65use rustc_hash::FxHashMap; 65use rustc_hash::FxHashMap;
66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER}; 66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER};
67 67
68use crate::{ 68use crate::{
69 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, 69 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf,
@@ -113,7 +113,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
113 let fixture = parse_single_fixture(ra_fixture); 113 let fixture = parse_single_fixture(ra_fixture);
114 114
115 let crate_graph = if let Some(entry) = fixture { 115 let crate_graph = if let Some(entry) = fixture {
116 let meta = match parse_meta(&entry.meta) { 116 let meta = match ParsedMeta::from(&entry.meta) {
117 ParsedMeta::File(it) => it, 117 ParsedMeta::File(it) => it,
118 _ => panic!("with_single_file only support file meta"), 118 _ => panic!("with_single_file only support file meta"),
119 }; 119 };
@@ -170,7 +170,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
170 let mut file_position = None; 170 let mut file_position = None;
171 171
172 for entry in fixture.iter() { 172 for entry in fixture.iter() {
173 let meta = match parse_meta(&entry.meta) { 173 let meta = match ParsedMeta::from(&entry.meta) {
174 ParsedMeta::Root { path } => { 174 ParsedMeta::Root { path } => {
175 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); 175 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local());
176 db.set_source_root(source_root_id, Arc::new(source_root)); 176 db.set_source_root(source_root_id, Arc::new(source_root));
@@ -258,53 +258,25 @@ struct FileMeta {
258 env: Env, 258 env: Env,
259} 259}
260 260
261//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo) 261impl From<&FixtureMeta> for ParsedMeta {
262fn parse_meta(meta: &str) -> ParsedMeta { 262 fn from(meta: &FixtureMeta) -> Self {
263 let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); 263 match meta {
264 264 FixtureMeta::Root { path } => {
265 if components[0] == "root" { 265 // `Self::Root` causes a false warning: 'variant is never constructed: `Root` '
266 let path: RelativePathBuf = components[1].into(); 266 // see https://github.com/rust-lang/rust/issues/69018
267 assert!(path.starts_with("/") && path.ends_with("/")); 267 ParsedMeta::Root { path: path.to_owned() }
268 return ParsedMeta::Root { path };
269 }
270
271 let path: RelativePathBuf = components[0].into();
272 assert!(path.starts_with("/"));
273
274 let mut krate = None;
275 let mut deps = Vec::new();
276 let mut edition = Edition::Edition2018;
277 let mut cfg = CfgOptions::default();
278 let mut env = Env::default();
279 for component in components[1..].iter() {
280 let (key, value) = split1(component, ':').unwrap();
281 match key {
282 "crate" => krate = Some(value.to_string()),
283 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
284 "edition" => edition = Edition::from_str(&value).unwrap(),
285 "cfg" => {
286 for key in value.split(',') {
287 match split1(key, '=') {
288 None => cfg.insert_atom(key.into()),
289 Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
290 }
291 }
292 }
293 "env" => {
294 for key in value.split(',') {
295 if let Some((k, v)) = split1(key, '=') {
296 env.set(k, v.into());
297 }
298 }
299 } 268 }
300 _ => panic!("bad component: {:?}", component), 269 FixtureMeta::File(f) => Self::File(FileMeta {
270 path: f.path.to_owned(),
271 krate: f.crate_name.to_owned(),
272 deps: f.deps.to_owned(),
273 cfg: f.cfg.to_owned(),
274 edition: f
275 .edition
276 .as_ref()
277 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()),
278 env: Env::from(f.env.iter()),
279 }),
301 } 280 }
302 } 281 }
303
304 ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env })
305}
306
307fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
308 let idx = haystack.find(delim)?;
309 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
310} 282}
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index ab14e2d5e..4d2d3b48a 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -311,6 +311,21 @@ impl fmt::Display for Edition {
311 } 311 }
312} 312}
313 313
314impl<'a, T> From<T> for Env
315where
316 T: Iterator<Item = (&'a String, &'a String)>,
317{
318 fn from(iter: T) -> Self {
319 let mut result = Self::default();
320
321 for (k, v) in iter {
322 result.entries.insert(k.to_owned(), v.to_owned());
323 }
324
325 result
326 }
327}
328
314impl Env { 329impl Env {
315 pub fn set(&mut self, env: &str, value: String) { 330 pub fn set(&mut self, env: &str, value: String) {
316 self.entries.insert(env.to_owned(), value); 331 self.entries.insert(env.to_owned(), value);
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index c5df4ac24..3364a822f 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -62,6 +62,7 @@ pub use crate::{
62 62
63pub use hir_def::{ 63pub use hir_def::{
64 adt::StructKind, 64 adt::StructKind,
65 attr::Attrs,
65 body::scope::ExprScopes, 66 body::scope::ExprScopes,
66 builtin_type::BuiltinType, 67 builtin_type::BuiltinType,
67 docs::Documentation, 68 docs::Documentation,
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 576cd0c65..8b6c0bede 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -81,7 +81,7 @@ impl Attrs {
81 } 81 }
82 } 82 }
83 83
84 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { 84 pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
85 let hygiene = Hygiene::new(db.upcast(), owner.file_id); 85 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
86 Attrs::new(owner.value, &hygiene) 86 Attrs::new(owner.value, &hygiene)
87 } 87 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index f5a7305dc..273036cee 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -29,7 +29,7 @@ use crate::{
29 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 29 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
30}; 30};
31 31
32/// A subset of Exander that only deals with cfg attributes. We only need it to 32/// A subset of Expander that only deals with cfg attributes. We only need it to
33/// avoid cyclic queries in crate def map during enum processing. 33/// avoid cyclic queries in crate def map during enum processing.
34pub(crate) struct CfgExpander { 34pub(crate) struct CfgExpander {
35 cfg_options: CfgOptions, 35 cfg_options: CfgOptions,
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 2f71511ba..945a0025e 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -17,6 +17,7 @@ use crate::{
17 item_scope::ItemInNs, 17 item_scope::ItemInNs,
18 lang_item::{LangItemTarget, LangItems}, 18 lang_item::{LangItemTarget, LangItems},
19 nameres::{raw::RawItems, CrateDefMap}, 19 nameres::{raw::RawItems, CrateDefMap},
20 path::ModPath,
20 visibility::Visibility, 21 visibility::Visibility,
21 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 22 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
22 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, 23 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
@@ -118,6 +119,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
118 item: ItemInNs, 119 item: ItemInNs,
119 krate: CrateId, 120 krate: CrateId,
120 ) -> Arc<[(ModuleId, Name, Visibility)]>; 121 ) -> Arc<[(ModuleId, Name, Visibility)]>;
122
123 #[salsa::invoke(find_path::find_path_inner_query)]
124 fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
121} 125}
122 126
123fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 68d3cde08..4db798473 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -20,7 +20,7 @@ use crate::{
20/// *from where* you're referring to the item, hence the `from` parameter. 20/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path"); 22 let _p = profile("find_path");
23 find_path_inner(db, item, from, MAX_PATH_LEN) 23 db.find_path_inner(item, from, MAX_PATH_LEN)
24} 24}
25 25
26const MAX_PATH_LEN: usize = 15; 26const MAX_PATH_LEN: usize = 15;
@@ -49,7 +49,7 @@ impl ModPath {
49 } 49 }
50} 50}
51 51
52fn find_path_inner( 52pub(crate) fn find_path_inner_query(
53 db: &dyn DefDatabase, 53 db: &dyn DefDatabase,
54 item: ItemInNs, 54 item: ItemInNs,
55 from: ModuleId, 55 from: ModuleId,
@@ -140,8 +140,7 @@ fn find_path_inner(
140 let mut best_path = None; 140 let mut best_path = None;
141 let mut best_path_len = max_len; 141 let mut best_path_len = max_len;
142 for (module_id, name) in importable_locations { 142 for (module_id, name) in importable_locations {
143 let mut path = match find_path_inner( 143 let mut path = match db.find_path_inner(
144 db,
145 ItemInNs::Types(ModuleDefId::ModuleId(module_id)), 144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
146 from, 145 from,
147 best_path_len - 1, 146 best_path_len - 1,
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index d96ac8c0a..3516784b8 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -73,8 +73,8 @@ pub struct LangItems {
73} 73}
74 74
75impl LangItems { 75impl LangItems {
76 pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> { 76 pub fn target(&self, item: &str) -> Option<LangItemTarget> {
77 self.items.get(item) 77 self.items.get(item).copied()
78 } 78 }
79 79
80 /// Salsa query. This will look for lang items in a specific crate. 80 /// Salsa query. This will look for lang items in a specific crate.
@@ -163,9 +163,13 @@ impl LangItems {
163 ) where 163 ) where
164 T: Into<AttrDefId> + Copy, 164 T: Into<AttrDefId> + Copy,
165 { 165 {
166 let attrs = db.attrs(item.into()); 166 if let Some(lang_item_name) = lang_attr(db, item) {
167 if let Some(lang_item_name) = attrs.by_key("lang").string_value() { 167 self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
168 self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
169 } 168 }
170 } 169 }
171} 170}
171
172pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
173 let attrs = db.attrs(item.into());
174 attrs.by_key("lang").string_value().cloned()
175}
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 5fc0ec5e3..b2de7fa34 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" }
27 27
28scoped-tls = "1" 28scoped-tls = "1"
29 29
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } 30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } 31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } 32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35insta = "0.16.0" 35insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index fdb49560b..0a8bb24ac 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -76,6 +76,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
76 #[salsa::interned] 76 #[salsa::interned]
77 fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; 77 fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId;
78 #[salsa::interned] 78 #[salsa::interned]
79 fn intern_callable_def(&self, callable_def: CallableDef) -> crate::CallableDefId;
80 #[salsa::interned]
79 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; 81 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
80 #[salsa::interned] 82 #[salsa::interned]
81 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; 83 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId;
@@ -89,11 +91,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
89 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; 91 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>;
90 92
91 #[salsa::invoke(chalk::struct_datum_query)] 93 #[salsa::invoke(chalk::struct_datum_query)]
92 fn struct_datum(&self, krate: CrateId, struct_id: chalk::StructId) -> Arc<chalk::StructDatum>; 94 fn struct_datum(&self, krate: CrateId, struct_id: chalk::AdtId) -> Arc<chalk::StructDatum>;
93 95
94 #[salsa::invoke(crate::traits::chalk::impl_datum_query)] 96 #[salsa::invoke(crate::traits::chalk::impl_datum_query)]
95 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; 97 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>;
96 98
99 #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)]
100 fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>;
101
97 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] 102 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)]
98 fn associated_ty_value( 103 fn associated_ty_value(
99 &self, 104 &self,
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index c87ee06ce..93cb45a64 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -155,10 +155,16 @@ pub enum TypeCtor {
155/// This exists just for Chalk, because Chalk just has a single `StructId` where 155/// This exists just for Chalk, because Chalk just has a single `StructId` where
156/// we have different kinds of ADTs, primitive types and special type 156/// we have different kinds of ADTs, primitive types and special type
157/// constructors like tuples and function pointers. 157/// constructors like tuples and function pointers.
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
159pub struct TypeCtorId(salsa::InternId); 159pub struct TypeCtorId(salsa::InternId);
160impl_intern_key!(TypeCtorId); 160impl_intern_key!(TypeCtorId);
161 161
162/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
163/// we have different IDs for struct and enum variant constructors.
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
165pub struct CallableDefId(salsa::InternId);
166impl_intern_key!(CallableDefId);
167
162impl TypeCtor { 168impl TypeCtor {
163 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { 169 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize {
164 match self { 170 match self {
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 34f4b9039..0419bc751 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2602,3 +2602,155 @@ fn test(x: &dyn Foo) {
2602 "### 2602 "###
2603 ); 2603 );
2604} 2604}
2605
2606#[test]
2607fn builtin_copy() {
2608 assert_snapshot!(
2609 infer_with_mismatches(r#"
2610#[lang = "copy"]
2611trait Copy {}
2612
2613struct IsCopy;
2614impl Copy for IsCopy {}
2615struct NotCopy;
2616
2617trait Test { fn test(&self) -> bool; }
2618impl<T: Copy> Test for T {}
2619
2620fn test() {
2621 IsCopy.test();
2622 NotCopy.test();
2623 (IsCopy, IsCopy).test();
2624 (IsCopy, NotCopy).test();
2625}
2626"#, true),
2627 @r###"
2628 111..115 'self': &Self
2629 167..268 '{ ...t(); }': ()
2630 173..179 'IsCopy': IsCopy
2631 173..186 'IsCopy.test()': bool
2632 192..199 'NotCopy': NotCopy
2633 192..206 'NotCopy.test()': {unknown}
2634 212..228 '(IsCop...sCopy)': (IsCopy, IsCopy)
2635 212..235 '(IsCop...test()': bool
2636 213..219 'IsCopy': IsCopy
2637 221..227 'IsCopy': IsCopy
2638 241..258 '(IsCop...tCopy)': (IsCopy, NotCopy)
2639 241..265 '(IsCop...test()': {unknown}
2640 242..248 'IsCopy': IsCopy
2641 250..257 'NotCopy': NotCopy
2642 "###
2643 );
2644}
2645
2646#[test]
2647fn builtin_fn_def_copy() {
2648 assert_snapshot!(
2649 infer_with_mismatches(r#"
2650#[lang = "copy"]
2651trait Copy {}
2652
2653fn foo() {}
2654fn bar<T: Copy>(T) -> T {}
2655struct Struct(usize);
2656enum Enum { Variant(usize) }
2657
2658trait Test { fn test(&self) -> bool; }
2659impl<T: Copy> Test for T {}
2660
2661fn test() {
2662 foo.test();
2663 bar.test();
2664 Struct.test();
2665 Enum::Variant.test();
2666}
2667"#, true),
2668 // wrong result, because the built-in Copy impl for fn defs doesn't exist in Chalk yet
2669 @r###"
2670 42..44 '{}': ()
2671 61..62 'T': {unknown}
2672 69..71 '{}': ()
2673 69..71: expected T, got ()
2674 146..150 'self': &Self
2675 202..282 '{ ...t(); }': ()
2676 208..211 'foo': fn foo()
2677 208..218 'foo.test()': {unknown}
2678 224..227 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
2679 224..234 'bar.test()': {unknown}
2680 240..246 'Struct': Struct(usize) -> Struct
2681 240..253 'Struct.test()': {unknown}
2682 259..272 'Enum::Variant': Variant(usize) -> Enum
2683 259..279 'Enum::...test()': {unknown}
2684 "###
2685 );
2686}
2687
2688#[test]
2689fn builtin_fn_ptr_copy() {
2690 assert_snapshot!(
2691 infer_with_mismatches(r#"
2692#[lang = "copy"]
2693trait Copy {}
2694
2695trait Test { fn test(&self) -> bool; }
2696impl<T: Copy> Test for T {}
2697
2698fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) {
2699 f1.test();
2700 f2.test();
2701 f3.test();
2702}
2703"#, true),
2704 @r###"
2705 55..59 'self': &Self
2706 109..111 'f1': fn()
2707 119..121 'f2': fn(usize) -> u8
2708 140..142 'f3': fn(u8, u8) -> &u8
2709 163..211 '{ ...t(); }': ()
2710 169..171 'f1': fn()
2711 169..178 'f1.test()': bool
2712 184..186 'f2': fn(usize) -> u8
2713 184..193 'f2.test()': bool
2714 199..201 'f3': fn(u8, u8) -> &u8
2715 199..208 'f3.test()': bool
2716 "###
2717 );
2718}
2719
2720#[test]
2721fn builtin_sized() {
2722 assert_snapshot!(
2723 infer_with_mismatches(r#"
2724#[lang = "sized"]
2725trait Sized {}
2726
2727trait Test { fn test(&self) -> bool; }
2728impl<T: Sized> Test for T {}
2729
2730fn test() {
2731 1u8.test();
2732 (*"foo").test(); // not Sized
2733 (1u8, 1u8).test();
2734 (1u8, *"foo").test(); // not Sized
2735}
2736"#, true),
2737 @r###"
2738 57..61 'self': &Self
2739 114..229 '{ ...ized }': ()
2740 120..123 '1u8': u8
2741 120..130 '1u8.test()': bool
2742 136..151 '(*"foo").test()': {unknown}
2743 137..143 '*"foo"': str
2744 138..143 '"foo"': &str
2745 170..180 '(1u8, 1u8)': (u8, u8)
2746 170..187 '(1u8, ...test()': bool
2747 171..174 '1u8': u8
2748 176..179 '1u8': u8
2749 193..206 '(1u8, *"foo")': (u8, str)
2750 193..213 '(1u8, ...test()': {unknown}
2751 194..197 '1u8': u8
2752 199..205 '*"foo"': str
2753 200..205 '"foo"': &str
2754 "###
2755 );
2756}
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
index ccab246bf..88a422d2c 100644
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -290,8 +290,7 @@ fn trait_object_unsize_impl_datum(
290 let self_trait_ref = TraitRef { trait_, substs: self_substs }; 290 let self_trait_ref = TraitRef { trait_, substs: self_substs };
291 let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)]; 291 let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)];
292 292
293 let impl_substs = 293 let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.into())).build();
294 Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.clone().into())).build();
295 294
296 let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; 295 let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs };
297 296
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 5870618a0..5b0f12a3c 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -1,301 +1,29 @@
1//! Conversion code from/to Chalk. 1//! Conversion code from/to Chalk.
2use std::{fmt, sync::Arc}; 2use std::sync::Arc;
3 3
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{ 6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
7 cast::Cast, fold::shift::Shift, interner::HasInterner, Goal, GoalData, Parameter,
8 PlaceholderIndex, TypeName, UniverseIndex,
9};
10 7
11use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; 8use hir_def::{
12use ra_db::{ 9 lang_item::{lang_attr, LangItemTarget},
13 salsa::{InternId, InternKey}, 10 AssocContainerId, AssocItemId, HasModule, Lookup, TypeAliasId,
14 CrateId,
15}; 11};
12use ra_db::{salsa::InternKey, CrateId};
16 13
17use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; 14use super::{builtin, AssocTyValue, ChalkContext, Impl};
18use crate::{ 15use crate::{
19 db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, 16 db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
20 ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 17 CallableDef, DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor,
21}; 18};
19use chalk_rust_ir::WellKnownTrait;
20use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders};
22 21
23pub(super) mod tls; 22pub use self::interner::*;
24
25#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
26pub struct Interner;
27
28impl chalk_ir::interner::Interner for Interner {
29 type InternedType = Box<chalk_ir::TyData<Self>>;
30 type InternedLifetime = chalk_ir::LifetimeData<Self>;
31 type InternedParameter = chalk_ir::ParameterData<Self>;
32 type InternedGoal = Arc<GoalData<Self>>;
33 type InternedGoals = Vec<Goal<Self>>;
34 type InternedSubstitution = Vec<Parameter<Self>>;
35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
36 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
38 type InternedParameterKinds = Vec<chalk_ir::ParameterKind<()>>;
39 type InternedCanonicalVarKinds = Vec<chalk_ir::ParameterKind<UniverseIndex>>;
40 type Identifier = TypeAliasId;
41 type DefId = InternId;
42
43 fn debug_struct_id(
44 type_kind_id: StructId,
45 fmt: &mut fmt::Formatter<'_>,
46 ) -> Option<fmt::Result> {
47 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
48 }
49
50 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
51 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
52 }
53
54 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
55 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
56 }
57
58 fn debug_alias(
59 alias: &chalk_ir::AliasTy<Interner>,
60 fmt: &mut fmt::Formatter<'_>,
61 ) -> Option<fmt::Result> {
62 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
63 }
64
65 fn debug_projection_ty(
66 proj: &chalk_ir::ProjectionTy<Interner>,
67 fmt: &mut fmt::Formatter<'_>,
68 ) -> Option<fmt::Result> {
69 tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
70 }
71
72 fn debug_opaque_ty(
73 opaque_ty: &chalk_ir::OpaqueTy<Interner>,
74 fmt: &mut fmt::Formatter<'_>,
75 ) -> Option<fmt::Result> {
76 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
77 }
78
79 fn debug_opaque_ty_id(
80 opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
81 fmt: &mut fmt::Formatter<'_>,
82 ) -> Option<fmt::Result> {
83 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
84 }
85
86 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
87 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
88 }
89
90 fn debug_lifetime(
91 lifetime: &chalk_ir::Lifetime<Interner>,
92 fmt: &mut fmt::Formatter<'_>,
93 ) -> Option<fmt::Result> {
94 tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
95 }
96
97 fn debug_parameter(
98 parameter: &Parameter<Interner>,
99 fmt: &mut fmt::Formatter<'_>,
100 ) -> Option<fmt::Result> {
101 tls::with_current_program(|prog| Some(prog?.debug_parameter(parameter, fmt)))
102 }
103
104 fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
105 tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
106 }
107
108 fn debug_goals(
109 goals: &chalk_ir::Goals<Interner>,
110 fmt: &mut fmt::Formatter<'_>,
111 ) -> Option<fmt::Result> {
112 tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
113 }
114
115 fn debug_program_clause_implication(
116 pci: &chalk_ir::ProgramClauseImplication<Interner>,
117 fmt: &mut fmt::Formatter<'_>,
118 ) -> Option<fmt::Result> {
119 tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
120 }
121
122 fn debug_application_ty(
123 application_ty: &chalk_ir::ApplicationTy<Interner>,
124 fmt: &mut fmt::Formatter<'_>,
125 ) -> Option<fmt::Result> {
126 tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt)))
127 }
128
129 fn debug_substitution(
130 substitution: &chalk_ir::Substitution<Interner>,
131 fmt: &mut fmt::Formatter<'_>,
132 ) -> Option<fmt::Result> {
133 tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
134 }
135
136 fn debug_separator_trait_ref(
137 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
138 fmt: &mut fmt::Formatter<'_>,
139 ) -> Option<fmt::Result> {
140 tls::with_current_program(|prog| {
141 Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
142 })
143 }
144
145 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
146 Box::new(ty)
147 }
148
149 fn ty_data<'a>(&self, ty: &'a Box<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
150 ty
151 }
152
153 fn intern_lifetime(
154 &self,
155 lifetime: chalk_ir::LifetimeData<Self>,
156 ) -> chalk_ir::LifetimeData<Self> {
157 lifetime
158 }
159
160 fn lifetime_data<'a>(
161 &self,
162 lifetime: &'a chalk_ir::LifetimeData<Self>,
163 ) -> &'a chalk_ir::LifetimeData<Self> {
164 lifetime
165 }
166
167 fn intern_parameter(
168 &self,
169 parameter: chalk_ir::ParameterData<Self>,
170 ) -> chalk_ir::ParameterData<Self> {
171 parameter
172 }
173
174 fn parameter_data<'a>(
175 &self,
176 parameter: &'a chalk_ir::ParameterData<Self>,
177 ) -> &'a chalk_ir::ParameterData<Self> {
178 parameter
179 }
180
181 fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> {
182 Arc::new(goal)
183 }
184
185 fn intern_goals<E>(
186 &self,
187 data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
188 ) -> Result<Self::InternedGoals, E> {
189 data.into_iter().collect()
190 }
191
192 fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> {
193 goal
194 }
195
196 fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] {
197 goals
198 }
199
200 fn intern_substitution<E>(
201 &self,
202 data: impl IntoIterator<Item = Result<Parameter<Self>, E>>,
203 ) -> Result<Vec<Parameter<Self>>, E> {
204 data.into_iter().collect()
205 }
206
207 fn substitution_data<'a>(
208 &self,
209 substitution: &'a Vec<Parameter<Self>>,
210 ) -> &'a [Parameter<Self>] {
211 substitution
212 }
213
214 fn intern_program_clause(
215 &self,
216 data: chalk_ir::ProgramClauseData<Self>,
217 ) -> chalk_ir::ProgramClauseData<Self> {
218 data
219 }
220
221 fn program_clause_data<'a>(
222 &self,
223 clause: &'a chalk_ir::ProgramClauseData<Self>,
224 ) -> &'a chalk_ir::ProgramClauseData<Self> {
225 clause
226 }
227
228 fn intern_program_clauses<E>(
229 &self,
230 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
231 ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> {
232 data.into_iter().collect()
233 }
234
235 fn program_clauses_data<'a>(
236 &self,
237 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>,
238 ) -> &'a [chalk_ir::ProgramClause<Self>] {
239 &clauses
240 }
241
242 fn intern_quantified_where_clauses<E>(
243 &self,
244 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
245 ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
246 data.into_iter().collect()
247 }
248
249 fn quantified_where_clauses_data<'a>(
250 &self,
251 clauses: &'a Self::InternedQuantifiedWhereClauses,
252 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
253 clauses
254 }
255
256 fn intern_parameter_kinds<E>(
257 &self,
258 data: impl IntoIterator<Item = Result<chalk_ir::ParameterKind<()>, E>>,
259 ) -> Result<Self::InternedParameterKinds, E> {
260 data.into_iter().collect()
261 }
262 23
263 fn parameter_kinds_data<'a>( 24pub(super) mod tls;
264 &self, 25mod interner;
265 parameter_kinds: &'a Self::InternedParameterKinds, 26mod mapping;
266 ) -> &'a [chalk_ir::ParameterKind<()>] {
267 &parameter_kinds
268 }
269
270 fn intern_canonical_var_kinds<E>(
271 &self,
272 data: impl IntoIterator<Item = Result<chalk_ir::ParameterKind<UniverseIndex>, E>>,
273 ) -> Result<Self::InternedCanonicalVarKinds, E> {
274 data.into_iter().collect()
275 }
276
277 fn canonical_var_kinds_data<'a>(
278 &self,
279 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
280 ) -> &'a [chalk_ir::ParameterKind<UniverseIndex>] {
281 &canonical_var_kinds
282 }
283}
284
285impl chalk_ir::interner::HasInterner for Interner {
286 type Interner = Self;
287}
288
289pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
290pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>;
291pub type TraitId = chalk_ir::TraitId<Interner>;
292pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>;
293pub type StructId = chalk_ir::StructId<Interner>;
294pub type StructDatum = chalk_rust_ir::StructDatum<Interner>;
295pub type ImplId = chalk_ir::ImplId<Interner>;
296pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>;
297pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId<Interner>;
298pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>;
299 27
300pub(super) trait ToChalk { 28pub(super) trait ToChalk {
301 type Chalk; 29 type Chalk;
@@ -310,508 +38,6 @@ where
310 T::from_chalk(db, chalk) 38 T::from_chalk(db, chalk)
311} 39}
312 40
313impl ToChalk for Ty {
314 type Chalk = chalk_ir::Ty<Interner>;
315 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
316 match self {
317 Ty::Apply(apply_ty) => {
318 let name = apply_ty.ctor.to_chalk(db);
319 let substitution = apply_ty.parameters.to_chalk(db);
320 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
321 }
322 Ty::Projection(proj_ty) => {
323 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
324 let substitution = proj_ty.parameters.to_chalk(db);
325 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
326 associated_ty_id,
327 substitution,
328 })
329 .cast(&Interner)
330 .intern(&Interner)
331 }
332 Ty::Placeholder(id) => {
333 let interned_id = db.intern_type_param_id(id);
334 PlaceholderIndex {
335 ui: UniverseIndex::ROOT,
336 idx: interned_id.as_intern_id().as_usize(),
337 }
338 .to_ty::<Interner>(&Interner)
339 }
340 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
341 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
342 Ty::Dyn(predicates) => {
343 let where_clauses = chalk_ir::QuantifiedWhereClauses::from(
344 &Interner,
345 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
346 );
347 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
348 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
349 }
350 Ty::Opaque(_) | Ty::Unknown => {
351 let substitution = chalk_ir::Substitution::empty(&Interner);
352 let name = TypeName::Error;
353 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
354 }
355 }
356 }
357 fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
358 match chalk.data(&Interner).clone() {
359 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
360 TypeName::Error => Ty::Unknown,
361 _ => {
362 let ctor = from_chalk(db, apply_ty.name);
363 let parameters = from_chalk(db, apply_ty.substitution);
364 Ty::Apply(ApplicationTy { ctor, parameters })
365 }
366 },
367 chalk_ir::TyData::Placeholder(idx) => {
368 assert_eq!(idx.ui, UniverseIndex::ROOT);
369 let interned_id = crate::db::GlobalTypeParamId::from_intern_id(
370 crate::salsa::InternId::from(idx.idx),
371 );
372 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
373 }
374 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
375 let associated_ty = from_chalk(db, proj.associated_ty_id);
376 let parameters = from_chalk(db, proj.substitution);
377 Ty::Projection(ProjectionTy { associated_ty, parameters })
378 }
379 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
380 chalk_ir::TyData::Function(_) => unimplemented!(),
381 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
382 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
383 chalk_ir::TyData::Dyn(where_clauses) => {
384 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
385 let predicates = where_clauses
386 .bounds
387 .skip_binders()
388 .iter(&Interner)
389 .map(|c| from_chalk(db, c.clone()))
390 .collect();
391 Ty::Dyn(predicates)
392 }
393 }
394 }
395}
396
397impl ToChalk for Substs {
398 type Chalk = chalk_ir::Substitution<Interner>;
399
400 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
401 chalk_ir::Substitution::from(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db)))
402 }
403
404 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
405 let tys = parameters
406 .iter(&Interner)
407 .map(|p| match p.ty(&Interner) {
408 Some(ty) => from_chalk(db, ty.clone()),
409 None => unimplemented!(),
410 })
411 .collect();
412 Substs(tys)
413 }
414}
415
416impl ToChalk for TraitRef {
417 type Chalk = chalk_ir::TraitRef<Interner>;
418
419 fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> {
420 let trait_id = self.trait_.to_chalk(db);
421 let substitution = self.substs.to_chalk(db);
422 chalk_ir::TraitRef { trait_id, substitution }
423 }
424
425 fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
426 let trait_ = from_chalk(db, trait_ref.trait_id);
427 let substs = from_chalk(db, trait_ref.substitution);
428 TraitRef { trait_, substs }
429 }
430}
431
432impl ToChalk for hir_def::TraitId {
433 type Chalk = TraitId;
434
435 fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId {
436 chalk_ir::TraitId(self.as_intern_id())
437 }
438
439 fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
440 InternKey::from_intern_id(trait_id.0)
441 }
442}
443
444impl ToChalk for TypeCtor {
445 type Chalk = TypeName<Interner>;
446
447 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> {
448 match self {
449 TypeCtor::AssociatedType(type_alias) => {
450 let type_id = type_alias.to_chalk(db);
451 TypeName::AssociatedType(type_id)
452 }
453 _ => {
454 // other TypeCtors get interned and turned into a chalk StructId
455 let struct_id = db.intern_type_ctor(self).into();
456 TypeName::Struct(struct_id)
457 }
458 }
459 }
460
461 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
462 match type_name {
463 TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
464 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
465 TypeName::OpaqueType(_) => unreachable!(),
466
467 TypeName::Scalar(_) => unreachable!(),
468 TypeName::Tuple(_) => unreachable!(),
469 TypeName::Raw(_) => unreachable!(),
470 TypeName::Slice => unreachable!(),
471 TypeName::Ref(_) => unreachable!(),
472 TypeName::Str => unreachable!(),
473
474 TypeName::Error => {
475 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
476 unreachable!()
477 }
478 }
479 }
480}
481
482impl ToChalk for Impl {
483 type Chalk = ImplId;
484
485 fn to_chalk(self, db: &dyn HirDatabase) -> ImplId {
486 db.intern_chalk_impl(self).into()
487 }
488
489 fn from_chalk(db: &dyn HirDatabase, impl_id: ImplId) -> Impl {
490 db.lookup_intern_chalk_impl(impl_id.into())
491 }
492}
493
494impl ToChalk for TypeAliasId {
495 type Chalk = AssocTypeId;
496
497 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId {
498 chalk_ir::AssocTypeId(self.as_intern_id())
499 }
500
501 fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId {
502 InternKey::from_intern_id(type_alias_id.0)
503 }
504}
505
506impl ToChalk for AssocTyValue {
507 type Chalk = AssociatedTyValueId;
508
509 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValueId {
510 db.intern_assoc_ty_value(self).into()
511 }
512
513 fn from_chalk(db: &dyn HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue {
514 db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into())
515 }
516}
517
518impl ToChalk for GenericPredicate {
519 type Chalk = chalk_ir::QuantifiedWhereClause<Interner>;
520
521 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> {
522 match self {
523 GenericPredicate::Implemented(trait_ref) => {
524 let chalk_trait_ref = trait_ref.to_chalk(db);
525 let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
526 make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
527 }
528 GenericPredicate::Projection(projection_pred) => {
529 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
530 let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
531 let alias = chalk_ir::AliasTy::Projection(projection);
532 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
533 }
534 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
535 }
536 }
537
538 fn from_chalk(
539 db: &dyn HirDatabase,
540 where_clause: chalk_ir::QuantifiedWhereClause<Interner>,
541 ) -> GenericPredicate {
542 // we don't produce any where clauses with binders and can't currently deal with them
543 match where_clause
544 .skip_binders()
545 .shifted_out(&Interner)
546 .expect("unexpected bound vars in where clause")
547 {
548 chalk_ir::WhereClause::Implemented(tr) => {
549 GenericPredicate::Implemented(from_chalk(db, tr))
550 }
551 chalk_ir::WhereClause::AliasEq(projection_eq) => {
552 let projection_ty = from_chalk(
553 db,
554 match projection_eq.alias {
555 chalk_ir::AliasTy::Projection(p) => p,
556 _ => unimplemented!(),
557 },
558 );
559 let ty = from_chalk(db, projection_eq.ty);
560 GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty })
561 }
562 }
563 }
564}
565
566impl ToChalk for ProjectionTy {
567 type Chalk = chalk_ir::ProjectionTy<Interner>;
568
569 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
570 chalk_ir::ProjectionTy {
571 associated_ty_id: self.associated_ty.to_chalk(db),
572 substitution: self.parameters.to_chalk(db),
573 }
574 }
575
576 fn from_chalk(
577 db: &dyn HirDatabase,
578 projection_ty: chalk_ir::ProjectionTy<Interner>,
579 ) -> ProjectionTy {
580 ProjectionTy {
581 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
582 parameters: from_chalk(db, projection_ty.substitution),
583 }
584 }
585}
586
587impl ToChalk for super::ProjectionPredicate {
588 type Chalk = chalk_ir::AliasEq<Interner>;
589
590 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
591 chalk_ir::AliasEq {
592 alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
593 ty: self.ty.to_chalk(db),
594 }
595 }
596
597 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
598 unimplemented!()
599 }
600}
601
602impl ToChalk for Obligation {
603 type Chalk = chalk_ir::DomainGoal<Interner>;
604
605 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
606 match self {
607 Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
608 Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner),
609 }
610 }
611
612 fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
613 unimplemented!()
614 }
615}
616
617impl<T> ToChalk for Canonical<T>
618where
619 T: ToChalk,
620 T::Chalk: HasInterner<Interner = Interner>,
621{
622 type Chalk = chalk_ir::Canonical<T::Chalk>;
623
624 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
625 let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
626 let value = self.value.to_chalk(db);
627 chalk_ir::Canonical {
628 value,
629 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
630 }
631 }
632
633 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
634 Canonical {
635 num_vars: canonical.binders.len(&Interner),
636 value: from_chalk(db, canonical.value),
637 }
638 }
639}
640
641impl ToChalk for Arc<super::TraitEnvironment> {
642 type Chalk = chalk_ir::Environment<Interner>;
643
644 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> {
645 let mut clauses = Vec::new();
646 for pred in &self.predicates {
647 if pred.is_error() {
648 // for env, we just ignore errors
649 continue;
650 }
651 let program_clause: chalk_ir::ProgramClause<Interner> =
652 pred.clone().to_chalk(db).cast(&Interner);
653 clauses.push(program_clause.into_from_env_clause(&Interner));
654 }
655 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
656 }
657
658 fn from_chalk(
659 _db: &dyn HirDatabase,
660 _env: chalk_ir::Environment<Interner>,
661 ) -> Arc<super::TraitEnvironment> {
662 unimplemented!()
663 }
664}
665
666impl<T: ToChalk> ToChalk for super::InEnvironment<T>
667where
668 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
669{
670 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
671
672 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
673 chalk_ir::InEnvironment {
674 environment: self.environment.to_chalk(db),
675 goal: self.value.to_chalk(db),
676 }
677 }
678
679 fn from_chalk(
680 db: &dyn HirDatabase,
681 in_env: chalk_ir::InEnvironment<T::Chalk>,
682 ) -> super::InEnvironment<T> {
683 super::InEnvironment {
684 environment: from_chalk(db, in_env.environment),
685 value: from_chalk(db, in_env.goal),
686 }
687 }
688}
689
690impl ToChalk for builtin::BuiltinImplData {
691 type Chalk = ImplDatum;
692
693 fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
694 let impl_type = chalk_rust_ir::ImplType::External;
695 let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
696
697 let impl_datum_bound =
698 chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
699 let associated_ty_value_ids =
700 self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
701 chalk_rust_ir::ImplDatum {
702 binders: make_binders(impl_datum_bound, self.num_vars),
703 impl_type,
704 polarity: chalk_rust_ir::Polarity::Positive,
705 associated_ty_value_ids,
706 }
707 }
708
709 fn from_chalk(_db: &dyn HirDatabase, _data: ImplDatum) -> Self {
710 unimplemented!()
711 }
712}
713
714impl ToChalk for builtin::BuiltinImplAssocTyValueData {
715 type Chalk = AssociatedTyValue;
716
717 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
718 let ty = self.value.to_chalk(db);
719 let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty };
720
721 chalk_rust_ir::AssociatedTyValue {
722 associated_ty_id: self.assoc_ty_id.to_chalk(db),
723 impl_id: self.impl_.to_chalk(db),
724 value: make_binders(value_bound, self.num_vars),
725 }
726 }
727
728 fn from_chalk(
729 _db: &dyn HirDatabase,
730 _data: AssociatedTyValue,
731 ) -> builtin::BuiltinImplAssocTyValueData {
732 unimplemented!()
733 }
734}
735
736fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
737where
738 T: HasInterner<Interner = Interner>,
739{
740 chalk_ir::Binders::new(
741 chalk_ir::ParameterKinds::from(
742 &Interner,
743 std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars),
744 ),
745 value,
746 )
747}
748
749fn convert_where_clauses(
750 db: &dyn HirDatabase,
751 def: GenericDefId,
752 substs: &Substs,
753) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
754 let generic_predicates = db.generic_predicates(def);
755 let mut result = Vec::with_capacity(generic_predicates.len());
756 for pred in generic_predicates.iter() {
757 if pred.value.is_error() {
758 // skip errored predicates completely
759 continue;
760 }
761 result.push(pred.clone().subst(substs).to_chalk(db));
762 }
763 result
764}
765
766fn generic_predicate_to_inline_bound(
767 db: &dyn HirDatabase,
768 pred: &GenericPredicate,
769 self_ty: &Ty,
770) -> Option<chalk_rust_ir::InlineBound<Interner>> {
771 // An InlineBound is like a GenericPredicate, except the self type is left out.
772 // We don't have a special type for this, but Chalk does.
773 match pred {
774 GenericPredicate::Implemented(trait_ref) => {
775 if &trait_ref.substs[0] != self_ty {
776 // we can only convert predicates back to type bounds if they
777 // have the expected self type
778 return None;
779 }
780 let args_no_self = trait_ref.substs[1..]
781 .iter()
782 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
783 .collect();
784 let trait_bound =
785 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
786 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
787 }
788 GenericPredicate::Projection(proj) => {
789 if &proj.projection_ty.parameters[0] != self_ty {
790 return None;
791 }
792 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
793 AssocContainerId::TraitId(t) => t,
794 _ => panic!("associated type not in trait"),
795 };
796 let args_no_self = proj.projection_ty.parameters[1..]
797 .iter()
798 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
799 .collect();
800 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
801 value: proj.ty.clone().to_chalk(db),
802 trait_bound: chalk_rust_ir::TraitBound {
803 trait_id: trait_.to_chalk(db),
804 args_no_self,
805 },
806 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
807 parameters: Vec::new(), // FIXME we don't support generic associated types yet
808 };
809 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
810 }
811 GenericPredicate::Error => None,
812 }
813}
814
815impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { 41impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
816 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { 42 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
817 self.db.associated_ty_data(id) 43 self.db.associated_ty_data(id)
@@ -819,16 +45,24 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
819 fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> { 45 fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> {
820 self.db.trait_datum(self.krate, trait_id) 46 self.db.trait_datum(self.krate, trait_id)
821 } 47 }
822 fn struct_datum(&self, struct_id: StructId) -> Arc<StructDatum> { 48 fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> {
823 self.db.struct_datum(self.krate, struct_id) 49 self.db.struct_datum(self.krate, struct_id)
824 } 50 }
825 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { 51 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
826 self.db.impl_datum(self.krate, impl_id) 52 self.db.impl_datum(self.krate, impl_id)
827 } 53 }
54
55 fn fn_def_datum(
56 &self,
57 fn_def_id: chalk_ir::FnDefId<Interner>,
58 ) -> Arc<chalk_rust_ir::FnDefDatum<Interner>> {
59 self.db.fn_def_datum(self.krate, fn_def_id)
60 }
61
828 fn impls_for_trait( 62 fn impls_for_trait(
829 &self, 63 &self,
830 trait_id: TraitId, 64 trait_id: TraitId,
831 parameters: &[Parameter<Interner>], 65 parameters: &[GenericArg<Interner>],
832 ) -> Vec<ImplId> { 66 ) -> Vec<ImplId> {
833 debug!("impls_for_trait {:?}", trait_id); 67 debug!("impls_for_trait {:?}", trait_id);
834 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 68 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
@@ -859,7 +93,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
859 debug!("impls_for_trait returned {} impls", result.len()); 93 debug!("impls_for_trait returned {} impls", result.len());
860 result 94 result
861 } 95 }
862 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: StructId) -> bool { 96 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: AdtId) -> bool {
863 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); 97 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id);
864 false // FIXME 98 false // FIXME
865 } 99 }
@@ -878,10 +112,15 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
878 } 112 }
879 fn well_known_trait_id( 113 fn well_known_trait_id(
880 &self, 114 &self,
881 _well_known_trait: chalk_rust_ir::WellKnownTrait, 115 well_known_trait: chalk_rust_ir::WellKnownTrait,
882 ) -> Option<chalk_ir::TraitId<Interner>> { 116 ) -> Option<chalk_ir::TraitId<Interner>> {
883 // FIXME tell Chalk about well-known traits (here and in trait_datum) 117 let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
884 None 118 let lang_items = self.db.crate_lang_items(self.krate);
119 let trait_ = match lang_items.target(lang_attr) {
120 Some(LangItemTarget::TraitId(trait_)) => trait_,
121 _ => return None,
122 };
123 Some(trait_.to_chalk(self.db))
885 } 124 }
886 125
887 fn program_clauses_for_env( 126 fn program_clauses_for_env(
@@ -983,7 +222,8 @@ pub(crate) fn trait_datum_query(
983 let associated_ty_ids = 222 let associated_ty_ids =
984 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); 223 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect();
985 let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses }; 224 let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses };
986 let well_known = None; // FIXME set this (depending on lang items) 225 let well_known =
226 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
987 let trait_datum = TraitDatum { 227 let trait_datum = TraitDatum {
988 id: trait_id, 228 id: trait_id,
989 binders: make_binders(trait_datum_bound, bound_vars.len()), 229 binders: make_binders(trait_datum_bound, bound_vars.len()),
@@ -994,13 +234,32 @@ pub(crate) fn trait_datum_query(
994 Arc::new(trait_datum) 234 Arc::new(trait_datum)
995} 235}
996 236
237fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
238 Some(match name {
239 "sized" => WellKnownTrait::SizedTrait,
240 "copy" => WellKnownTrait::CopyTrait,
241 "clone" => WellKnownTrait::CloneTrait,
242 "drop" => WellKnownTrait::DropTrait,
243 _ => return None,
244 })
245}
246
247fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
248 match attr {
249 WellKnownTrait::SizedTrait => "sized",
250 WellKnownTrait::CopyTrait => "copy",
251 WellKnownTrait::CloneTrait => "clone",
252 WellKnownTrait::DropTrait => "drop",
253 }
254}
255
997pub(crate) fn struct_datum_query( 256pub(crate) fn struct_datum_query(
998 db: &dyn HirDatabase, 257 db: &dyn HirDatabase,
999 krate: CrateId, 258 krate: CrateId,
1000 struct_id: StructId, 259 struct_id: AdtId,
1001) -> Arc<StructDatum> { 260) -> Arc<StructDatum> {
1002 debug!("struct_datum {:?}", struct_id); 261 debug!("struct_datum {:?}", struct_id);
1003 let type_ctor: TypeCtor = from_chalk(db, TypeName::Struct(struct_id)); 262 let type_ctor: TypeCtor = from_chalk(db, TypeName::Adt(struct_id));
1004 debug!("struct {:?} = {:?}", struct_id, type_ctor); 263 debug!("struct {:?} = {:?}", struct_id, type_ctor);
1005 let num_params = type_ctor.num_ty_params(db); 264 let num_params = type_ctor.num_ty_params(db);
1006 let upstream = type_ctor.krate(db) != Some(krate); 265 let upstream = type_ctor.krate(db) != Some(krate);
@@ -1012,12 +271,12 @@ pub(crate) fn struct_datum_query(
1012 convert_where_clauses(db, generic_def, &bound_vars) 271 convert_where_clauses(db, generic_def, &bound_vars)
1013 }) 272 })
1014 .unwrap_or_else(Vec::new); 273 .unwrap_or_else(Vec::new);
1015 let flags = chalk_rust_ir::StructFlags { 274 let flags = chalk_rust_ir::AdtFlags {
1016 upstream, 275 upstream,
1017 // FIXME set fundamental flag correctly 276 // FIXME set fundamental flag correctly
1018 fundamental: false, 277 fundamental: false,
1019 }; 278 };
1020 let struct_datum_bound = chalk_rust_ir::StructDatumBound { 279 let struct_datum_bound = chalk_rust_ir::AdtDatumBound {
1021 fields: Vec::new(), // FIXME add fields (only relevant for auto traits) 280 fields: Vec::new(), // FIXME add fields (only relevant for auto traits)
1022 where_clauses, 281 where_clauses,
1023 }; 282 };
@@ -1145,15 +404,47 @@ fn type_alias_associated_ty_value(
1145 Arc::new(value) 404 Arc::new(value)
1146} 405}
1147 406
1148impl From<StructId> for crate::TypeCtorId { 407pub(crate) fn fn_def_datum_query(
1149 fn from(struct_id: StructId) -> Self { 408 db: &dyn HirDatabase,
1150 InternKey::from_intern_id(struct_id.0) 409 _krate: CrateId,
410 fn_def_id: FnDefId,
411) -> Arc<FnDefDatum> {
412 let callable_def: CallableDef = from_chalk(db, fn_def_id);
413 let generic_params = generics(db.upcast(), callable_def.into());
414 let sig = db.callable_item_signature(callable_def);
415 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
416 let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
417 let bound = chalk_rust_ir::FnDefDatumBound {
418 // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
419 argument_types: sig.value.params().iter().map(|ty| ty.clone().to_chalk(db)).collect(),
420 return_type: sig.value.ret().clone().to_chalk(db),
421 where_clauses,
422 };
423 let datum = FnDefDatum { id: fn_def_id, binders: make_binders(bound, sig.num_binders) };
424 Arc::new(datum)
425}
426
427impl From<AdtId> for crate::TypeCtorId {
428 fn from(struct_id: AdtId) -> Self {
429 struct_id.0
1151 } 430 }
1152} 431}
1153 432
1154impl From<crate::TypeCtorId> for StructId { 433impl From<crate::TypeCtorId> for AdtId {
1155 fn from(type_ctor_id: crate::TypeCtorId) -> Self { 434 fn from(type_ctor_id: crate::TypeCtorId) -> Self {
1156 chalk_ir::StructId(type_ctor_id.as_intern_id()) 435 chalk_ir::AdtId(type_ctor_id)
436 }
437}
438
439impl From<FnDefId> for crate::CallableDefId {
440 fn from(fn_def_id: FnDefId) -> Self {
441 InternKey::from_intern_id(fn_def_id.0)
442 }
443}
444
445impl From<crate::CallableDefId> for FnDefId {
446 fn from(callable_def_id: crate::CallableDefId) -> Self {
447 chalk_ir::FnDefId(callable_def_id.as_intern_id())
1157 } 448 }
1158} 449}
1159 450
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
new file mode 100644
index 000000000..2a27f8ed8
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -0,0 +1,353 @@
1//! Implementation of the Chalk `Interner` trait, which allows customizing the
2//! representation of the various objects Chalk deals with (types, goals etc.).
3
4use super::tls;
5use chalk_ir::{GenericArg, Goal, GoalData};
6use hir_def::TypeAliasId;
7use ra_db::salsa::InternId;
8use std::{fmt, sync::Arc};
9
10#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
11pub struct Interner;
12
13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
14pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>;
15pub type TraitId = chalk_ir::TraitId<Interner>;
16pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>;
17pub type AdtId = chalk_ir::AdtId<Interner>;
18pub type StructDatum = chalk_rust_ir::AdtDatum<Interner>;
19pub type ImplId = chalk_ir::ImplId<Interner>;
20pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>;
21pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId<Interner>;
22pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>;
23pub type FnDefId = chalk_ir::FnDefId<Interner>;
24pub type FnDefDatum = chalk_rust_ir::FnDefDatum<Interner>;
25
26impl chalk_ir::interner::Interner for Interner {
27 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
28 type InternedLifetime = chalk_ir::LifetimeData<Self>;
29 type InternedConst = Arc<chalk_ir::ConstData<Self>>;
30 type InternedConcreteConst = ();
31 type InternedGenericArg = chalk_ir::GenericArgData<Self>;
32 type InternedGoal = Arc<GoalData<Self>>;
33 type InternedGoals = Vec<Goal<Self>>;
34 type InternedSubstitution = Vec<GenericArg<Self>>;
35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
36 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
38 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
39 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
40 type DefId = InternId;
41 type InternedAdtId = crate::TypeCtorId;
42 type Identifier = TypeAliasId;
43
44 fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
45 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
46 }
47
48 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
49 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
50 }
51
52 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
53 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
54 }
55
56 fn debug_alias(
57 alias: &chalk_ir::AliasTy<Interner>,
58 fmt: &mut fmt::Formatter<'_>,
59 ) -> Option<fmt::Result> {
60 tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
61 }
62
63 fn debug_projection_ty(
64 proj: &chalk_ir::ProjectionTy<Interner>,
65 fmt: &mut fmt::Formatter<'_>,
66 ) -> Option<fmt::Result> {
67 tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
68 }
69
70 fn debug_opaque_ty(
71 opaque_ty: &chalk_ir::OpaqueTy<Interner>,
72 fmt: &mut fmt::Formatter<'_>,
73 ) -> Option<fmt::Result> {
74 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
75 }
76
77 fn debug_opaque_ty_id(
78 opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
79 fmt: &mut fmt::Formatter<'_>,
80 ) -> Option<fmt::Result> {
81 tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
82 }
83
84 fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
85 tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
86 }
87
88 fn debug_lifetime(
89 lifetime: &chalk_ir::Lifetime<Interner>,
90 fmt: &mut fmt::Formatter<'_>,
91 ) -> Option<fmt::Result> {
92 tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
93 }
94
95 fn debug_generic_arg(
96 parameter: &GenericArg<Interner>,
97 fmt: &mut fmt::Formatter<'_>,
98 ) -> Option<fmt::Result> {
99 tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt)))
100 }
101
102 fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
103 tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
104 }
105
106 fn debug_goals(
107 goals: &chalk_ir::Goals<Interner>,
108 fmt: &mut fmt::Formatter<'_>,
109 ) -> Option<fmt::Result> {
110 tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
111 }
112
113 fn debug_program_clause_implication(
114 pci: &chalk_ir::ProgramClauseImplication<Interner>,
115 fmt: &mut fmt::Formatter<'_>,
116 ) -> Option<fmt::Result> {
117 tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
118 }
119
120 fn debug_application_ty(
121 application_ty: &chalk_ir::ApplicationTy<Interner>,
122 fmt: &mut fmt::Formatter<'_>,
123 ) -> Option<fmt::Result> {
124 tls::with_current_program(|prog| Some(prog?.debug_application_ty(application_ty, fmt)))
125 }
126
127 fn debug_substitution(
128 substitution: &chalk_ir::Substitution<Interner>,
129 fmt: &mut fmt::Formatter<'_>,
130 ) -> Option<fmt::Result> {
131 tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
132 }
133
134 fn debug_separator_trait_ref(
135 separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
136 fmt: &mut fmt::Formatter<'_>,
137 ) -> Option<fmt::Result> {
138 tls::with_current_program(|prog| {
139 Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
140 })
141 }
142
143 fn debug_fn_def_id(
144 fn_def_id: chalk_ir::FnDefId<Self>,
145 fmt: &mut fmt::Formatter<'_>,
146 ) -> Option<fmt::Result> {
147 tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
148 }
149 fn debug_const(
150 constant: &chalk_ir::Const<Self>,
151 fmt: &mut fmt::Formatter<'_>,
152 ) -> Option<fmt::Result> {
153 tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt)))
154 }
155 fn debug_variable_kinds(
156 variable_kinds: &chalk_ir::VariableKinds<Self>,
157 fmt: &mut fmt::Formatter<'_>,
158 ) -> Option<fmt::Result> {
159 tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt)))
160 }
161 fn debug_variable_kinds_with_angles(
162 variable_kinds: &chalk_ir::VariableKinds<Self>,
163 fmt: &mut fmt::Formatter<'_>,
164 ) -> Option<fmt::Result> {
165 tls::with_current_program(|prog| {
166 Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt))
167 })
168 }
169 fn debug_canonical_var_kinds(
170 canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
171 fmt: &mut fmt::Formatter<'_>,
172 ) -> Option<fmt::Result> {
173 tls::with_current_program(|prog| {
174 Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt))
175 })
176 }
177 fn debug_program_clause(
178 clause: &chalk_ir::ProgramClause<Self>,
179 fmt: &mut fmt::Formatter<'_>,
180 ) -> Option<fmt::Result> {
181 tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt)))
182 }
183 fn debug_program_clauses(
184 clauses: &chalk_ir::ProgramClauses<Self>,
185 fmt: &mut fmt::Formatter<'_>,
186 ) -> Option<fmt::Result> {
187 tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt)))
188 }
189 fn debug_quantified_where_clauses(
190 clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
191 fmt: &mut fmt::Formatter<'_>,
192 ) -> Option<fmt::Result> {
193 tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
194 }
195
196 fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
197 Box::new(ty)
198 }
199
200 fn ty_data<'a>(&self, ty: &'a Box<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
201 ty
202 }
203
204 fn intern_lifetime(
205 &self,
206 lifetime: chalk_ir::LifetimeData<Self>,
207 ) -> chalk_ir::LifetimeData<Self> {
208 lifetime
209 }
210
211 fn lifetime_data<'a>(
212 &self,
213 lifetime: &'a chalk_ir::LifetimeData<Self>,
214 ) -> &'a chalk_ir::LifetimeData<Self> {
215 lifetime
216 }
217
218 fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Arc<chalk_ir::ConstData<Self>> {
219 Arc::new(constant)
220 }
221
222 fn const_data<'a>(
223 &self,
224 constant: &'a Arc<chalk_ir::ConstData<Self>>,
225 ) -> &'a chalk_ir::ConstData<Self> {
226 constant
227 }
228
229 fn const_eq(&self, _ty: &Box<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool {
230 true
231 }
232
233 fn intern_generic_arg(
234 &self,
235 parameter: chalk_ir::GenericArgData<Self>,
236 ) -> chalk_ir::GenericArgData<Self> {
237 parameter
238 }
239
240 fn generic_arg_data<'a>(
241 &self,
242 parameter: &'a chalk_ir::GenericArgData<Self>,
243 ) -> &'a chalk_ir::GenericArgData<Self> {
244 parameter
245 }
246
247 fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> {
248 Arc::new(goal)
249 }
250
251 fn intern_goals<E>(
252 &self,
253 data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
254 ) -> Result<Self::InternedGoals, E> {
255 data.into_iter().collect()
256 }
257
258 fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> {
259 goal
260 }
261
262 fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] {
263 goals
264 }
265
266 fn intern_substitution<E>(
267 &self,
268 data: impl IntoIterator<Item = Result<GenericArg<Self>, E>>,
269 ) -> Result<Vec<GenericArg<Self>>, E> {
270 data.into_iter().collect()
271 }
272
273 fn substitution_data<'a>(
274 &self,
275 substitution: &'a Vec<GenericArg<Self>>,
276 ) -> &'a [GenericArg<Self>] {
277 substitution
278 }
279
280 fn intern_program_clause(
281 &self,
282 data: chalk_ir::ProgramClauseData<Self>,
283 ) -> chalk_ir::ProgramClauseData<Self> {
284 data
285 }
286
287 fn program_clause_data<'a>(
288 &self,
289 clause: &'a chalk_ir::ProgramClauseData<Self>,
290 ) -> &'a chalk_ir::ProgramClauseData<Self> {
291 clause
292 }
293
294 fn intern_program_clauses<E>(
295 &self,
296 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
297 ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> {
298 data.into_iter().collect()
299 }
300
301 fn program_clauses_data<'a>(
302 &self,
303 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>,
304 ) -> &'a [chalk_ir::ProgramClause<Self>] {
305 &clauses
306 }
307
308 fn intern_quantified_where_clauses<E>(
309 &self,
310 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
311 ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
312 data.into_iter().collect()
313 }
314
315 fn quantified_where_clauses_data<'a>(
316 &self,
317 clauses: &'a Self::InternedQuantifiedWhereClauses,
318 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
319 clauses
320 }
321
322 fn intern_generic_arg_kinds<E>(
323 &self,
324 data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>,
325 ) -> Result<Self::InternedVariableKinds, E> {
326 data.into_iter().collect()
327 }
328
329 fn variable_kinds_data<'a>(
330 &self,
331 parameter_kinds: &'a Self::InternedVariableKinds,
332 ) -> &'a [chalk_ir::VariableKind<Self>] {
333 &parameter_kinds
334 }
335
336 fn intern_canonical_var_kinds<E>(
337 &self,
338 data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>,
339 ) -> Result<Self::InternedCanonicalVarKinds, E> {
340 data.into_iter().collect()
341 }
342
343 fn canonical_var_kinds_data<'a>(
344 &self,
345 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
346 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
347 &canonical_var_kinds
348 }
349}
350
351impl chalk_ir::interner::HasInterner for Interner {
352 type Interner = Self;
353}
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
new file mode 100644
index 000000000..7082cb095
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -0,0 +1,701 @@
1//! This module contains the implementations of the `ToChalk` trait, which
2//! handles conversion between our data types and their corresponding types in
3//! Chalk (in both directions); plus some helper functions for more specialized
4//! conversions.
5
6use chalk_ir::{
7 cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName,
8 UniverseIndex,
9};
10
11use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId};
12use ra_db::salsa::InternKey;
13
14use crate::{
15 db::HirDatabase,
16 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
17 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
18 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy,
19 Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
20};
21
22use super::interner::*;
23use super::*;
24
25impl ToChalk for Ty {
26 type Chalk = chalk_ir::Ty<Interner>;
27 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
28 match self {
29 Ty::Apply(apply_ty) => match apply_ty.ctor {
30 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
31 TypeCtor::FnPtr { num_args: _ } => {
32 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
33 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution })
34 .intern(&Interner)
35 }
36 _ => {
37 let name = apply_ty.ctor.to_chalk(db);
38 let substitution = apply_ty.parameters.to_chalk(db);
39 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
40 }
41 },
42 Ty::Projection(proj_ty) => {
43 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
44 let substitution = proj_ty.parameters.to_chalk(db);
45 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
46 associated_ty_id,
47 substitution,
48 })
49 .cast(&Interner)
50 .intern(&Interner)
51 }
52 Ty::Placeholder(id) => {
53 let interned_id = db.intern_type_param_id(id);
54 PlaceholderIndex {
55 ui: UniverseIndex::ROOT,
56 idx: interned_id.as_intern_id().as_usize(),
57 }
58 .to_ty::<Interner>(&Interner)
59 }
60 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
61 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
62 Ty::Dyn(predicates) => {
63 let where_clauses = chalk_ir::QuantifiedWhereClauses::from(
64 &Interner,
65 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
66 );
67 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
68 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
69 }
70 Ty::Opaque(_) | Ty::Unknown => {
71 let substitution = chalk_ir::Substitution::empty(&Interner);
72 let name = TypeName::Error;
73 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
74 }
75 }
76 }
77 fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
78 match chalk.data(&Interner).clone() {
79 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
80 TypeName::Error => Ty::Unknown,
81 TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution),
82 _ => {
83 let ctor = from_chalk(db, apply_ty.name);
84 let parameters = from_chalk(db, apply_ty.substitution);
85 Ty::Apply(ApplicationTy { ctor, parameters })
86 }
87 },
88 chalk_ir::TyData::Placeholder(idx) => {
89 assert_eq!(idx.ui, UniverseIndex::ROOT);
90 let interned_id = crate::db::GlobalTypeParamId::from_intern_id(
91 crate::salsa::InternId::from(idx.idx),
92 );
93 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
94 }
95 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
96 let associated_ty = from_chalk(db, proj.associated_ty_id);
97 let parameters = from_chalk(db, proj.substitution);
98 Ty::Projection(ProjectionTy { associated_ty, parameters })
99 }
100 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
101 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => {
102 let parameters: Substs = from_chalk(db, substitution);
103 Ty::Apply(ApplicationTy {
104 ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 },
105 parameters,
106 })
107 }
108 chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
109 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
110 chalk_ir::TyData::Dyn(where_clauses) => {
111 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
112 let predicates = where_clauses
113 .bounds
114 .skip_binders()
115 .iter(&Interner)
116 .map(|c| from_chalk(db, c.clone()))
117 .collect();
118 Ty::Dyn(predicates)
119 }
120 }
121 }
122}
123
124const LIFETIME_PLACEHOLDER: PlaceholderIndex =
125 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX };
126
127/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
128/// fake lifetime here, because Chalks built-in logic may expect it to be there.
129fn ref_to_chalk(
130 db: &dyn HirDatabase,
131 mutability: Mutability,
132 subst: Substs,
133) -> chalk_ir::Ty<Interner> {
134 let arg = subst[0].clone().to_chalk(db);
135 let lifetime = LIFETIME_PLACEHOLDER.to_lifetime(&Interner);
136 chalk_ir::ApplicationTy {
137 name: TypeName::Ref(mutability.to_chalk(db)),
138 substitution: chalk_ir::Substitution::from(
139 &Interner,
140 vec![lifetime.cast(&Interner), arg.cast(&Interner)],
141 ),
142 }
143 .intern(&Interner)
144}
145
146/// Here we remove the lifetime from the type we got from Chalk.
147fn ref_from_chalk(
148 db: &dyn HirDatabase,
149 mutability: chalk_ir::Mutability,
150 subst: chalk_ir::Substitution<Interner>,
151) -> Ty {
152 let tys = subst
153 .iter(&Interner)
154 .filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone())))
155 .collect();
156 Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys))
157}
158
159impl ToChalk for Substs {
160 type Chalk = chalk_ir::Substitution<Interner>;
161
162 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
163 chalk_ir::Substitution::from(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db)))
164 }
165
166 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
167 let tys = parameters
168 .iter(&Interner)
169 .map(|p| match p.ty(&Interner) {
170 Some(ty) => from_chalk(db, ty.clone()),
171 None => unimplemented!(),
172 })
173 .collect();
174 Substs(tys)
175 }
176}
177
178impl ToChalk for TraitRef {
179 type Chalk = chalk_ir::TraitRef<Interner>;
180
181 fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> {
182 let trait_id = self.trait_.to_chalk(db);
183 let substitution = self.substs.to_chalk(db);
184 chalk_ir::TraitRef { trait_id, substitution }
185 }
186
187 fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
188 let trait_ = from_chalk(db, trait_ref.trait_id);
189 let substs = from_chalk(db, trait_ref.substitution);
190 TraitRef { trait_, substs }
191 }
192}
193
194impl ToChalk for hir_def::TraitId {
195 type Chalk = TraitId;
196
197 fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId {
198 chalk_ir::TraitId(self.as_intern_id())
199 }
200
201 fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
202 InternKey::from_intern_id(trait_id.0)
203 }
204}
205
206impl ToChalk for TypeCtor {
207 type Chalk = TypeName<Interner>;
208
209 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> {
210 match self {
211 TypeCtor::AssociatedType(type_alias) => {
212 let type_id = type_alias.to_chalk(db);
213 TypeName::AssociatedType(type_id)
214 }
215
216 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
217 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
218 TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
219 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => {
220 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
221 }
222 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => {
223 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
224 }
225
226 TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()),
227 TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)),
228 TypeCtor::Slice => TypeName::Slice,
229 TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
230 TypeCtor::Str => TypeName::Str,
231 TypeCtor::FnDef(callable_def) => {
232 let id = callable_def.to_chalk(db);
233 TypeName::FnDef(id)
234 }
235 TypeCtor::Int(Uncertain::Unknown)
236 | TypeCtor::Float(Uncertain::Unknown)
237 | TypeCtor::Adt(_)
238 | TypeCtor::Array
239 | TypeCtor::FnPtr { .. }
240 | TypeCtor::Never
241 | TypeCtor::Closure { .. } => {
242 // other TypeCtors get interned and turned into a chalk StructId
243 let struct_id = db.intern_type_ctor(self).into();
244 TypeName::Adt(struct_id)
245 }
246 }
247 }
248
249 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
250 match type_name {
251 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
252 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
253 TypeName::OpaqueType(_) => unreachable!(),
254
255 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
256 TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
257 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
258 signedness: Signedness::Signed,
259 bitness: bitness_from_chalk_int(int_ty),
260 })),
261 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy {
262 signedness: Signedness::Unsigned,
263 bitness: bitness_from_chalk_uint(uint_ty),
264 })),
265 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
266 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 }))
267 }
268 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
269 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 }))
270 }
271 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
272 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
273 TypeName::Slice => TypeCtor::Slice,
274 TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
275 TypeName::Str => TypeCtor::Str,
276
277 TypeName::FnDef(fn_def_id) => {
278 let callable_def = from_chalk(db, fn_def_id);
279 TypeCtor::FnDef(callable_def)
280 }
281
282 TypeName::Error => {
283 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
284 unreachable!()
285 }
286 }
287 }
288}
289
290fn bitness_from_chalk_uint(uint_ty: chalk_ir::UintTy) -> IntBitness {
291 use chalk_ir::UintTy;
292
293 match uint_ty {
294 UintTy::Usize => IntBitness::Xsize,
295 UintTy::U8 => IntBitness::X8,
296 UintTy::U16 => IntBitness::X16,
297 UintTy::U32 => IntBitness::X32,
298 UintTy::U64 => IntBitness::X64,
299 UintTy::U128 => IntBitness::X128,
300 }
301}
302
303fn bitness_from_chalk_int(int_ty: chalk_ir::IntTy) -> IntBitness {
304 use chalk_ir::IntTy;
305
306 match int_ty {
307 IntTy::Isize => IntBitness::Xsize,
308 IntTy::I8 => IntBitness::X8,
309 IntTy::I16 => IntBitness::X16,
310 IntTy::I32 => IntBitness::X32,
311 IntTy::I64 => IntBitness::X64,
312 IntTy::I128 => IntBitness::X128,
313 }
314}
315
316fn int_ty_to_chalk(int_ty: IntTy) -> Scalar {
317 use chalk_ir::{IntTy, UintTy};
318
319 match int_ty.signedness {
320 Signedness::Signed => Scalar::Int(match int_ty.bitness {
321 IntBitness::Xsize => IntTy::Isize,
322 IntBitness::X8 => IntTy::I8,
323 IntBitness::X16 => IntTy::I16,
324 IntBitness::X32 => IntTy::I32,
325 IntBitness::X64 => IntTy::I64,
326 IntBitness::X128 => IntTy::I128,
327 }),
328 Signedness::Unsigned => Scalar::Uint(match int_ty.bitness {
329 IntBitness::Xsize => UintTy::Usize,
330 IntBitness::X8 => UintTy::U8,
331 IntBitness::X16 => UintTy::U16,
332 IntBitness::X32 => UintTy::U32,
333 IntBitness::X64 => UintTy::U64,
334 IntBitness::X128 => UintTy::U128,
335 }),
336 }
337}
338
339impl ToChalk for Mutability {
340 type Chalk = chalk_ir::Mutability;
341 fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
342 match self {
343 Mutability::Shared => chalk_ir::Mutability::Not,
344 Mutability::Mut => chalk_ir::Mutability::Mut,
345 }
346 }
347 fn from_chalk(_db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
348 match chalk {
349 chalk_ir::Mutability::Mut => Mutability::Mut,
350 chalk_ir::Mutability::Not => Mutability::Shared,
351 }
352 }
353}
354
355impl ToChalk for Impl {
356 type Chalk = ImplId;
357
358 fn to_chalk(self, db: &dyn HirDatabase) -> ImplId {
359 db.intern_chalk_impl(self).into()
360 }
361
362 fn from_chalk(db: &dyn HirDatabase, impl_id: ImplId) -> Impl {
363 db.lookup_intern_chalk_impl(impl_id.into())
364 }
365}
366
367impl ToChalk for CallableDef {
368 type Chalk = FnDefId;
369
370 fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
371 db.intern_callable_def(self).into()
372 }
373
374 fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDef {
375 db.lookup_intern_callable_def(fn_def_id.into())
376 }
377}
378
379impl ToChalk for TypeAliasId {
380 type Chalk = AssocTypeId;
381
382 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId {
383 chalk_ir::AssocTypeId(self.as_intern_id())
384 }
385
386 fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId {
387 InternKey::from_intern_id(type_alias_id.0)
388 }
389}
390
391impl ToChalk for AssocTyValue {
392 type Chalk = AssociatedTyValueId;
393
394 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValueId {
395 db.intern_assoc_ty_value(self).into()
396 }
397
398 fn from_chalk(db: &dyn HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue {
399 db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into())
400 }
401}
402
403impl ToChalk for GenericPredicate {
404 type Chalk = chalk_ir::QuantifiedWhereClause<Interner>;
405
406 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> {
407 match self {
408 GenericPredicate::Implemented(trait_ref) => {
409 let chalk_trait_ref = trait_ref.to_chalk(db);
410 let chalk_trait_ref = chalk_trait_ref.shifted_in(&Interner);
411 make_binders(chalk_ir::WhereClause::Implemented(chalk_trait_ref), 0)
412 }
413 GenericPredicate::Projection(projection_pred) => {
414 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
415 let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
416 let alias = chalk_ir::AliasTy::Projection(projection);
417 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
418 }
419 GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
420 }
421 }
422
423 fn from_chalk(
424 db: &dyn HirDatabase,
425 where_clause: chalk_ir::QuantifiedWhereClause<Interner>,
426 ) -> GenericPredicate {
427 // we don't produce any where clauses with binders and can't currently deal with them
428 match where_clause
429 .skip_binders()
430 .shifted_out(&Interner)
431 .expect("unexpected bound vars in where clause")
432 {
433 chalk_ir::WhereClause::Implemented(tr) => {
434 GenericPredicate::Implemented(from_chalk(db, tr))
435 }
436 chalk_ir::WhereClause::AliasEq(projection_eq) => {
437 let projection_ty = from_chalk(
438 db,
439 match projection_eq.alias {
440 chalk_ir::AliasTy::Projection(p) => p,
441 _ => unimplemented!(),
442 },
443 );
444 let ty = from_chalk(db, projection_eq.ty);
445 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty })
446 }
447 }
448 }
449}
450
451impl ToChalk for ProjectionTy {
452 type Chalk = chalk_ir::ProjectionTy<Interner>;
453
454 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
455 chalk_ir::ProjectionTy {
456 associated_ty_id: self.associated_ty.to_chalk(db),
457 substitution: self.parameters.to_chalk(db),
458 }
459 }
460
461 fn from_chalk(
462 db: &dyn HirDatabase,
463 projection_ty: chalk_ir::ProjectionTy<Interner>,
464 ) -> ProjectionTy {
465 ProjectionTy {
466 associated_ty: from_chalk(db, projection_ty.associated_ty_id),
467 parameters: from_chalk(db, projection_ty.substitution),
468 }
469 }
470}
471
472impl ToChalk for ProjectionPredicate {
473 type Chalk = chalk_ir::AliasEq<Interner>;
474
475 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
476 chalk_ir::AliasEq {
477 alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
478 ty: self.ty.to_chalk(db),
479 }
480 }
481
482 fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
483 unimplemented!()
484 }
485}
486
487impl ToChalk for Obligation {
488 type Chalk = chalk_ir::DomainGoal<Interner>;
489
490 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
491 match self {
492 Obligation::Trait(tr) => tr.to_chalk(db).cast(&Interner),
493 Obligation::Projection(pr) => pr.to_chalk(db).cast(&Interner),
494 }
495 }
496
497 fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
498 unimplemented!()
499 }
500}
501
502impl<T> ToChalk for Canonical<T>
503where
504 T: ToChalk,
505 T::Chalk: HasInterner<Interner = Interner>,
506{
507 type Chalk = chalk_ir::Canonical<T::Chalk>;
508
509 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
510 let parameter = chalk_ir::CanonicalVarKind::new(
511 chalk_ir::VariableKind::Ty,
512 chalk_ir::UniverseIndex::ROOT,
513 );
514 let value = self.value.to_chalk(db);
515 chalk_ir::Canonical {
516 value,
517 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
518 }
519 }
520
521 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
522 Canonical {
523 num_vars: canonical.binders.len(&Interner),
524 value: from_chalk(db, canonical.value),
525 }
526 }
527}
528
529impl ToChalk for Arc<TraitEnvironment> {
530 type Chalk = chalk_ir::Environment<Interner>;
531
532 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> {
533 let mut clauses = Vec::new();
534 for pred in &self.predicates {
535 if pred.is_error() {
536 // for env, we just ignore errors
537 continue;
538 }
539 let program_clause: chalk_ir::ProgramClause<Interner> =
540 pred.clone().to_chalk(db).cast(&Interner);
541 clauses.push(program_clause.into_from_env_clause(&Interner));
542 }
543 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
544 }
545
546 fn from_chalk(
547 _db: &dyn HirDatabase,
548 _env: chalk_ir::Environment<Interner>,
549 ) -> Arc<TraitEnvironment> {
550 unimplemented!()
551 }
552}
553
554impl<T: ToChalk> ToChalk for InEnvironment<T>
555where
556 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
557{
558 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
559
560 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
561 chalk_ir::InEnvironment {
562 environment: self.environment.to_chalk(db),
563 goal: self.value.to_chalk(db),
564 }
565 }
566
567 fn from_chalk(
568 db: &dyn HirDatabase,
569 in_env: chalk_ir::InEnvironment<T::Chalk>,
570 ) -> InEnvironment<T> {
571 InEnvironment {
572 environment: from_chalk(db, in_env.environment),
573 value: from_chalk(db, in_env.goal),
574 }
575 }
576}
577
578impl ToChalk for builtin::BuiltinImplData {
579 type Chalk = ImplDatum;
580
581 fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
582 let impl_type = chalk_rust_ir::ImplType::External;
583 let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
584
585 let impl_datum_bound =
586 chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
587 let associated_ty_value_ids =
588 self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
589 chalk_rust_ir::ImplDatum {
590 binders: make_binders(impl_datum_bound, self.num_vars),
591 impl_type,
592 polarity: chalk_rust_ir::Polarity::Positive,
593 associated_ty_value_ids,
594 }
595 }
596
597 fn from_chalk(_db: &dyn HirDatabase, _data: ImplDatum) -> Self {
598 unimplemented!()
599 }
600}
601
602impl ToChalk for builtin::BuiltinImplAssocTyValueData {
603 type Chalk = AssociatedTyValue;
604
605 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
606 let ty = self.value.to_chalk(db);
607 let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty };
608
609 chalk_rust_ir::AssociatedTyValue {
610 associated_ty_id: self.assoc_ty_id.to_chalk(db),
611 impl_id: self.impl_.to_chalk(db),
612 value: make_binders(value_bound, self.num_vars),
613 }
614 }
615
616 fn from_chalk(
617 _db: &dyn HirDatabase,
618 _data: AssociatedTyValue,
619 ) -> builtin::BuiltinImplAssocTyValueData {
620 unimplemented!()
621 }
622}
623
624pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
625where
626 T: HasInterner<Interner = Interner>,
627{
628 chalk_ir::Binders::new(
629 chalk_ir::VariableKinds::from(
630 &Interner,
631 std::iter::repeat(chalk_ir::VariableKind::Ty).take(num_vars),
632 ),
633 value,
634 )
635}
636
637pub(super) fn convert_where_clauses(
638 db: &dyn HirDatabase,
639 def: GenericDefId,
640 substs: &Substs,
641) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
642 let generic_predicates = db.generic_predicates(def);
643 let mut result = Vec::with_capacity(generic_predicates.len());
644 for pred in generic_predicates.iter() {
645 if pred.value.is_error() {
646 // skip errored predicates completely
647 continue;
648 }
649 result.push(pred.clone().subst(substs).to_chalk(db));
650 }
651 result
652}
653
654pub(super) fn generic_predicate_to_inline_bound(
655 db: &dyn HirDatabase,
656 pred: &GenericPredicate,
657 self_ty: &Ty,
658) -> Option<chalk_rust_ir::InlineBound<Interner>> {
659 // An InlineBound is like a GenericPredicate, except the self type is left out.
660 // We don't have a special type for this, but Chalk does.
661 match pred {
662 GenericPredicate::Implemented(trait_ref) => {
663 if &trait_ref.substs[0] != self_ty {
664 // we can only convert predicates back to type bounds if they
665 // have the expected self type
666 return None;
667 }
668 let args_no_self = trait_ref.substs[1..]
669 .iter()
670 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
671 .collect();
672 let trait_bound =
673 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
674 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
675 }
676 GenericPredicate::Projection(proj) => {
677 if &proj.projection_ty.parameters[0] != self_ty {
678 return None;
679 }
680 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
681 AssocContainerId::TraitId(t) => t,
682 _ => panic!("associated type not in trait"),
683 };
684 let args_no_self = proj.projection_ty.parameters[1..]
685 .iter()
686 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
687 .collect();
688 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
689 value: proj.ty.clone().to_chalk(db),
690 trait_bound: chalk_rust_ir::TraitBound {
691 trait_id: trait_.to_chalk(db),
692 args_no_self,
693 },
694 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
695 parameters: Vec::new(), // FIXME we don't support generic associated types yet
696 };
697 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
698 }
699 GenericPredicate::Error => None,
700 }
701}
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index 4867cb17e..d88828c7c 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -1,7 +1,7 @@
1//! Implementation of Chalk debug helper functions using TLS. 1//! Implementation of Chalk debug helper functions using TLS.
2use std::fmt; 2use std::fmt;
3 3
4use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName}; 4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName};
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use super::{from_chalk, Interner}; 7use super::{from_chalk, Interner};
@@ -15,10 +15,10 @@ pub struct DebugContext<'a>(&'a (dyn HirDatabase + 'a));
15impl DebugContext<'_> { 15impl DebugContext<'_> {
16 pub fn debug_struct_id( 16 pub fn debug_struct_id(
17 &self, 17 &self,
18 id: super::StructId, 18 id: super::AdtId,
19 f: &mut fmt::Formatter<'_>, 19 f: &mut fmt::Formatter<'_>,
20 ) -> Result<(), fmt::Error> { 20 ) -> Result<(), fmt::Error> {
21 let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Struct(id)); 21 let type_ctor: TypeCtor = from_chalk(self.0, TypeName::Adt(id));
22 match type_ctor { 22 match type_ctor {
23 TypeCtor::Bool => write!(f, "bool")?, 23 TypeCtor::Bool => write!(f, "bool")?,
24 TypeCtor::Char => write!(f, "char")?, 24 TypeCtor::Char => write!(f, "char")?,
@@ -188,9 +188,9 @@ impl DebugContext<'_> {
188 write!(fmt, "{:?}", lifetime.data(&Interner)) 188 write!(fmt, "{:?}", lifetime.data(&Interner))
189 } 189 }
190 190
191 pub fn debug_parameter( 191 pub fn debug_generic_arg(
192 &self, 192 &self,
193 parameter: &Parameter<Interner>, 193 parameter: &GenericArg<Interner>,
194 fmt: &mut fmt::Formatter<'_>, 194 fmt: &mut fmt::Formatter<'_>,
195 ) -> Result<(), fmt::Error> { 195 ) -> Result<(), fmt::Error> {
196 write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()) 196 write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())
@@ -244,6 +244,79 @@ impl DebugContext<'_> {
244 ) -> Result<(), fmt::Error> { 244 ) -> Result<(), fmt::Error> {
245 write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)) 245 write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))
246 } 246 }
247
248 pub fn debug_fn_def_id(
249 &self,
250 fn_def_id: chalk_ir::FnDefId<Interner>,
251 fmt: &mut fmt::Formatter<'_>,
252 ) -> Result<(), fmt::Error> {
253 let def: CallableDef = from_chalk(self.0, fn_def_id);
254 let name = match def {
255 CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(),
256 CallableDef::StructId(s) => self.0.struct_data(s).name.clone(),
257 CallableDef::EnumVariantId(e) => {
258 let enum_data = self.0.enum_data(e.parent);
259 enum_data.variants[e.local_id].name.clone()
260 }
261 };
262 match def {
263 CallableDef::FunctionId(_) => write!(fmt, "{{fn {}}}", name),
264 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
265 write!(fmt, "{{ctor {}}}", name)
266 }
267 }
268 }
269
270 pub fn debug_const(
271 &self,
272 _constant: &chalk_ir::Const<Interner>,
273 fmt: &mut fmt::Formatter<'_>,
274 ) -> fmt::Result {
275 write!(fmt, "const")
276 }
277
278 pub fn debug_variable_kinds(
279 &self,
280 variable_kinds: &chalk_ir::VariableKinds<Interner>,
281 fmt: &mut fmt::Formatter<'_>,
282 ) -> fmt::Result {
283 write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))
284 }
285 pub fn debug_variable_kinds_with_angles(
286 &self,
287 variable_kinds: &chalk_ir::VariableKinds<Interner>,
288 fmt: &mut fmt::Formatter<'_>,
289 ) -> fmt::Result {
290 write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))
291 }
292 pub fn debug_canonical_var_kinds(
293 &self,
294 canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>,
295 fmt: &mut fmt::Formatter<'_>,
296 ) -> fmt::Result {
297 write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))
298 }
299 pub fn debug_program_clause(
300 &self,
301 clause: &chalk_ir::ProgramClause<Interner>,
302 fmt: &mut fmt::Formatter<'_>,
303 ) -> fmt::Result {
304 write!(fmt, "{:?}", clause.data(&Interner))
305 }
306 pub fn debug_program_clauses(
307 &self,
308 clauses: &chalk_ir::ProgramClauses<Interner>,
309 fmt: &mut fmt::Formatter<'_>,
310 ) -> fmt::Result {
311 write!(fmt, "{:?}", clauses.as_slice(&Interner))
312 }
313 pub fn debug_quantified_where_clauses(
314 &self,
315 clauses: &chalk_ir::QuantifiedWhereClauses<Interner>,
316 fmt: &mut fmt::Formatter<'_>,
317 ) -> fmt::Result {
318 write!(fmt, "{:?}", clauses.as_slice(&Interner))
319 }
247} 320}
248 321
249mod unsafe_tls { 322mod unsafe_tls {
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index 85d1f0cb1..defd8176f 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -246,6 +246,35 @@ mod tests {
246 } 246 }
247 247
248 #[test] 248 #[test]
249 fn test_call_hierarchy_in_tests_mod() {
250 check_hierarchy(
251 r#"
252 //- /lib.rs cfg:test
253 fn callee() {}
254 fn caller1() {
255 call<|>ee();
256 }
257
258 #[cfg(test)]
259 mod tests {
260 use super::*;
261
262 #[test]
263 fn test_caller() {
264 callee();
265 }
266 }
267 "#,
268 "callee FN_DEF FileId(1) 0..14 3..9",
269 &[
270 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
271 "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]",
272 ],
273 &[],
274 );
275 }
276
277 #[test]
249 fn test_call_hierarchy_in_different_files() { 278 fn test_call_hierarchy_in_different_files() {
250 check_hierarchy( 279 check_hierarchy(
251 r#" 280 r#"
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 6021f7279..cfb7c1e38 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -63,8 +63,8 @@ impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
65 s.field("label", &self.label()).field("source_range", &self.source_range()); 65 s.field("label", &self.label()).field("source_range", &self.source_range());
66 if self.text_edit().as_indels().len() == 1 { 66 if self.text_edit().len() == 1 {
67 let atom = &self.text_edit().as_indels()[0]; 67 let atom = &self.text_edit().iter().next().unwrap();
68 s.field("delete", &atom.delete); 68 s.field("delete", &atom.delete);
69 s.field("insert", &atom.insert); 69 s.field("insert", &atom.insert);
70 } else { 70 } else {
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 54c2bcc09..3d83c0f71 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -21,7 +21,7 @@ use ra_syntax::{
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -63,8 +63,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
63 .parent() 63 .parent()
64 .unwrap_or_else(|| RelativePath::new("")) 64 .unwrap_or_else(|| RelativePath::new(""))
65 .join(&d.candidate); 65 .join(&d.candidate);
66 let create_file = FileSystemEdit::CreateFile { source_root, path }; 66 let fix =
67 let fix = SourceChange::file_system_edit("Create module", create_file); 67 Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into());
68 res.borrow_mut().push(Diagnostic { 68 res.borrow_mut().push(Diagnostic {
69 range: sema.diagnostics_range(d).range, 69 range: sema.diagnostics_range(d).range,
70 message: d.message(), 70 message: d.message(),
@@ -88,14 +88,12 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
88 field_list = field_list.append_field(&field); 88 field_list = field_list.append_field(&field);
89 } 89 }
90 90
91 let mut builder = TextEditBuilder::default(); 91 let edit = {
92 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); 92 let mut builder = TextEditBuilder::default();
93 93 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder);
94 Some(SourceChange::source_file_edit_from( 94 builder.finish()
95 "Fill struct fields", 95 };
96 file_id, 96 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
97 builder.finish(),
98 ))
99 }; 97 };
100 98
101 res.borrow_mut().push(Diagnostic { 99 res.borrow_mut().push(Diagnostic {
@@ -117,7 +115,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
117 let node = d.ast(db); 115 let node = d.ast(db);
118 let replacement = format!("Ok({})", node.syntax()); 116 let replacement = format!("Ok({})", node.syntax());
119 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 117 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
120 let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); 118 let source_change = SourceChange::source_file_edit_from(file_id, edit);
119 let fix = Fix::new("Wrap with ok", source_change);
121 res.borrow_mut().push(Diagnostic { 120 res.borrow_mut().push(Diagnostic {
122 range: sema.diagnostics_range(d).range, 121 range: sema.diagnostics_range(d).range,
123 message: d.message(), 122 message: d.message(),
@@ -154,9 +153,9 @@ fn check_unnecessary_braces_in_use_statement(
154 range, 153 range,
155 message: "Unnecessary braces in use statement".to_string(), 154 message: "Unnecessary braces in use statement".to_string(),
156 severity: Severity::WeakWarning, 155 severity: Severity::WeakWarning,
157 fix: Some(SourceChange::source_file_edit( 156 fix: Some(Fix::new(
158 "Remove unnecessary braces", 157 "Remove unnecessary braces",
159 SourceFileEdit { file_id, edit }, 158 SourceFileEdit { file_id, edit }.into(),
160 )), 159 )),
161 }); 160 });
162 } 161 }
@@ -198,9 +197,9 @@ fn check_struct_shorthand_initialization(
198 range: record_field.syntax().text_range(), 197 range: record_field.syntax().text_range(),
199 message: "Shorthand struct initialization".to_string(), 198 message: "Shorthand struct initialization".to_string(),
200 severity: Severity::WeakWarning, 199 severity: Severity::WeakWarning,
201 fix: Some(SourceChange::source_file_edit( 200 fix: Some(Fix::new(
202 "Use struct shorthand initialization", 201 "Use struct shorthand initialization",
203 SourceFileEdit { file_id, edit }, 202 SourceFileEdit { file_id, edit }.into(),
204 )), 203 )),
205 }); 204 });
206 } 205 }
@@ -240,7 +239,7 @@ mod tests {
240 let diagnostic = 239 let diagnostic =
241 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); 240 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
242 let mut fix = diagnostic.fix.unwrap(); 241 let mut fix = diagnostic.fix.unwrap();
243 let edit = fix.source_file_edits.pop().unwrap().edit; 242 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
244 let actual = { 243 let actual = {
245 let mut actual = before.to_string(); 244 let mut actual = before.to_string();
246 edit.apply(&mut actual); 245 edit.apply(&mut actual);
@@ -258,7 +257,7 @@ mod tests {
258 let (analysis, file_position) = analysis_and_position(fixture); 257 let (analysis, file_position) = analysis_and_position(fixture);
259 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 258 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
260 let mut fix = diagnostic.fix.unwrap(); 259 let mut fix = diagnostic.fix.unwrap();
261 let edit = fix.source_file_edits.pop().unwrap().edit; 260 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
262 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 261 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
263 let actual = { 262 let actual = {
264 let mut actual = target_file_contents.to_string(); 263 let mut actual = target_file_contents.to_string();
@@ -295,7 +294,7 @@ mod tests {
295 let (analysis, file_id) = single_file(before); 294 let (analysis, file_id) = single_file(before);
296 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 295 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap();
297 let mut fix = diagnostic.fix.unwrap(); 296 let mut fix = diagnostic.fix.unwrap();
298 let edit = fix.source_file_edits.pop().unwrap().edit; 297 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
299 let actual = { 298 let actual = {
300 let mut actual = before.to_string(); 299 let mut actual = before.to_string();
301 edit.apply(&mut actual); 300 edit.apply(&mut actual);
@@ -616,23 +615,24 @@ mod tests {
616 Diagnostic { 615 Diagnostic {
617 message: "unresolved module", 616 message: "unresolved module",
618 range: 0..8, 617 range: 0..8,
618 severity: Error,
619 fix: Some( 619 fix: Some(
620 SourceChange { 620 Fix {
621 label: "Create module", 621 label: "Create module",
622 source_file_edits: [], 622 source_change: SourceChange {
623 file_system_edits: [ 623 source_file_edits: [],
624 CreateFile { 624 file_system_edits: [
625 source_root: SourceRootId( 625 CreateFile {
626 0, 626 source_root: SourceRootId(
627 ), 627 0,
628 path: "foo.rs", 628 ),
629 }, 629 path: "foo.rs",
630 ], 630 },
631 cursor_position: None, 631 ],
632 is_snippet: false, 632 is_snippet: false,
633 },
633 }, 634 },
634 ), 635 ),
635 severity: Error,
636 }, 636 },
637 ] 637 ]
638 "###); 638 "###);
@@ -666,30 +666,31 @@ mod tests {
666 Diagnostic { 666 Diagnostic {
667 message: "Missing structure fields:\n- b", 667 message: "Missing structure fields:\n- b",
668 range: 224..233, 668 range: 224..233,
669 severity: Error,
669 fix: Some( 670 fix: Some(
670 SourceChange { 671 Fix {
671 label: "Fill struct fields", 672 label: "Fill struct fields",
672 source_file_edits: [ 673 source_change: SourceChange {
673 SourceFileEdit { 674 source_file_edits: [
674 file_id: FileId( 675 SourceFileEdit {
675 1, 676 file_id: FileId(
676 ), 677 1,
677 edit: TextEdit { 678 ),
678 indels: [ 679 edit: TextEdit {
679 Indel { 680 indels: [
680 insert: "{a:42, b: ()}", 681 Indel {
681 delete: 3..9, 682 insert: "{a:42, b: ()}",
682 }, 683 delete: 3..9,
683 ], 684 },
685 ],
686 },
684 }, 687 },
685 }, 688 ],
686 ], 689 file_system_edits: [],
687 file_system_edits: [], 690 is_snippet: false,
688 cursor_position: None, 691 },
689 is_snippet: false,
690 }, 692 },
691 ), 693 ),
692 severity: Error,
693 }, 694 },
694 ] 695 ]
695 "###); 696 "###);
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 722092de9..827c094e7 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -79,16 +79,17 @@ pub(crate) fn rust_code_markup_with_doc(
79 doc: Option<&str>, 79 doc: Option<&str>,
80 mod_path: Option<&str>, 80 mod_path: Option<&str>,
81) -> String { 81) -> String {
82 let mut buf = "```rust\n".to_owned(); 82 let mut buf = String::new();
83 83
84 if let Some(mod_path) = mod_path { 84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() { 85 if !mod_path.is_empty() {
86 format_to!(buf, "{}\n", mod_path); 86 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
87 } 87 }
88 } 88 }
89 format_to!(buf, "{}\n```", code); 89 format_to!(buf, "```rust\n{}\n```", code);
90 90
91 if let Some(doc) = doc { 91 if let Some(doc) = doc {
92 format_to!(buf, "\n___");
92 format_to!(buf, "\n\n{}", doc); 93 format_to!(buf, "\n\n{}", doc);
93 } 94 }
94 95
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index befa977c7..3e721dcca 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -405,7 +405,7 @@ mod tests {
405 }; 405 };
406 } 406 }
407 "#, 407 "#,
408 &["Foo\nfield_a: u32"], 408 &["Foo\n```\n\n```rust\nfield_a: u32"],
409 ); 409 );
410 410
411 // Hovering over the field in the definition 411 // Hovering over the field in the definition
@@ -422,7 +422,7 @@ mod tests {
422 }; 422 };
423 } 423 }
424 "#, 424 "#,
425 &["Foo\nfield_a: u32"], 425 &["Foo\n```\n\n```rust\nfield_a: u32"],
426 ); 426 );
427 } 427 }
428 428
@@ -475,7 +475,7 @@ fn main() {
475 ", 475 ",
476 ); 476 );
477 let hover = analysis.hover(position).unwrap().unwrap(); 477 let hover = analysis.hover(position).unwrap().unwrap();
478 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); 478 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome"));
479 479
480 let (analysis, position) = single_file_with_position( 480 let (analysis, position) = single_file_with_position(
481 " 481 "
@@ -503,8 +503,12 @@ fn main() {
503 "#, 503 "#,
504 &[" 504 &["
505Option 505Option
506```
507
508```rust
506None 509None
507``` 510```
511___
508 512
509The None variant 513The None variant
510 " 514 "
@@ -524,8 +528,12 @@ The None variant
524 "#, 528 "#,
525 &[" 529 &["
526Option 530Option
531```
532
533```rust
527Some 534Some
528``` 535```
536___
529 537
530The Some variant 538The Some variant
531 " 539 "
@@ -606,7 +614,10 @@ fn func(foo: i32) { if true { <|>foo; }; }
606 ", 614 ",
607 ); 615 );
608 let hover = analysis.hover(position).unwrap().unwrap(); 616 let hover = analysis.hover(position).unwrap().unwrap();
609 assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); 617 assert_eq!(
618 trim_markup_opt(hover.info.first()),
619 Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing")
620 );
610 } 621 }
611 622
612 #[test] 623 #[test]
@@ -882,7 +893,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
882 fo<|>o(); 893 fo<|>o();
883 } 894 }
884 ", 895 ",
885 &["fn foo()\n```\n\n<- `\u{3000}` here"], 896 &["fn foo()\n```\n___\n\n<- `\u{3000}` here"],
886 ); 897 );
887 } 898 }
888 899
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index d3af780c4..af1ade8a1 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -166,16 +166,28 @@ fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
166 166
167#[cfg(test)] 167#[cfg(test)]
168mod tests { 168mod tests {
169 use crate::test_utils::{assert_eq_text, check_action, extract_range}; 169 use ra_syntax::SourceFile;
170 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
170 171
171 use super::*; 172 use super::*;
172 173
173 fn check_join_lines(before: &str, after: &str) { 174 fn check_join_lines(before: &str, after: &str) {
174 check_action(before, after, |file, offset| { 175 let (before_cursor_pos, before) = extract_offset(before);
175 let range = TextRange::empty(offset); 176 let file = SourceFile::parse(&before).ok().unwrap();
176 let res = join_lines(file, range); 177
177 Some(res) 178 let range = TextRange::empty(before_cursor_pos);
178 }) 179 let result = join_lines(&file, range);
180
181 let actual = {
182 let mut actual = before.to_string();
183 result.apply(&mut actual);
184 actual
185 };
186 let actual_cursor_pos = result
187 .apply_to_offset(before_cursor_pos)
188 .expect("cursor position is affected by the edit");
189 let actual = add_cursor(&actual, actual_cursor_pos);
190 assert_eq_text!(after, &actual);
179 } 191 }
180 192
181 #[test] 193 #[test]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 83cb498f7..d983cd910 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -42,9 +42,6 @@ mod inlay_hints;
42mod expand_macro; 42mod expand_macro;
43mod ssr; 43mod ssr;
44 44
45#[cfg(test)]
46mod test_utils;
47
48use std::sync::Arc; 45use std::sync::Arc;
49 46
50use ra_cfg::CfgOptions; 47use ra_cfg::CfgOptions;
@@ -87,12 +84,12 @@ pub use ra_db::{
87pub use ra_ide_db::{ 84pub use ra_ide_db::{
88 change::{AnalysisChange, LibraryData}, 85 change::{AnalysisChange, LibraryData},
89 line_index::{LineCol, LineIndex}, 86 line_index::{LineCol, LineIndex},
90 line_index_utils::translate_offset_with_edit,
91 search::SearchScope, 87 search::SearchScope,
92 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
93 symbol_index::Query, 89 symbol_index::Query,
94 RootDatabase, 90 RootDatabase,
95}; 91};
92pub use ra_text_edit::{Indel, TextEdit};
96 93
97pub type Cancelable<T> = Result<T, Canceled>; 94pub type Cancelable<T> = Result<T, Canceled>;
98 95
@@ -100,8 +97,22 @@ pub type Cancelable<T> = Result<T, Canceled>;
100pub struct Diagnostic { 97pub struct Diagnostic {
101 pub message: String, 98 pub message: String,
102 pub range: TextRange, 99 pub range: TextRange,
103 pub fix: Option<SourceChange>,
104 pub severity: Severity, 100 pub severity: Severity,
101 pub fix: Option<Fix>,
102}
103
104#[derive(Debug)]
105pub struct Fix {
106 pub label: String,
107 pub source_change: SourceChange,
108}
109
110impl Fix {
111 pub fn new(label: impl Into<String>, source_change: SourceChange) -> Self {
112 let label = label.into();
113 assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
114 Self { label, source_change }
115 }
105} 116}
106 117
107/// Info associated with a text range. 118/// Info associated with a text range.
@@ -289,20 +300,17 @@ impl Analysis {
289 300
290 /// Returns an edit to remove all newlines in the range, cleaning up minor 301 /// Returns an edit to remove all newlines in the range, cleaning up minor
291 /// stuff like trailing commas. 302 /// stuff like trailing commas.
292 pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { 303 pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> {
293 self.with_db(|db| { 304 self.with_db(|db| {
294 let parse = db.parse(frange.file_id); 305 let parse = db.parse(frange.file_id);
295 let file_edit = SourceFileEdit { 306 join_lines::join_lines(&parse.tree(), frange.range)
296 file_id: frange.file_id,
297 edit: join_lines::join_lines(&parse.tree(), frange.range),
298 };
299 SourceChange::source_file_edit("Join lines", file_edit)
300 }) 307 })
301 } 308 }
302 309
303 /// Returns an edit which should be applied when opening a new line, fixing 310 /// Returns an edit which should be applied when opening a new line, fixing
304 /// up minor stuff like continuing the comment. 311 /// up minor stuff like continuing the comment.
305 pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { 312 /// The edit will be a snippet (with `$0`).
313 pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> {
306 self.with_db(|db| typing::on_enter(&db, position)) 314 self.with_db(|db| typing::on_enter(&db, position))
307 } 315 }
308 316
@@ -500,7 +508,7 @@ impl Analysis {
500 ) -> Cancelable<Result<SourceChange, SsrError>> { 508 ) -> Cancelable<Result<SourceChange, SsrError>> {
501 self.with_db(|db| { 509 self.with_db(|db| {
502 let edits = ssr::parse_search_replace(query, parse_only, db)?; 510 let edits = ssr::parse_search_replace(query, parse_only, db)?;
503 Ok(SourceChange::source_file_edits("Structural Search Replace", edits)) 511 Ok(SourceChange::source_file_edits(edits))
504 }) 512 })
505 } 513 }
506 514
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 2c13f206a..ad78d2d93 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -1,21 +1,81 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::str::FromStr;
3use std::sync::Arc; 4use std::sync::Arc;
4 5
5use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
6use ra_db::{CrateName, Env, RelativePathBuf}; 7use ra_db::{CrateName, Env, RelativePathBuf};
7use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; 8use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER};
8 9
9use crate::{ 10use crate::{
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
11 FileRange, SourceRootId, 12 SourceRootId,
12}; 13};
13 14
15#[derive(Debug)]
16enum MockFileData {
17 Plain { path: String, content: String },
18 Fixture(FixtureEntry),
19}
20
21impl MockFileData {
22 fn new(path: String, content: String) -> Self {
23 // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
24 // see https://github.com/rust-lang/rust/issues/69018
25 MockFileData::Plain { path, content }
26 }
27
28 fn path(&self) -> &str {
29 match self {
30 MockFileData::Plain { path, .. } => path.as_str(),
31 MockFileData::Fixture(f) => f.meta.path().as_str(),
32 }
33 }
34
35 fn content(&self) -> &str {
36 match self {
37 MockFileData::Plain { content, .. } => content,
38 MockFileData::Fixture(f) => f.text.as_str(),
39 }
40 }
41
42 fn cfg_options(&self) -> CfgOptions {
43 match self {
44 MockFileData::Fixture(f) => {
45 f.meta.cfg_options().map_or_else(Default::default, |o| o.clone())
46 }
47 _ => CfgOptions::default(),
48 }
49 }
50
51 fn edition(&self) -> Edition {
52 match self {
53 MockFileData::Fixture(f) => {
54 f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap())
55 }
56 _ => Edition::Edition2018,
57 }
58 }
59
60 fn env(&self) -> Env {
61 match self {
62 MockFileData::Fixture(f) => Env::from(f.meta.env()),
63 _ => Env::default(),
64 }
65 }
66}
67
68impl From<FixtureEntry> for MockFileData {
69 fn from(fixture: FixtureEntry) -> Self {
70 Self::Fixture(fixture)
71 }
72}
73
14/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 74/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
15/// from a set of in-memory files. 75/// from a set of in-memory files.
16#[derive(Debug, Default)] 76#[derive(Debug, Default)]
17pub struct MockAnalysis { 77pub struct MockAnalysis {
18 files: Vec<(String, String)>, 78 files: Vec<MockFileData>,
19} 79}
20 80
21impl MockAnalysis { 81impl MockAnalysis {
@@ -35,7 +95,7 @@ impl MockAnalysis {
35 pub fn with_files(fixture: &str) -> MockAnalysis { 95 pub fn with_files(fixture: &str) -> MockAnalysis {
36 let mut res = MockAnalysis::new(); 96 let mut res = MockAnalysis::new();
37 for entry in parse_fixture(fixture) { 97 for entry in parse_fixture(fixture) {
38 res.add_file(&entry.meta, &entry.text); 98 res.add_file_fixture(entry);
39 } 99 }
40 res 100 res
41 } 101 }
@@ -48,30 +108,44 @@ impl MockAnalysis {
48 for entry in parse_fixture(fixture) { 108 for entry in parse_fixture(fixture) {
49 if entry.text.contains(CURSOR_MARKER) { 109 if entry.text.contains(CURSOR_MARKER) {
50 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); 110 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
51 position = Some(res.add_file_with_position(&entry.meta, &entry.text)); 111 position = Some(res.add_file_fixture_with_position(entry));
52 } else { 112 } else {
53 res.add_file(&entry.meta, &entry.text); 113 res.add_file_fixture(entry);
54 } 114 }
55 } 115 }
56 let position = position.expect("expected a marker (<|>)"); 116 let position = position.expect("expected a marker (<|>)");
57 (res, position) 117 (res, position)
58 } 118 }
59 119
120 pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId {
121 let file_id = self.next_id();
122 self.files.push(MockFileData::from(fixture));
123 file_id
124 }
125
126 pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition {
127 let (offset, text) = extract_offset(&fixture.text);
128 fixture.text = text;
129 let file_id = self.next_id();
130 self.files.push(MockFileData::from(fixture));
131 FilePosition { file_id, offset }
132 }
133
60 pub fn add_file(&mut self, path: &str, text: &str) -> FileId { 134 pub fn add_file(&mut self, path: &str, text: &str) -> FileId {
61 let file_id = FileId((self.files.len() + 1) as u32); 135 let file_id = self.next_id();
62 self.files.push((path.to_string(), text.to_string())); 136 self.files.push(MockFileData::new(path.to_string(), text.to_string()));
63 file_id 137 file_id
64 } 138 }
65 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { 139 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition {
66 let (offset, text) = extract_offset(text); 140 let (offset, text) = extract_offset(text);
67 let file_id = FileId((self.files.len() + 1) as u32); 141 let file_id = self.next_id();
68 self.files.push((path.to_string(), text)); 142 self.files.push(MockFileData::new(path.to_string(), text));
69 FilePosition { file_id, offset } 143 FilePosition { file_id, offset }
70 } 144 }
71 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { 145 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
72 let (range, text) = extract_range(text); 146 let (range, text) = extract_range(text);
73 let file_id = FileId((self.files.len() + 1) as u32); 147 let file_id = self.next_id();
74 self.files.push((path.to_string(), text)); 148 self.files.push(MockFileData::new(path.to_string(), text));
75 FileRange { file_id, range } 149 FileRange { file_id, range }
76 } 150 }
77 pub fn id_of(&self, path: &str) -> FileId { 151 pub fn id_of(&self, path: &str) -> FileId {
@@ -79,7 +153,7 @@ impl MockAnalysis {
79 .files 153 .files
80 .iter() 154 .iter()
81 .enumerate() 155 .enumerate()
82 .find(|(_, (p, _text))| path == p) 156 .find(|(_, data)| path == data.path())
83 .expect("no file in this mock"); 157 .expect("no file in this mock");
84 FileId(idx as u32 + 1) 158 FileId(idx as u32 + 1)
85 } 159 }
@@ -90,18 +164,21 @@ impl MockAnalysis {
90 change.add_root(source_root, true); 164 change.add_root(source_root, true);
91 let mut crate_graph = CrateGraph::default(); 165 let mut crate_graph = CrateGraph::default();
92 let mut root_crate = None; 166 let mut root_crate = None;
93 for (i, (path, contents)) in self.files.into_iter().enumerate() { 167 for (i, data) in self.files.into_iter().enumerate() {
168 let path = data.path();
94 assert!(path.starts_with('/')); 169 assert!(path.starts_with('/'));
95 let path = RelativePathBuf::from_path(&path[1..]).unwrap(); 170 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
171 let cfg_options = data.cfg_options();
96 let file_id = FileId(i as u32 + 1); 172 let file_id = FileId(i as u32 + 1);
97 let cfg_options = CfgOptions::default(); 173 let edition = data.edition();
174 let env = data.env();
98 if path == "/lib.rs" || path == "/main.rs" { 175 if path == "/lib.rs" || path == "/main.rs" {
99 root_crate = Some(crate_graph.add_crate_root( 176 root_crate = Some(crate_graph.add_crate_root(
100 file_id, 177 file_id,
101 Edition2018, 178 edition,
102 None, 179 None,
103 cfg_options, 180 cfg_options,
104 Env::default(), 181 env,
105 Default::default(), 182 Default::default(),
106 Default::default(), 183 Default::default(),
107 )); 184 ));
@@ -109,10 +186,10 @@ impl MockAnalysis {
109 let crate_name = path.parent().unwrap().file_name().unwrap(); 186 let crate_name = path.parent().unwrap().file_name().unwrap();
110 let other_crate = crate_graph.add_crate_root( 187 let other_crate = crate_graph.add_crate_root(
111 file_id, 188 file_id,
112 Edition2018, 189 edition,
113 Some(CrateName::new(crate_name).unwrap()), 190 Some(CrateName::new(crate_name).unwrap()),
114 cfg_options, 191 cfg_options,
115 Env::default(), 192 env,
116 Default::default(), 193 Default::default(),
117 Default::default(), 194 Default::default(),
118 ); 195 );
@@ -122,7 +199,7 @@ impl MockAnalysis {
122 .unwrap(); 199 .unwrap();
123 } 200 }
124 } 201 }
125 change.add_file(source_root, file_id, path, Arc::new(contents)); 202 change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned()));
126 } 203 }
127 change.set_crate_graph(crate_graph); 204 change.set_crate_graph(crate_graph);
128 host.apply_change(change); 205 host.apply_change(change);
@@ -131,6 +208,10 @@ impl MockAnalysis {
131 pub fn analysis(self) -> Analysis { 208 pub fn analysis(self) -> Analysis {
132 self.analysis_host().analysis() 209 self.analysis_host().analysis()
133 } 210 }
211
212 fn next_id(&self) -> FileId {
213 FileId((self.files.len() + 1) as u32)
214 }
134} 215}
135 216
136/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 217/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 62ec8d85d..28c6349b1 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -128,7 +128,7 @@ fn rename_mod(
128 source_file_edits.extend(ref_edits); 128 source_file_edits.extend(ref_edits);
129 } 129 }
130 130
131 Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) 131 Some(SourceChange::from_edits(source_file_edits, file_system_edits))
132} 132}
133 133
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 171 ),
172 }); 172 });
173 173
174 Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) 174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits)))
175} 175}
176 176
177fn text_edit_from_self_param( 177fn text_edit_from_self_param(
@@ -234,7 +234,7 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 234 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 235 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 236
237 Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) 237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits)))
238} 238}
239 239
240fn rename_reference( 240fn rename_reference(
@@ -253,7 +253,7 @@ fn rename_reference(
253 return None; 253 return None;
254 } 254 }
255 255
256 Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) 256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit)))
257} 257}
258 258
259#[cfg(test)] 259#[cfg(test)]
@@ -642,7 +642,6 @@ mod tests {
642 RangeInfo { 642 RangeInfo {
643 range: 4..7, 643 range: 4..7,
644 info: SourceChange { 644 info: SourceChange {
645 label: "Rename",
646 source_file_edits: [ 645 source_file_edits: [
647 SourceFileEdit { 646 SourceFileEdit {
648 file_id: FileId( 647 file_id: FileId(
@@ -669,7 +668,6 @@ mod tests {
669 dst_path: "bar/foo2.rs", 668 dst_path: "bar/foo2.rs",
670 }, 669 },
671 ], 670 ],
672 cursor_position: None,
673 is_snippet: false, 671 is_snippet: false,
674 }, 672 },
675 }, 673 },
@@ -695,7 +693,6 @@ mod tests {
695 RangeInfo { 693 RangeInfo {
696 range: 4..7, 694 range: 4..7,
697 info: SourceChange { 695 info: SourceChange {
698 label: "Rename",
699 source_file_edits: [ 696 source_file_edits: [
700 SourceFileEdit { 697 SourceFileEdit {
701 file_id: FileId( 698 file_id: FileId(
@@ -722,7 +719,6 @@ mod tests {
722 dst_path: "foo2/mod.rs", 719 dst_path: "foo2/mod.rs",
723 }, 720 },
724 ], 721 ],
725 cursor_position: None,
726 is_snippet: false, 722 is_snippet: false,
727 }, 723 },
728 }, 724 },
@@ -779,7 +775,6 @@ mod tests {
779 RangeInfo { 775 RangeInfo {
780 range: 8..11, 776 range: 8..11,
781 info: SourceChange { 777 info: SourceChange {
782 label: "Rename",
783 source_file_edits: [ 778 source_file_edits: [
784 SourceFileEdit { 779 SourceFileEdit {
785 file_id: FileId( 780 file_id: FileId(
@@ -819,7 +814,6 @@ mod tests {
819 dst_path: "bar/foo2.rs", 814 dst_path: "bar/foo2.rs",
820 }, 815 },
821 ], 816 ],
822 cursor_position: None,
823 is_snippet: false, 817 is_snippet: false,
824 }, 818 },
825 }, 819 },
@@ -986,8 +980,8 @@ mod tests {
986 if let Some(change) = source_change { 980 if let Some(change) = source_change {
987 for edit in change.info.source_file_edits { 981 for edit in change.info.source_file_edits {
988 file_id = Some(edit.file_id); 982 file_id = Some(edit.file_id);
989 for indel in edit.edit.as_indels() { 983 for indel in edit.edit.into_iter() {
990 text_edit_builder.replace(indel.delete, indel.insert.clone()); 984 text_edit_builder.replace(indel.delete, indel.insert);
991 } 985 }
992 } 986 }
993 } 987 }
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 131b8f307..6e7e47199 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{AsAssocItem, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -10,12 +10,14 @@ use ra_syntax::{
10 10
11use crate::FileId; 11use crate::FileId;
12use ast::DocCommentsOwner; 12use ast::DocCommentsOwner;
13use ra_cfg::CfgExpr;
13use std::fmt::Display; 14use std::fmt::Display;
14 15
15#[derive(Debug)] 16#[derive(Debug)]
16pub struct Runnable { 17pub struct Runnable {
17 pub range: TextRange, 18 pub range: TextRange,
18 pub kind: RunnableKind, 19 pub kind: RunnableKind,
20 pub cfg_exprs: Vec<CfgExpr>,
19} 21}
20 22
21#[derive(Debug)] 23#[derive(Debug)]
@@ -45,29 +47,33 @@ pub enum RunnableKind {
45pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 47pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
46 let sema = Semantics::new(db); 48 let sema = Semantics::new(db);
47 let source_file = sema.parse(file_id); 49 let source_file = sema.parse(file_id);
48 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() 50 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
49} 51}
50 52
51fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 53fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> {
52 match_ast! { 54 match_ast! {
53 match item { 55 match item {
54 ast::FnDef(it) => runnable_fn(sema, it), 56 ast::FnDef(it) => runnable_fn(sema, it, file_id),
55 ast::Module(it) => runnable_mod(sema, it), 57 ast::Module(it) => runnable_mod(sema, it, file_id),
56 _ => None, 58 _ => None,
57 } 59 }
58 } 60 }
59} 61}
60 62
61fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { 63fn runnable_fn(
64 sema: &Semantics<RootDatabase>,
65 fn_def: ast::FnDef,
66 file_id: FileId,
67) -> Option<Runnable> {
62 let name_string = fn_def.name()?.text().to_string(); 68 let name_string = fn_def.name()?.text().to_string();
63 69
64 let kind = if name_string == "main" { 70 let kind = if name_string == "main" {
65 RunnableKind::Bin 71 RunnableKind::Bin
66 } else { 72 } else {
67 let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { 73 let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
68 let def = sema.to_def(&fn_def)?; 74 Some(module) => {
69 let impl_trait_name = 75 let def = sema.to_def(&fn_def)?;
70 def.as_assoc_item(sema.db).and_then(|assoc_item| { 76 let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| {
71 match assoc_item.container(sema.db) { 77 match assoc_item.container(sema.db) {
72 hir::AssocItemContainer::Trait(trait_item) => { 78 hir::AssocItemContainer::Trait(trait_item) => {
73 Some(trait_item.name(sema.db).to_string()) 79 Some(trait_item.name(sema.db).to_string())
@@ -79,25 +85,25 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
79 } 85 }
80 }); 86 });
81 87
82 let path_iter = module 88 let path_iter = module
83 .path_to_root(sema.db) 89 .path_to_root(sema.db)
84 .into_iter() 90 .into_iter()
85 .rev() 91 .rev()
86 .filter_map(|it| it.name(sema.db)) 92 .filter_map(|it| it.name(sema.db))
87 .map(|name| name.to_string()); 93 .map(|name| name.to_string());
88 94
89 let path = if let Some(impl_trait_name) = impl_trait_name { 95 let path = if let Some(impl_trait_name) = impl_trait_name {
90 path_iter 96 path_iter
91 .chain(std::iter::once(impl_trait_name)) 97 .chain(std::iter::once(impl_trait_name))
92 .chain(std::iter::once(name_string)) 98 .chain(std::iter::once(name_string))
93 .join("::") 99 .join("::")
94 } else { 100 } else {
95 path_iter.chain(std::iter::once(name_string)).join("::") 101 path_iter.chain(std::iter::once(name_string)).join("::")
96 }; 102 };
97 103
98 TestId::Path(path) 104 TestId::Path(path)
99 } else { 105 }
100 TestId::Name(name_string) 106 None => TestId::Name(name_string),
101 }; 107 };
102 108
103 if has_test_related_attribute(&fn_def) { 109 if has_test_related_attribute(&fn_def) {
@@ -111,7 +117,12 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
111 return None; 117 return None;
112 } 118 }
113 }; 119 };
114 Some(Runnable { range: fn_def.syntax().text_range(), kind }) 120
121 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
122 let cfg_exprs =
123 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
124
125 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
115} 126}
116 127
117#[derive(Debug)] 128#[derive(Debug)]
@@ -147,7 +158,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool {
147 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) 158 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
148} 159}
149 160
150fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { 161fn runnable_mod(
162 sema: &Semantics<RootDatabase>,
163 module: ast::Module,
164 file_id: FileId,
165) -> Option<Runnable> {
151 let has_test_function = module 166 let has_test_function = module
152 .item_list()? 167 .item_list()?
153 .items() 168 .items()
@@ -160,11 +175,20 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R
160 return None; 175 return None;
161 } 176 }
162 let range = module.syntax().text_range(); 177 let range = module.syntax().text_range();
163 let module = sema.to_def(&module)?; 178 let module_def = sema.to_def(&module)?;
164 179
165 let path = 180 let path = module_def
166 module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); 181 .path_to_root(sema.db)
167 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 182 .into_iter()
183 .rev()
184 .filter_map(|it| it.name(sema.db))
185 .join("::");
186
187 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
188 let cfg_exprs =
189 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
190
191 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
168} 192}
169 193
170#[cfg(test)] 194#[cfg(test)]
@@ -196,6 +220,7 @@ mod tests {
196 Runnable { 220 Runnable {
197 range: 1..21, 221 range: 1..21,
198 kind: Bin, 222 kind: Bin,
223 cfg_exprs: [],
199 }, 224 },
200 Runnable { 225 Runnable {
201 range: 22..46, 226 range: 22..46,
@@ -207,6 +232,7 @@ mod tests {
207 ignore: false, 232 ignore: false,
208 }, 233 },
209 }, 234 },
235 cfg_exprs: [],
210 }, 236 },
211 Runnable { 237 Runnable {
212 range: 47..81, 238 range: 47..81,
@@ -218,6 +244,7 @@ mod tests {
218 ignore: true, 244 ignore: true,
219 }, 245 },
220 }, 246 },
247 cfg_exprs: [],
221 }, 248 },
222 ] 249 ]
223 "### 250 "###
@@ -245,6 +272,7 @@ mod tests {
245 Runnable { 272 Runnable {
246 range: 1..21, 273 range: 1..21,
247 kind: Bin, 274 kind: Bin,
275 cfg_exprs: [],
248 }, 276 },
249 Runnable { 277 Runnable {
250 range: 22..64, 278 range: 22..64,
@@ -253,6 +281,7 @@ mod tests {
253 "foo", 281 "foo",
254 ), 282 ),
255 }, 283 },
284 cfg_exprs: [],
256 }, 285 },
257 ] 286 ]
258 "### 287 "###
@@ -283,6 +312,7 @@ mod tests {
283 Runnable { 312 Runnable {
284 range: 1..21, 313 range: 1..21,
285 kind: Bin, 314 kind: Bin,
315 cfg_exprs: [],
286 }, 316 },
287 Runnable { 317 Runnable {
288 range: 51..105, 318 range: 51..105,
@@ -291,6 +321,7 @@ mod tests {
291 "Data::foo", 321 "Data::foo",
292 ), 322 ),
293 }, 323 },
324 cfg_exprs: [],
294 }, 325 },
295 ] 326 ]
296 "### 327 "###
@@ -318,6 +349,7 @@ mod tests {
318 kind: TestMod { 349 kind: TestMod {
319 path: "test_mod", 350 path: "test_mod",
320 }, 351 },
352 cfg_exprs: [],
321 }, 353 },
322 Runnable { 354 Runnable {
323 range: 28..57, 355 range: 28..57,
@@ -329,6 +361,7 @@ mod tests {
329 ignore: false, 361 ignore: false,
330 }, 362 },
331 }, 363 },
364 cfg_exprs: [],
332 }, 365 },
333 ] 366 ]
334 "### 367 "###
@@ -358,6 +391,7 @@ mod tests {
358 kind: TestMod { 391 kind: TestMod {
359 path: "foo::test_mod", 392 path: "foo::test_mod",
360 }, 393 },
394 cfg_exprs: [],
361 }, 395 },
362 Runnable { 396 Runnable {
363 range: 46..79, 397 range: 46..79,
@@ -369,6 +403,7 @@ mod tests {
369 ignore: false, 403 ignore: false,
370 }, 404 },
371 }, 405 },
406 cfg_exprs: [],
372 }, 407 },
373 ] 408 ]
374 "### 409 "###
@@ -400,6 +435,7 @@ mod tests {
400 kind: TestMod { 435 kind: TestMod {
401 path: "foo::bar::test_mod", 436 path: "foo::bar::test_mod",
402 }, 437 },
438 cfg_exprs: [],
403 }, 439 },
404 Runnable { 440 Runnable {
405 range: 68..105, 441 range: 68..105,
@@ -411,6 +447,89 @@ mod tests {
411 ignore: false, 447 ignore: false,
412 }, 448 },
413 }, 449 },
450 cfg_exprs: [],
451 },
452 ]
453 "###
454 );
455 }
456
457 #[test]
458 fn test_runnables_with_feature() {
459 let (analysis, pos) = analysis_and_position(
460 r#"
461 //- /lib.rs crate:foo cfg:feature=foo
462 <|> //empty
463 #[test]
464 #[cfg(feature = "foo")]
465 fn test_foo1() {}
466 "#,
467 );
468 let runnables = analysis.runnables(pos.file_id).unwrap();
469 assert_debug_snapshot!(&runnables,
470 @r###"
471 [
472 Runnable {
473 range: 1..58,
474 kind: Test {
475 test_id: Path(
476 "test_foo1",
477 ),
478 attr: TestAttr {
479 ignore: false,
480 },
481 },
482 cfg_exprs: [
483 KeyValue {
484 key: "feature",
485 value: "foo",
486 },
487 ],
488 },
489 ]
490 "###
491 );
492 }
493
494 #[test]
495 fn test_runnables_with_features() {
496 let (analysis, pos) = analysis_and_position(
497 r#"
498 //- /lib.rs crate:foo cfg:feature=foo,feature=bar
499 <|> //empty
500 #[test]
501 #[cfg(all(feature = "foo", feature = "bar"))]
502 fn test_foo1() {}
503 "#,
504 );
505 let runnables = analysis.runnables(pos.file_id).unwrap();
506 assert_debug_snapshot!(&runnables,
507 @r###"
508 [
509 Runnable {
510 range: 1..80,
511 kind: Test {
512 test_id: Path(
513 "test_foo1",
514 ),
515 attr: TestAttr {
516 ignore: false,
517 },
518 },
519 cfg_exprs: [
520 All(
521 [
522 KeyValue {
523 key: "feature",
524 value: "foo",
525 },
526 KeyValue {
527 key: "feature",
528 value: "bar",
529 },
530 ],
531 ),
532 ],
414 }, 533 },
415 ] 534 ]
416 "### 535 "###
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index ea026d7a0..68fc589bc 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 752b487e8..326744361 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 635fe5cf9..352e35095 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
@@ -27,19 +28,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
27.keyword.unsafe { color: #BC8383; font-weight: bold; } 28.keyword.unsafe { color: #BC8383; font-weight: bold; }
28.control { font-style: italic; } 29.control { font-style: italic; }
29</style> 30</style>
30<pre><code><span class="attribute">#[derive(Clone, Debug)]</span> 31<pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span>
31<span class="keyword">struct</span> <span class="struct declaration">Foo</span> { 32<span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
32 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>, 33 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>,
33 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, 34 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>,
34} 35}
35 36
36<span class="keyword">trait</span> <span class="trait declaration">Bar</span> { 37<span class="keyword">trait</span> <span class="trait declaration">Bar</span> {
37 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -&gt; <span class="builtin_type">i32</span>; 38 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span>;
38} 39}
39 40
40<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { 41<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> {
41 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -&gt; <span class="builtin_type">i32</span> { 42 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span> {
42 <span class="keyword">self</span>.<span class="field">x</span> 43 <span class="self_keyword">self</span>.<span class="field">x</span>
43 } 44 }
44} 45}
45 46
@@ -64,7 +65,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
64 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); 65 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
65 66
66 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>(); 67 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>();
67 <span class="keyword control">if</span> <span class="keyword">true</span> { 68 <span class="keyword control">if</span> <span class="bool_literal">true</span> {
68 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; 69 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>;
69 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); 70 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
70 } 71 }
@@ -91,7 +92,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
91<span class="keyword">use</span> <span class="enum">Option</span>::*; 92<span class="keyword">use</span> <span class="enum">Option</span>::*;
92 93
93<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; { 94<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; {
94 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; { 95 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; {
95 <span class="keyword control">match</span> <span class="variable">other</span> { 96 <span class="keyword control">match</span> <span class="variable">other</span> {
96 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(), 97 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(),
97 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>, 98 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>,
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 11e1f3e44..2a0294f71 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1873d1d0d..130d3b4c3 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -196,10 +196,10 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
196 ) -> Option<Match> { 196 ) -> Option<Match> {
197 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; 197 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
198 198
199 let mut pattern_fields = 199 let mut pattern_fields: Vec<RecordField> =
200 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); 200 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
201 let mut code_fields = 201 let mut code_fields: Vec<RecordField> =
202 code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); 202 code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
203 203
204 if pattern_fields.len() != code_fields.len() { 204 if pattern_fields.len() != code_fields.len() {
205 return None; 205 return None;
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index be57eeb0a..8a995d779 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -361,7 +361,9 @@ fn highlight_element(
361 } 361 }
362 362
363 // Highlight references like the definitions they resolve to 363 // Highlight references like the definitions they resolve to
364 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, 364 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
365 Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute
366 }
365 NAME_REF => { 367 NAME_REF => {
366 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 368 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
367 match classify_name_ref(sema, &name_ref) { 369 match classify_name_ref(sema, &name_ref) {
@@ -411,6 +413,8 @@ fn highlight_element(
411 | T![in] => h | HighlightModifier::ControlFlow, 413 | T![in] => h | HighlightModifier::ControlFlow,
412 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, 414 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow,
413 T![unsafe] => h | HighlightModifier::Unsafe, 415 T![unsafe] => h | HighlightModifier::Unsafe,
416 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
417 T![self] => HighlightTag::SelfKeyword.into(),
414 _ => h, 418 _ => h,
415 } 419 }
416 } 420 }
@@ -478,23 +482,31 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
478} 482}
479 483
480fn highlight_name_by_syntax(name: ast::Name) -> Highlight { 484fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
481 let default = HighlightTag::Function.into(); 485 let default = HighlightTag::UnresolvedReference;
482 486
483 let parent = match name.syntax().parent() { 487 let parent = match name.syntax().parent() {
484 Some(it) => it, 488 Some(it) => it,
485 _ => return default, 489 _ => return default.into(),
486 }; 490 };
487 491
488 match parent.kind() { 492 let tag = match parent.kind() {
489 STRUCT_DEF => HighlightTag::Struct.into(), 493 STRUCT_DEF => HighlightTag::Struct,
490 ENUM_DEF => HighlightTag::Enum.into(), 494 ENUM_DEF => HighlightTag::Enum,
491 UNION_DEF => HighlightTag::Union.into(), 495 UNION_DEF => HighlightTag::Union,
492 TRAIT_DEF => HighlightTag::Trait.into(), 496 TRAIT_DEF => HighlightTag::Trait,
493 TYPE_ALIAS_DEF => HighlightTag::TypeAlias.into(), 497 TYPE_ALIAS_DEF => HighlightTag::TypeAlias,
494 TYPE_PARAM => HighlightTag::TypeParam.into(), 498 TYPE_PARAM => HighlightTag::TypeParam,
495 RECORD_FIELD_DEF => HighlightTag::Field.into(), 499 RECORD_FIELD_DEF => HighlightTag::Field,
500 MODULE => HighlightTag::Module,
501 FN_DEF => HighlightTag::Function,
502 CONST_DEF => HighlightTag::Constant,
503 STATIC_DEF => HighlightTag::Static,
504 ENUM_VARIANT => HighlightTag::EnumVariant,
505 BIND_PAT => HighlightTag::Local,
496 _ => default, 506 _ => default,
497 } 507 };
508
509 tag.into()
498} 510}
499 511
500fn highlight_injection( 512fn highlight_injection(
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index ff0eeeb52..edfe61f39 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -76,6 +76,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
76.type_param { color: #DFAF8F; } 76.type_param { color: #DFAF8F; }
77.attribute { color: #94BFF3; } 77.attribute { color: #94BFF3; }
78.numeric_literal { color: #BFEBBF; } 78.numeric_literal { color: #BFEBBF; }
79.bool_literal { color: #BFE6EB; }
79.macro { color: #94BFF3; } 80.macro { color: #94BFF3; }
80.module { color: #AFD8AF; } 81.module { color: #AFD8AF; }
81.variable { color: #DCDCCC; } 82.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index be1a0f12b..46c718c91 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -15,6 +15,7 @@ pub struct HighlightModifiers(u32);
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub enum HighlightTag { 16pub enum HighlightTag {
17 Attribute, 17 Attribute,
18 BoolLiteral,
18 BuiltinType, 19 BuiltinType,
19 ByteLiteral, 20 ByteLiteral,
20 CharLiteral, 21 CharLiteral,
@@ -29,6 +30,7 @@ pub enum HighlightTag {
29 Macro, 30 Macro,
30 Module, 31 Module,
31 NumericLiteral, 32 NumericLiteral,
33 SelfKeyword,
32 SelfType, 34 SelfType,
33 Static, 35 Static,
34 StringLiteral, 36 StringLiteral,
@@ -45,8 +47,10 @@ pub enum HighlightTag {
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 47#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
46#[repr(u8)] 48#[repr(u8)]
47pub enum HighlightModifier { 49pub enum HighlightModifier {
50 /// Used to differentiate individual elements within attributes.
51 Attribute = 0,
48 /// Used with keywords like `if` and `break`. 52 /// Used with keywords like `if` and `break`.
49 ControlFlow = 0, 53 ControlFlow,
50 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 54 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
51 /// not. 55 /// not.
52 Definition, 56 Definition,
@@ -58,6 +62,7 @@ impl HighlightTag {
58 fn as_str(self) -> &'static str { 62 fn as_str(self) -> &'static str {
59 match self { 63 match self {
60 HighlightTag::Attribute => "attribute", 64 HighlightTag::Attribute => "attribute",
65 HighlightTag::BoolLiteral => "bool_literal",
61 HighlightTag::BuiltinType => "builtin_type", 66 HighlightTag::BuiltinType => "builtin_type",
62 HighlightTag::ByteLiteral => "byte_literal", 67 HighlightTag::ByteLiteral => "byte_literal",
63 HighlightTag::CharLiteral => "char_literal", 68 HighlightTag::CharLiteral => "char_literal",
@@ -72,6 +77,7 @@ impl HighlightTag {
72 HighlightTag::Macro => "macro", 77 HighlightTag::Macro => "macro",
73 HighlightTag::Module => "module", 78 HighlightTag::Module => "module",
74 HighlightTag::NumericLiteral => "numeric_literal", 79 HighlightTag::NumericLiteral => "numeric_literal",
80 HighlightTag::SelfKeyword => "self_keyword",
75 HighlightTag::SelfType => "self_type", 81 HighlightTag::SelfType => "self_type",
76 HighlightTag::Static => "static", 82 HighlightTag::Static => "static",
77 HighlightTag::StringLiteral => "string_literal", 83 HighlightTag::StringLiteral => "string_literal",
@@ -95,6 +101,7 @@ impl fmt::Display for HighlightTag {
95 101
96impl HighlightModifier { 102impl HighlightModifier {
97 const ALL: &'static [HighlightModifier] = &[ 103 const ALL: &'static [HighlightModifier] = &[
104 HighlightModifier::Attribute,
98 HighlightModifier::ControlFlow, 105 HighlightModifier::ControlFlow,
99 HighlightModifier::Definition, 106 HighlightModifier::Definition,
100 HighlightModifier::Mutable, 107 HighlightModifier::Mutable,
@@ -103,6 +110,7 @@ impl HighlightModifier {
103 110
104 fn as_str(self) -> &'static str { 111 fn as_str(self) -> &'static str {
105 match self { 112 match self {
113 HighlightModifier::Attribute => "attribute",
106 HighlightModifier::ControlFlow => "control", 114 HighlightModifier::ControlFlow => "control",
107 HighlightModifier::Definition => "declaration", 115 HighlightModifier::Definition => "declaration",
108 HighlightModifier::Mutable => "mutable", 116 HighlightModifier::Mutable => "mutable",
diff --git a/crates/ra_ide/src/test_utils.rs b/crates/ra_ide/src/test_utils.rs
deleted file mode 100644
index 48c8fd1f4..000000000
--- a/crates/ra_ide/src/test_utils.rs
+++ /dev/null
@@ -1,25 +0,0 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{SourceFile, TextSize};
4use ra_text_edit::TextEdit;
5
6pub use test_utils::*;
7
8pub fn check_action<F: Fn(&SourceFile, TextSize) -> Option<TextEdit>>(
9 before: &str,
10 after: &str,
11 f: F,
12) {
13 let (before_cursor_pos, before) = extract_offset(before);
14 let file = SourceFile::parse(&before).ok().unwrap();
15 let result = f(&file, before_cursor_pos).expect("code action is not applicable");
16 let actual = {
17 let mut actual = before.to_string();
18 result.apply(&mut actual);
19 actual
20 };
21 let actual_cursor_pos =
22 result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit");
23 let actual = add_cursor(&actual, actual_cursor_pos);
24 assert_eq_text!(after, &actual);
25}
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 6f04f0be4..39bb3b357 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -17,7 +17,7 @@ mod on_enter;
17 17
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::{source_change::SingleFileChange, RootDatabase}; 20use ra_ide_db::RootDatabase;
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
@@ -40,15 +40,11 @@ pub(crate) fn on_char_typed(
40 assert!(TRIGGER_CHARS.contains(char_typed)); 40 assert!(TRIGGER_CHARS.contains(char_typed));
41 let file = &db.parse(position.file_id).tree(); 41 let file = &db.parse(position.file_id).tree();
42 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 42 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
43 let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?; 43 let text_edit = on_char_typed_inner(file, position.offset, char_typed)?;
44 Some(single_file_change.into_source_change(position.file_id)) 44 Some(SourceChange::source_file_edit_from(position.file_id, text_edit))
45} 45}
46 46
47fn on_char_typed_inner( 47fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
48 file: &SourceFile,
49 offset: TextSize,
50 char_typed: char,
51) -> Option<SingleFileChange> {
52 assert!(TRIGGER_CHARS.contains(char_typed)); 48 assert!(TRIGGER_CHARS.contains(char_typed));
53 match char_typed { 49 match char_typed {
54 '.' => on_dot_typed(file, offset), 50 '.' => on_dot_typed(file, offset),
@@ -61,7 +57,7 @@ fn on_char_typed_inner(
61/// Returns an edit which should be applied after `=` was typed. Primarily, 57/// Returns an edit which should be applied after `=` was typed. Primarily,
62/// this works when adding `let =`. 58/// this works when adding `let =`.
63// FIXME: use a snippet completion instead of this hack here. 59// FIXME: use a snippet completion instead of this hack here.
64fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 60fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
65 assert_eq!(file.syntax().text().char_at(offset), Some('=')); 61 assert_eq!(file.syntax().text().char_at(offset), Some('='));
66 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; 62 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
67 if let_stmt.semicolon_token().is_some() { 63 if let_stmt.semicolon_token().is_some() {
@@ -79,15 +75,11 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
79 return None; 75 return None;
80 } 76 }
81 let offset = let_stmt.syntax().text_range().end(); 77 let offset = let_stmt.syntax().text_range().end();
82 Some(SingleFileChange { 78 Some(TextEdit::insert(offset, ";".to_string()))
83 label: "add semicolon".to_string(),
84 edit: TextEdit::insert(offset, ";".to_string()),
85 cursor_position: None,
86 })
87} 79}
88 80
89/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. 81/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
90fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 82fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
91 assert_eq!(file.syntax().text().char_at(offset), Some('.')); 83 assert_eq!(file.syntax().text().char_at(offset), Some('.'));
92 let whitespace = 84 let whitespace =
93 file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; 85 file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
@@ -108,15 +100,11 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
108 return None; 100 return None;
109 } 101 }
110 102
111 Some(SingleFileChange { 103 Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
112 label: "reindent dot".to_string(),
113 edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent),
114 cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')),
115 })
116} 104}
117 105
118/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` 106/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
119fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 107fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
120 let file_text = file.syntax().text(); 108 let file_text = file.syntax().text();
121 assert_eq!(file_text.char_at(offset), Some('>')); 109 assert_eq!(file_text.char_at(offset), Some('>'));
122 let after_arrow = offset + TextSize::of('>'); 110 let after_arrow = offset + TextSize::of('>');
@@ -127,11 +115,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang
127 return None; 115 return None;
128 } 116 }
129 117
130 Some(SingleFileChange { 118 Some(TextEdit::insert(after_arrow, " ".to_string()))
131 label: "add space after return type".to_string(),
132 edit: TextEdit::insert(after_arrow, " ".to_string()),
133 cursor_position: Some(after_arrow),
134 })
135} 119}
136 120
137#[cfg(test)] 121#[cfg(test)]
@@ -140,29 +124,23 @@ mod tests {
140 124
141 use super::*; 125 use super::*;
142 126
143 fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { 127 fn do_type_char(char_typed: char, before: &str) -> Option<String> {
144 let (offset, before) = extract_offset(before); 128 let (offset, before) = extract_offset(before);
145 let edit = TextEdit::insert(offset, char_typed.to_string()); 129 let edit = TextEdit::insert(offset, char_typed.to_string());
146 let mut before = before.to_string(); 130 let mut before = before.to_string();
147 edit.apply(&mut before); 131 edit.apply(&mut before);
148 let parse = SourceFile::parse(&before); 132 let parse = SourceFile::parse(&before);
149 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { 133 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| {
150 it.edit.apply(&mut before); 134 it.apply(&mut before);
151 (before.to_string(), it) 135 before.to_string()
152 }) 136 })
153 } 137 }
154 138
155 fn type_char(char_typed: char, before: &str, after: &str) { 139 fn type_char(char_typed: char, before: &str, after: &str) {
156 let (actual, file_change) = do_type_char(char_typed, before) 140 let actual = do_type_char(char_typed, before)
157 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 141 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
158 142
159 if after.contains("<|>") { 143 assert_eq_text!(after, &actual);
160 let (offset, after) = extract_offset(after);
161 assert_eq_text!(&after, &actual);
162 assert_eq!(file_change.cursor_position, Some(offset))
163 } else {
164 assert_eq_text!(after, &actual);
165 }
166 } 144 }
167 145
168 fn type_char_noop(char_typed: char, before: &str) { 146 fn type_char_noop(char_typed: char, before: &str) {
@@ -350,6 +328,6 @@ fn foo() {
350 328
351 #[test] 329 #[test]
352 fn adds_space_after_return_type() { 330 fn adds_space_after_return_type() {
353 type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }") 331 type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }")
354 } 332 }
355} 333}
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index 78a40cc94..a40d8af9c 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -11,9 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::TextEdit; 12use ra_text_edit::TextEdit;
13 13
14use crate::{SourceChange, SourceFileEdit}; 14pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
15
16pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
17 let parse = db.parse(position.file_id); 15 let parse = db.parse(position.file_id);
18 let file = parse.tree(); 16 let file = parse.tree();
19 let comment = file 17 let comment = file
@@ -38,17 +36,10 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
38 } 36 }
39 37
40 let indent = node_indent(&file, comment.syntax())?; 38 let indent = node_indent(&file, comment.syntax())?;
41 let inserted = format!("\n{}{} ", indent, prefix); 39 let inserted = format!("\n{}{} $0", indent, prefix);
42 let cursor_position = position.offset + TextSize::of(&inserted);
43 let edit = TextEdit::insert(position.offset, inserted); 40 let edit = TextEdit::insert(position.offset, inserted);
44 41
45 Some( 42 Some(edit)
46 SourceChange::source_file_edit(
47 "On enter",
48 SourceFileEdit { edit, file_id: position.file_id },
49 )
50 .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
51 )
52} 43}
53 44
54fn followed_by_comment(comment: &ast::Comment) -> bool { 45fn followed_by_comment(comment: &ast::Comment) -> bool {
@@ -84,7 +75,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
84 75
85#[cfg(test)] 76#[cfg(test)]
86mod tests { 77mod tests {
87 use test_utils::{add_cursor, assert_eq_text, extract_offset}; 78 use test_utils::{assert_eq_text, extract_offset};
88 79
89 use crate::mock_analysis::single_file; 80 use crate::mock_analysis::single_file;
90 81
@@ -95,10 +86,8 @@ mod tests {
95 let (analysis, file_id) = single_file(&before); 86 let (analysis, file_id) = single_file(&before);
96 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; 87 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;
97 88
98 assert_eq!(result.source_file_edits.len(), 1);
99 let mut actual = before.to_string(); 89 let mut actual = before.to_string();
100 result.source_file_edits[0].edit.apply(&mut actual); 90 result.apply(&mut actual);
101 let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
102 Some(actual) 91 Some(actual)
103 } 92 }
104 93
@@ -121,7 +110,7 @@ fn foo() {
121", 110",
122 r" 111 r"
123/// Some docs 112/// Some docs
124/// <|> 113/// $0
125fn foo() { 114fn foo() {
126} 115}
127", 116",
@@ -137,7 +126,7 @@ impl S {
137 r" 126 r"
138impl S { 127impl S {
139 /// Some 128 /// Some
140 /// <|> docs. 129 /// $0 docs.
141 fn foo() {} 130 fn foo() {}
142} 131}
143", 132",
@@ -151,7 +140,7 @@ fn foo() {
151", 140",
152 r" 141 r"
153/// 142///
154/// <|> Some docs 143/// $0 Some docs
155fn foo() { 144fn foo() {
156} 145}
157", 146",
@@ -175,7 +164,7 @@ fn main() {
175 r" 164 r"
176fn main() { 165fn main() {
177 // Fix 166 // Fix
178 // <|> me 167 // $0 me
179 let x = 1 + 1; 168 let x = 1 + 1;
180} 169}
181", 170",
@@ -195,7 +184,7 @@ fn main() {
195 r" 184 r"
196fn main() { 185fn main() {
197 // Fix 186 // Fix
198 // <|> 187 // $0
199 // me 188 // me
200 let x = 1 + 1; 189 let x = 1 + 1;
201} 190}
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 4f37954bf..1b74e6558 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -3,7 +3,6 @@
3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. 3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
4 4
5pub mod line_index; 5pub mod line_index;
6pub mod line_index_utils;
7pub mod symbol_index; 6pub mod symbol_index;
8pub mod change; 7pub mod change;
9pub mod defs; 8pub mod defs;
diff --git a/crates/ra_ide_db/src/line_index_utils.rs b/crates/ra_ide_db/src/line_index_utils.rs
deleted file mode 100644
index 7fa6fc448..000000000
--- a/crates/ra_ide_db/src/line_index_utils.rs
+++ /dev/null
@@ -1,302 +0,0 @@
1//! Code actions can specify desirable final position of the cursor.
2//!
3//! The position is specified as a `TextSize` in the final file. We need to send
4//! it in `(Line, Column)` coordinate though. However, we only have a LineIndex
5//! for a file pre-edit!
6//!
7//! Code in this module applies this "to (Line, Column) after edit"
8//! transformation.
9
10use std::convert::TryInto;
11
12use ra_syntax::{TextRange, TextSize};
13use ra_text_edit::{Indel, TextEdit};
14
15use crate::line_index::{LineCol, LineIndex, Utf16Char};
16
17pub fn translate_offset_with_edit(
18 line_index: &LineIndex,
19 offset: TextSize,
20 text_edit: &TextEdit,
21) -> LineCol {
22 let mut state = Edits::from_text_edit(&text_edit);
23
24 let mut res = RunningLineCol::new();
25
26 macro_rules! test_step {
27 ($x:ident) => {
28 match &$x {
29 Step::Newline(n) => {
30 if offset < *n {
31 return res.to_line_col(offset);
32 } else {
33 res.add_line(*n);
34 }
35 }
36 Step::Utf16Char(x) => {
37 if offset < x.end() {
38 // if the offset is inside a multibyte char it's invalid
39 // clamp it to the start of the char
40 let clamp = offset.min(x.start());
41 return res.to_line_col(clamp);
42 } else {
43 res.adjust_col(*x);
44 }
45 }
46 }
47 };
48 }
49
50 for orig_step in LineIndexStepIter::from(line_index) {
51 loop {
52 let translated_step = state.translate_step(&orig_step);
53 match state.next_steps(&translated_step) {
54 NextSteps::Use => {
55 test_step!(translated_step);
56 break;
57 }
58 NextSteps::ReplaceMany(ns) => {
59 for n in ns {
60 test_step!(n);
61 }
62 break;
63 }
64 NextSteps::AddMany(ns) => {
65 for n in ns {
66 test_step!(n);
67 }
68 }
69 }
70 }
71 }
72
73 loop {
74 match state.next_inserted_steps() {
75 None => break,
76 Some(ns) => {
77 for n in ns {
78 test_step!(n);
79 }
80 }
81 }
82 }
83
84 res.to_line_col(offset)
85}
86
87#[derive(Debug, Clone)]
88enum Step {
89 Newline(TextSize),
90 Utf16Char(TextRange),
91}
92
93#[derive(Debug)]
94struct LineIndexStepIter<'a> {
95 line_index: &'a LineIndex,
96 next_newline_idx: usize,
97 utf16_chars: Option<(TextSize, std::slice::Iter<'a, Utf16Char>)>,
98}
99
100impl LineIndexStepIter<'_> {
101 fn from(line_index: &LineIndex) -> LineIndexStepIter {
102 let mut x = LineIndexStepIter { line_index, next_newline_idx: 0, utf16_chars: None };
103 // skip first newline since it's not real
104 x.next();
105 x
106 }
107}
108
109impl Iterator for LineIndexStepIter<'_> {
110 type Item = Step;
111 fn next(&mut self) -> Option<Step> {
112 self.utf16_chars
113 .as_mut()
114 .and_then(|(newline, x)| {
115 let x = x.next()?;
116 Some(Step::Utf16Char(TextRange::new(*newline + x.start, *newline + x.end)))
117 })
118 .or_else(|| {
119 let next_newline = *self.line_index.newlines.get(self.next_newline_idx)?;
120 self.utf16_chars = self
121 .line_index
122 .utf16_lines
123 .get(&(self.next_newline_idx as u32))
124 .map(|x| (next_newline, x.iter()));
125 self.next_newline_idx += 1;
126 Some(Step::Newline(next_newline))
127 })
128 }
129}
130
131#[derive(Debug)]
132struct OffsetStepIter<'a> {
133 text: &'a str,
134 offset: TextSize,
135}
136
137impl Iterator for OffsetStepIter<'_> {
138 type Item = Step;
139 fn next(&mut self) -> Option<Step> {
140 let (next, next_offset) = self
141 .text
142 .char_indices()
143 .filter_map(|(i, c)| {
144 let i: TextSize = i.try_into().unwrap();
145 let char_len = TextSize::of(c);
146 if c == '\n' {
147 let next_offset = self.offset + i + char_len;
148 let next = Step::Newline(next_offset);
149 Some((next, next_offset))
150 } else {
151 if !c.is_ascii() {
152 let start = self.offset + i;
153 let end = start + char_len;
154 let next = Step::Utf16Char(TextRange::new(start, end));
155 let next_offset = end;
156 Some((next, next_offset))
157 } else {
158 None
159 }
160 }
161 })
162 .next()?;
163 let next_idx: usize = (next_offset - self.offset).into();
164 self.text = &self.text[next_idx..];
165 self.offset = next_offset;
166 Some(next)
167 }
168}
169
170#[derive(Debug)]
171enum NextSteps<'a> {
172 Use,
173 ReplaceMany(OffsetStepIter<'a>),
174 AddMany(OffsetStepIter<'a>),
175}
176
177#[derive(Debug)]
178struct TranslatedEdit<'a> {
179 delete: TextRange,
180 insert: &'a str,
181 diff: i64,
182}
183
184struct Edits<'a> {
185 edits: &'a [Indel],
186 current: Option<TranslatedEdit<'a>>,
187 acc_diff: i64,
188}
189
190impl<'a> Edits<'a> {
191 fn from_text_edit(text_edit: &'a TextEdit) -> Edits<'a> {
192 let mut x = Edits { edits: text_edit.as_indels(), current: None, acc_diff: 0 };
193 x.advance_edit();
194 x
195 }
196 fn advance_edit(&mut self) {
197 self.acc_diff += self.current.as_ref().map_or(0, |x| x.diff);
198 match self.edits.split_first() {
199 Some((next, rest)) => {
200 let delete = self.translate_range(next.delete);
201 let diff = next.insert.len() as i64 - usize::from(next.delete.len()) as i64;
202 self.current = Some(TranslatedEdit { delete, insert: &next.insert, diff });
203 self.edits = rest;
204 }
205 None => {
206 self.current = None;
207 }
208 }
209 }
210
211 fn next_inserted_steps(&mut self) -> Option<OffsetStepIter<'a>> {
212 let cur = self.current.as_ref()?;
213 let res = Some(OffsetStepIter { offset: cur.delete.start(), text: &cur.insert });
214 self.advance_edit();
215 res
216 }
217
218 fn next_steps(&mut self, step: &Step) -> NextSteps {
219 let step_pos = match *step {
220 Step::Newline(n) => n,
221 Step::Utf16Char(r) => r.end(),
222 };
223 match &mut self.current {
224 Some(edit) => {
225 if step_pos <= edit.delete.start() {
226 NextSteps::Use
227 } else if step_pos <= edit.delete.end() {
228 let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert };
229 // empty slice to avoid returning steps again
230 edit.insert = &edit.insert[edit.insert.len()..];
231 NextSteps::ReplaceMany(iter)
232 } else {
233 let iter = OffsetStepIter { offset: edit.delete.start(), text: &edit.insert };
234 // empty slice to avoid returning steps again
235 edit.insert = &edit.insert[edit.insert.len()..];
236 self.advance_edit();
237 NextSteps::AddMany(iter)
238 }
239 }
240 None => NextSteps::Use,
241 }
242 }
243
244 fn translate_range(&self, range: TextRange) -> TextRange {
245 if self.acc_diff == 0 {
246 range
247 } else {
248 let start = self.translate(range.start());
249 let end = self.translate(range.end());
250 TextRange::new(start, end)
251 }
252 }
253
254 fn translate(&self, x: TextSize) -> TextSize {
255 if self.acc_diff == 0 {
256 x
257 } else {
258 TextSize::from((usize::from(x) as i64 + self.acc_diff) as u32)
259 }
260 }
261
262 fn translate_step(&self, x: &Step) -> Step {
263 if self.acc_diff == 0 {
264 x.clone()
265 } else {
266 match *x {
267 Step::Newline(n) => Step::Newline(self.translate(n)),
268 Step::Utf16Char(r) => Step::Utf16Char(self.translate_range(r)),
269 }
270 }
271 }
272}
273
274#[derive(Debug)]
275struct RunningLineCol {
276 line: u32,
277 last_newline: TextSize,
278 col_adjust: TextSize,
279}
280
281impl RunningLineCol {
282 fn new() -> RunningLineCol {
283 RunningLineCol { line: 0, last_newline: TextSize::from(0), col_adjust: TextSize::from(0) }
284 }
285
286 fn to_line_col(&self, offset: TextSize) -> LineCol {
287 LineCol {
288 line: self.line,
289 col_utf16: ((offset - self.last_newline) - self.col_adjust).into(),
290 }
291 }
292
293 fn add_line(&mut self, newline: TextSize) {
294 self.line += 1;
295 self.last_newline = newline;
296 self.col_adjust = TextSize::from(0);
297 }
298
299 fn adjust_col(&mut self, range: TextRange) {
300 self.col_adjust += range.len() - TextSize::from(1);
301 }
302}
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index c64165f3a..e713f4b7e 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -3,94 +3,35 @@
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `AnalysisChange`.
5 5
6use ra_db::{FileId, FilePosition, RelativePathBuf, SourceRootId}; 6use ra_db::{FileId, RelativePathBuf, SourceRootId};
7use ra_text_edit::{TextEdit, TextSize}; 7use ra_text_edit::TextEdit;
8 8
9#[derive(Debug, Clone)] 9#[derive(Debug, Clone)]
10pub struct SourceChange { 10pub struct SourceChange {
11 /// For display in the undo log in the editor
12 pub label: String,
13 pub source_file_edits: Vec<SourceFileEdit>, 11 pub source_file_edits: Vec<SourceFileEdit>,
14 pub file_system_edits: Vec<FileSystemEdit>, 12 pub file_system_edits: Vec<FileSystemEdit>,
15 pub cursor_position: Option<FilePosition>,
16 pub is_snippet: bool, 13 pub is_snippet: bool,
17} 14}
18 15
19impl SourceChange { 16impl SourceChange {
20 /// Creates a new SourceChange with the given label 17 /// Creates a new SourceChange with the given label
21 /// from the edits. 18 /// from the edits.
22 pub fn from_edits<L: Into<String>>( 19 pub fn from_edits(
23 label: L,
24 source_file_edits: Vec<SourceFileEdit>, 20 source_file_edits: Vec<SourceFileEdit>,
25 file_system_edits: Vec<FileSystemEdit>, 21 file_system_edits: Vec<FileSystemEdit>,
26 ) -> Self { 22 ) -> Self {
27 SourceChange { 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
28 label: label.into(),
29 source_file_edits,
30 file_system_edits,
31 cursor_position: None,
32 is_snippet: false,
33 }
34 } 24 }
35 25
36 /// Creates a new SourceChange with the given label, 26 /// Creates a new SourceChange with the given label,
37 /// containing only the given `SourceFileEdits`. 27 /// containing only the given `SourceFileEdits`.
38 pub fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { 28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
39 let label = label.into(); 29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
40 assert!(label.starts_with(char::is_uppercase));
41 SourceChange {
42 label: label,
43 source_file_edits: edits,
44 file_system_edits: vec![],
45 cursor_position: None,
46 is_snippet: false,
47 }
48 }
49
50 /// Creates a new SourceChange with the given label,
51 /// containing only the given `FileSystemEdits`.
52 pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self {
53 SourceChange {
54 label: label.into(),
55 source_file_edits: vec![],
56 file_system_edits: edits,
57 cursor_position: None,
58 is_snippet: false,
59 }
60 }
61
62 /// Creates a new SourceChange with the given label,
63 /// containing only a single `SourceFileEdit`.
64 pub fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self {
65 SourceChange::source_file_edits(label, vec![edit])
66 }
67
68 /// Creates a new SourceChange with the given label
69 /// from the given `FileId` and `TextEdit`
70 pub fn source_file_edit_from<L: Into<String>>(
71 label: L,
72 file_id: FileId,
73 edit: TextEdit,
74 ) -> Self {
75 SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit })
76 } 30 }
77
78 /// Creates a new SourceChange with the given label 31 /// Creates a new SourceChange with the given label
79 /// from the given `FileId` and `TextEdit` 32 /// from the given `FileId` and `TextEdit`
80 pub fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self { 33 pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self {
81 SourceChange::file_system_edits(label, vec![edit]) 34 SourceFileEdit { file_id, edit }.into()
82 }
83
84 /// Sets the cursor position to the given `FilePosition`
85 pub fn with_cursor(mut self, cursor_position: FilePosition) -> Self {
86 self.cursor_position = Some(cursor_position);
87 self
88 }
89
90 /// Sets the cursor position to the given `FilePosition`
91 pub fn with_cursor_opt(mut self, cursor_position: Option<FilePosition>) -> Self {
92 self.cursor_position = cursor_position;
93 self
94 } 35 }
95} 36}
96 37
@@ -100,25 +41,27 @@ pub struct SourceFileEdit {
100 pub edit: TextEdit, 41 pub edit: TextEdit,
101} 42}
102 43
44impl From<SourceFileEdit> for SourceChange {
45 fn from(edit: SourceFileEdit) -> SourceChange {
46 SourceChange {
47 source_file_edits: vec![edit],
48 file_system_edits: Vec::new(),
49 is_snippet: false,
50 }
51 }
52}
53
103#[derive(Debug, Clone)] 54#[derive(Debug, Clone)]
104pub enum FileSystemEdit { 55pub enum FileSystemEdit {
105 CreateFile { source_root: SourceRootId, path: RelativePathBuf }, 56 CreateFile { source_root: SourceRootId, path: RelativePathBuf },
106 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, 57 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
107} 58}
108 59
109pub struct SingleFileChange { 60impl From<FileSystemEdit> for SourceChange {
110 pub label: String, 61 fn from(edit: FileSystemEdit) -> SourceChange {
111 pub edit: TextEdit,
112 pub cursor_position: Option<TextSize>,
113}
114
115impl SingleFileChange {
116 pub fn into_source_change(self, file_id: FileId) -> SourceChange {
117 SourceChange { 62 SourceChange {
118 label: self.label, 63 source_file_edits: Vec::new(),
119 source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], 64 file_system_edits: vec![edit],
120 file_system_edits: Vec::new(),
121 cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }),
122 is_snippet: false, 65 is_snippet: false,
123 } 66 }
124 } 67 }
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index c07ff488e..a9a5cc7bc 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.9.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "660.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 74906d8a6..3cd6d99c3 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -6,6 +6,7 @@ use crate::{
6 ast::{AstToken, Comment, RawString, String, Whitespace}, 6 ast::{AstToken, Comment, RawString, String, Whitespace},
7 TextRange, TextSize, 7 TextRange, TextSize,
8}; 8};
9use rustc_lexer::unescape::{unescape_literal, Mode};
9 10
10impl Comment { 11impl Comment {
11 pub fn kind(&self) -> CommentKind { 12 pub fn kind(&self) -> CommentKind {
@@ -147,7 +148,7 @@ impl HasStringValue for String {
147 148
148 let mut buf = std::string::String::with_capacity(text.len()); 149 let mut buf = std::string::String::with_capacity(text.len());
149 let mut has_error = false; 150 let mut has_error = false;
150 rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char { 151 unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char {
151 Ok(c) => buf.push(c), 152 Ok(c) => buf.push(c),
152 Err(_) => has_error = true, 153 Err(_) => has_error = true,
153 }); 154 });
@@ -498,7 +499,7 @@ impl HasFormatSpecifier for String {
498 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); 499 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
499 500
500 let mut res = Vec::with_capacity(text.len()); 501 let mut res = Vec::with_capacity(text.len());
501 rustc_lexer::unescape::unescape_str(text, &mut |range, unescaped_char| { 502 unescape_literal(text, Mode::Str, &mut |range, unescaped_char| {
502 res.push(( 503 res.push((
503 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()) 504 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap())
504 + offset, 505 + offset,
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index d68cf0a82..fdec48fb0 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -2,15 +2,15 @@
2 2
3mod block; 3mod block;
4 4
5use std::convert::TryFrom;
6
7use rustc_lexer::unescape;
8
9use crate::{ 5use crate::{
10 ast, match_ast, AstNode, SyntaxError, 6 ast, match_ast, AstNode, SyntaxError,
11 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, 7 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
12 SyntaxNode, SyntaxToken, TextSize, T, 8 SyntaxNode, SyntaxToken, TextSize, T,
13}; 9};
10use rustc_lexer::unescape::{
11 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
12};
13use std::convert::TryFrom;
14 14
15fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { 15fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
16 use unescape::EscapeError as EE; 16 use unescape::EscapeError as EE;
@@ -81,10 +81,8 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
81 81
82pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { 82pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
83 // FIXME: 83 // FIXME:
84 // * Add validation of character literal containing only a single char 84 // * Add unescape validation of raw string literals and raw byte string literals
85 // * Add validation of `crate` keyword not appearing in the middle of the symbol path
86 // * Add validation of doc comments are being attached to nodes 85 // * Add validation of doc comments are being attached to nodes
87 // * Remove validation of unterminated literals (it is already implemented in `tokenize()`)
88 86
89 let mut errors = Vec::new(); 87 let mut errors = Vec::new();
90 for node in root.descendants() { 88 for node in root.descendants() {
@@ -121,18 +119,18 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
121 119
122 match token.kind() { 120 match token.kind() {
123 BYTE => { 121 BYTE => {
124 if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape::unescape_byte) { 122 if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
125 push_err(2, e); 123 push_err(2, e);
126 } 124 }
127 } 125 }
128 CHAR => { 126 CHAR => {
129 if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape::unescape_char) { 127 if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
130 push_err(1, e); 128 push_err(1, e);
131 } 129 }
132 } 130 }
133 BYTE_STRING => { 131 BYTE_STRING => {
134 if let Some(without_quotes) = unquote(text, 2, '"') { 132 if let Some(without_quotes) = unquote(text, 2, '"') {
135 unescape::unescape_byte_str(without_quotes, &mut |range, char| { 133 unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
136 if let Err(err) = char { 134 if let Err(err) = char {
137 push_err(2, (range.start, err)); 135 push_err(2, (range.start, err));
138 } 136 }
@@ -141,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
141 } 139 }
142 STRING => { 140 STRING => {
143 if let Some(without_quotes) = unquote(text, 1, '"') { 141 if let Some(without_quotes) = unquote(text, 1, '"') {
144 unescape::unescape_str(without_quotes, &mut |range, char| { 142 unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
145 if let Err(err) = char { 143 if let Err(err) = char {
146 push_err(1, (range.start, err)); 144 push_err(1, (range.start, err));
147 } 145 }
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index c4f945101..25554f583 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -3,6 +3,7 @@
3//! `rust-analyzer` never mutates text itself and only sends diffs to clients, 3//! `rust-analyzer` never mutates text itself and only sends diffs to clients,
4//! so `TextEdit` is the ultimate representation of the work done by 4//! so `TextEdit` is the ultimate representation of the work done by
5//! rust-analyzer. 5//! rust-analyzer.
6use std::{slice, vec};
6 7
7pub use text_size::{TextRange, TextSize}; 8pub use text_size::{TextRange, TextSize};
8 9
@@ -16,7 +17,7 @@ pub struct Indel {
16 pub delete: TextRange, 17 pub delete: TextRange,
17} 18}
18 19
19#[derive(Debug, Clone)] 20#[derive(Default, Debug, Clone)]
20pub struct TextEdit { 21pub struct TextEdit {
21 indels: Vec<Indel>, 22 indels: Vec<Indel>,
22} 23}
@@ -63,25 +64,24 @@ impl TextEdit {
63 builder.finish() 64 builder.finish()
64 } 65 }
65 66
66 pub(crate) fn from_indels(mut indels: Vec<Indel>) -> TextEdit { 67 pub fn len(&self) -> usize {
67 indels.sort_by_key(|a| (a.delete.start(), a.delete.end())); 68 self.indels.len()
68 for (a1, a2) in indels.iter().zip(indels.iter().skip(1)) {
69 assert!(a1.delete.end() <= a2.delete.start())
70 }
71 TextEdit { indels }
72 } 69 }
73 70
74 pub fn is_empty(&self) -> bool { 71 pub fn is_empty(&self) -> bool {
75 self.indels.is_empty() 72 self.indels.is_empty()
76 } 73 }
77 74
78 // FXME: impl IntoIter instead 75 pub fn iter(&self) -> slice::Iter<'_, Indel> {
79 pub fn as_indels(&self) -> &[Indel] { 76 self.indels.iter()
80 &self.indels 77 }
78
79 pub fn into_iter(self) -> vec::IntoIter<Indel> {
80 self.indels.into_iter()
81 } 81 }
82 82
83 pub fn apply(&self, text: &mut String) { 83 pub fn apply(&self, text: &mut String) {
84 match self.indels.len() { 84 match self.len() {
85 0 => return, 85 0 => return,
86 1 => { 86 1 => {
87 self.indels[0].apply(text); 87 self.indels[0].apply(text);
@@ -114,6 +114,17 @@ impl TextEdit {
114 *text = buf 114 *text = buf
115 } 115 }
116 116
117 pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> {
118 // FIXME: can be done without allocating intermediate vector
119 let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>();
120 if !check_disjoint(&mut all) {
121 return Err(other);
122 }
123 self.indels.extend(other.indels);
124 assert!(check_disjoint(&mut self.indels));
125 Ok(())
126 }
127
117 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> { 128 pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
118 let mut res = offset; 129 let mut res = offset;
119 for indel in self.indels.iter() { 130 for indel in self.indels.iter() {
@@ -141,9 +152,19 @@ impl TextEditBuilder {
141 self.indels.push(Indel::insert(offset, text)) 152 self.indels.push(Indel::insert(offset, text))
142 } 153 }
143 pub fn finish(self) -> TextEdit { 154 pub fn finish(self) -> TextEdit {
144 TextEdit::from_indels(self.indels) 155 let mut indels = self.indels;
156 assert!(check_disjoint(&mut indels));
157 TextEdit { indels }
145 } 158 }
146 pub fn invalidates_offset(&self, offset: TextSize) -> bool { 159 pub fn invalidates_offset(&self, offset: TextSize) -> bool {
147 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset)) 160 self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
148 } 161 }
149} 162}
163
164fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
165 indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
166 indels
167 .iter()
168 .zip(indels.iter().skip(1))
169 .all(|(l, r)| l.borrow().delete.end() <= r.borrow().delete.start())
170}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 9b2d29b1d..65b487db3 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" }
40ra_syntax = { path = "../ra_syntax" } 40ra_syntax = { path = "../ra_syntax" }
41ra_text_edit = { path = "../ra_text_edit" } 41ra_text_edit = { path = "../ra_text_edit" }
42ra_vfs = "0.6.0" 42ra_vfs = "0.6.0"
43ra_cfg = { path = "../ra_cfg"}
43 44
44# This should only be used in CLI 45# This should only be used in CLI
45ra_db = { path = "../ra_db" } 46ra_db = { path = "../ra_db" }
@@ -55,6 +56,8 @@ winapi = "0.3.8"
55tempfile = "3.1.0" 56tempfile = "3.1.0"
56insta = "0.16.0" 57insta = "0.16.0"
57test_utils = { path = "../test_utils" } 58test_utils = { path = "../test_utils" }
59mbe = { path = "../ra_mbe", package = "ra_mbe" }
60tt = { path = "../ra_tt", package = "ra_tt" }
58 61
59[features] 62[features]
60jemalloc = [ "ra_prof/jemalloc" ] 63jemalloc = [ "ra_prof/jemalloc" ]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 09908458d..e82fd57de 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -74,12 +74,25 @@ fn run_server() -> Result<()> {
74 log::info!("lifecycle: server started"); 74 log::info!("lifecycle: server started");
75 75
76 let (connection, io_threads) = Connection::stdio(); 76 let (connection, io_threads) = Connection::stdio();
77 let server_capabilities = serde_json::to_value(rust_analyzer::server_capabilities()).unwrap();
78 77
79 let initialize_params = connection.initialize(server_capabilities)?; 78 let (initialize_id, initialize_params) = connection.initialize_start()?;
80 let initialize_params = 79 let initialize_params =
81 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?; 80 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
82 81
82 let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
83
84 let initialize_result = lsp_types::InitializeResult {
85 capabilities: server_capabilities,
86 server_info: Some(lsp_types::ServerInfo {
87 name: String::from("rust-analyzer"),
88 version: Some(String::from(env!("REV"))),
89 }),
90 };
91
92 let initialize_result = serde_json::to_value(initialize_result).unwrap();
93
94 connection.initialize_finish(initialize_id, initialize_result)?;
95
83 if let Some(client_info) = initialize_params.client_info { 96 if let Some(client_info) = initialize_params.client_info {
84 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 97 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
85 } 98 }
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 110c9a442..345693524 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,19 +1,23 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2use std::env; 2use std::env;
3 3
4use crate::semantic_tokens;
5
6use lsp_types::{ 4use lsp_types::{
7 CallHierarchyServerCapability, CodeActionOptions, CodeActionProviderCapability, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionOptions,
8 CodeLensOptions, CompletionOptions, DocumentOnTypeFormattingOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
9 FoldingRangeProviderCapability, ImplementationProviderCapability, RenameOptions, 7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
10 RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability, 8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
11 SemanticTokensDocumentProvider, SemanticTokensLegend, SemanticTokensOptions, 9 SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
12 ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, 10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
13 TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, 11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
12 WorkDoneProgressOptions,
14}; 13};
14use serde_json::json;
15
16use crate::semantic_tokens;
17
18pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities {
19 let code_action_provider = code_action_capabilities(client_caps);
15 20
16pub fn server_capabilities() -> ServerCapabilities {
17 ServerCapabilities { 21 ServerCapabilities {
18 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { 22 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
19 open_close: Some(true), 23 open_close: Some(true),
@@ -45,20 +49,7 @@ pub fn server_capabilities() -> ServerCapabilities {
45 document_highlight_provider: Some(true), 49 document_highlight_provider: Some(true),
46 document_symbol_provider: Some(true), 50 document_symbol_provider: Some(true),
47 workspace_symbol_provider: Some(true), 51 workspace_symbol_provider: Some(true),
48 code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions { 52 code_action_provider: Some(code_action_provider),
49 // Advertise support for all built-in CodeActionKinds
50 code_action_kinds: Some(vec![
51 lsp_types::code_action_kind::EMPTY.to_string(),
52 lsp_types::code_action_kind::QUICKFIX.to_string(),
53 lsp_types::code_action_kind::REFACTOR.to_string(),
54 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
55 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
56 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
57 lsp_types::code_action_kind::SOURCE.to_string(),
58 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
59 ]),
60 work_done_progress_options: Default::default(),
61 })),
62 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
63 document_formatting_provider: Some(true), 54 document_formatting_provider: Some(true),
64 document_range_formatting_provider: None, 55 document_range_formatting_provider: None,
@@ -91,6 +82,37 @@ pub fn server_capabilities() -> ServerCapabilities {
91 } 82 }
92 .into(), 83 .into(),
93 ), 84 ),
94 experimental: Default::default(), 85 experimental: Some(json!({
86 "joinLines": true,
87 "ssr": true,
88 "onEnter": true,
89 "parentModule": true,
90 })),
95 } 91 }
96} 92}
93
94fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
95 client_caps
96 .text_document
97 .as_ref()
98 .and_then(|it| it.code_action.as_ref())
99 .and_then(|it| it.code_action_literal_support.as_ref())
100 .map_or(CodeActionProviderCapability::Simple(true), |_| {
101 CodeActionProviderCapability::Options(CodeActionOptions {
102 // Advertise support for all built-in CodeActionKinds.
103 // Ideally we would base this off of the client capabilities
104 // but the client is supposed to fall back gracefully for unknown values.
105 code_action_kinds: Some(vec![
106 lsp_types::code_action_kind::EMPTY.to_string(),
107 lsp_types::code_action_kind::QUICKFIX.to_string(),
108 lsp_types::code_action_kind::REFACTOR.to_string(),
109 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
110 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
111 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
112 lsp_types::code_action_kind::SOURCE.to_string(),
113 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
114 ]),
115 work_done_progress_options: Default::default(),
116 })
117 })
118}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 5e5a17943..441fb61df 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -4,6 +4,7 @@ use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 4use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 5
6use crate::{world::WorldSnapshot, Result}; 6use crate::{world::WorldSnapshot, Result};
7use ra_syntax::SmolStr;
7 8
8/// Abstract representation of Cargo target. 9/// Abstract representation of Cargo target.
9/// 10///
@@ -20,6 +21,7 @@ impl CargoTargetSpec {
20 pub(crate) fn runnable_args( 21 pub(crate) fn runnable_args(
21 spec: Option<CargoTargetSpec>, 22 spec: Option<CargoTargetSpec>,
22 kind: &RunnableKind, 23 kind: &RunnableKind,
24 features_needed: &Vec<SmolStr>,
23 ) -> Result<(Vec<String>, Vec<String>)> { 25 ) -> Result<(Vec<String>, Vec<String>)> {
24 let mut args = Vec::new(); 26 let mut args = Vec::new();
25 let mut extra_args = Vec::new(); 27 let mut extra_args = Vec::new();
@@ -73,6 +75,12 @@ impl CargoTargetSpec {
73 } 75 }
74 } 76 }
75 } 77 }
78
79 features_needed.iter().for_each(|feature| {
80 args.push("--features".to_string());
81 args.push(feature.to_string());
82 });
83
76 Ok((args, extra_args)) 84 Ok((args, extra_args))
77 } 85 }
78 86
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index d75c48597..c0f7c2c0c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -102,6 +102,7 @@ pub struct ClientCapsConfig {
102 pub hierarchical_symbols: bool, 102 pub hierarchical_symbols: bool,
103 pub code_action_literals: bool, 103 pub code_action_literals: bool,
104 pub work_done_progress: bool, 104 pub work_done_progress: bool,
105 pub code_action_group: bool,
105} 106}
106 107
107impl Default for Config { 108impl Default for Config {
@@ -268,10 +269,8 @@ impl Config {
268 { 269 {
269 self.client_caps.hierarchical_symbols = value 270 self.client_caps.hierarchical_symbols = value
270 } 271 }
271 if let Some(value) = doc_caps 272 if let Some(value) =
272 .code_action 273 doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
273 .as_ref()
274 .and_then(|it| Some(it.code_action_literal_support.is_some()))
275 { 274 {
276 self.client_caps.code_action_literals = value; 275 self.client_caps.code_action_literals = value;
277 } 276 }
@@ -294,9 +293,13 @@ impl Config {
294 293
295 self.assist.allow_snippets(false); 294 self.assist.allow_snippets(false);
296 if let Some(experimental) = &caps.experimental { 295 if let Some(experimental) = &caps.experimental {
297 let enable = 296 let snippet_text_edit =
298 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); 297 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true);
299 self.assist.allow_snippets(enable); 298 self.assist.allow_snippets(snippet_text_edit);
299
300 let code_action_group =
301 experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true);
302 self.client_caps.code_action_group = code_action_group
300 } 303 }
301 } 304 }
302} 305}
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
index 96466b5c9..c40cfdcdc 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -65,6 +65,7 @@ expression: diag
65 fixes: [ 65 fixes: [
66 CodeAction { 66 CodeAction {
67 title: "return the expression directly", 67 title: "return the expression directly",
68 group: None,
68 kind: Some( 69 kind: Some(
69 "quickfix", 70 "quickfix",
70 ), 71 ),
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 8f962277f..6dd3fcb2e 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -50,6 +50,7 @@ expression: diag
50 fixes: [ 50 fixes: [
51 CodeAction { 51 CodeAction {
52 title: "consider prefixing with an underscore", 52 title: "consider prefixing with an underscore",
53 group: None,
53 kind: Some( 54 kind: Some(
54 "quickfix", 55 "quickfix",
55 ), 56 ),
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index afea59525..a500d670a 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -145,6 +145,7 @@ fn map_rust_child_diagnostic(
145 } else { 145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147 title: rd.message.clone(), 147 title: rd.message.clone(),
148 group: None,
148 kind: Some("quickfix".to_string()), 149 kind: Some("quickfix".to_string()),
149 edit: Some(lsp_ext::SnippetWorkspaceEdit { 150 edit: Some(lsp_ext::SnippetWorkspaceEdit {
150 // FIXME: there's no good reason to use edit_map here.... 151 // FIXME: there's no good reason to use edit_map here....
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index f75a26eb7..acb1dacb6 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -3,7 +3,7 @@
3use std::{collections::HashMap, path::PathBuf}; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 8use serde::{Deserialize, Serialize};
9 9
@@ -38,13 +38,6 @@ pub struct SyntaxTreeParams {
38 pub range: Option<Range>, 38 pub range: Option<Range>,
39} 39}
40 40
41#[derive(Deserialize, Serialize, Debug)]
42#[serde(rename_all = "camelCase")]
43pub struct ExpandedMacro {
44 pub name: String,
45 pub expansion: String,
46}
47
48pub enum ExpandMacro {} 41pub enum ExpandMacro {}
49 42
50impl Request for ExpandMacro { 43impl Request for ExpandMacro {
@@ -57,53 +50,60 @@ impl Request for ExpandMacro {
57#[serde(rename_all = "camelCase")] 50#[serde(rename_all = "camelCase")]
58pub struct ExpandMacroParams { 51pub struct ExpandMacroParams {
59 pub text_document: TextDocumentIdentifier, 52 pub text_document: TextDocumentIdentifier,
60 pub position: Option<Position>, 53 pub position: Position,
54}
55
56#[derive(Deserialize, Serialize, Debug)]
57#[serde(rename_all = "camelCase")]
58pub struct ExpandedMacro {
59 pub name: String,
60 pub expansion: String,
61} 61}
62 62
63pub enum FindMatchingBrace {} 63pub enum MatchingBrace {}
64 64
65impl Request for FindMatchingBrace { 65impl Request for MatchingBrace {
66 type Params = FindMatchingBraceParams; 66 type Params = MatchingBraceParams;
67 type Result = Vec<Position>; 67 type Result = Vec<Position>;
68 const METHOD: &'static str = "rust-analyzer/findMatchingBrace"; 68 const METHOD: &'static str = "experimental/matchingBrace";
69} 69}
70 70
71#[derive(Deserialize, Serialize, Debug)] 71#[derive(Deserialize, Serialize, Debug)]
72#[serde(rename_all = "camelCase")] 72#[serde(rename_all = "camelCase")]
73pub struct FindMatchingBraceParams { 73pub struct MatchingBraceParams {
74 pub text_document: TextDocumentIdentifier, 74 pub text_document: TextDocumentIdentifier,
75 pub offsets: Vec<Position>, 75 pub positions: Vec<Position>,
76} 76}
77 77
78pub enum ParentModule {} 78pub enum ParentModule {}
79 79
80impl Request for ParentModule { 80impl Request for ParentModule {
81 type Params = lsp_types::TextDocumentPositionParams; 81 type Params = lsp_types::TextDocumentPositionParams;
82 type Result = Vec<Location>; 82 type Result = Option<lsp_types::GotoDefinitionResponse>;
83 const METHOD: &'static str = "rust-analyzer/parentModule"; 83 const METHOD: &'static str = "experimental/parentModule";
84} 84}
85 85
86pub enum JoinLines {} 86pub enum JoinLines {}
87 87
88impl Request for JoinLines { 88impl Request for JoinLines {
89 type Params = JoinLinesParams; 89 type Params = JoinLinesParams;
90 type Result = SourceChange; 90 type Result = Vec<lsp_types::TextEdit>;
91 const METHOD: &'static str = "rust-analyzer/joinLines"; 91 const METHOD: &'static str = "experimental/joinLines";
92} 92}
93 93
94#[derive(Deserialize, Serialize, Debug)] 94#[derive(Deserialize, Serialize, Debug)]
95#[serde(rename_all = "camelCase")] 95#[serde(rename_all = "camelCase")]
96pub struct JoinLinesParams { 96pub struct JoinLinesParams {
97 pub text_document: TextDocumentIdentifier, 97 pub text_document: TextDocumentIdentifier,
98 pub range: Range, 98 pub ranges: Vec<Range>,
99} 99}
100 100
101pub enum OnEnter {} 101pub enum OnEnter {}
102 102
103impl Request for OnEnter { 103impl Request for OnEnter {
104 type Params = lsp_types::TextDocumentPositionParams; 104 type Params = lsp_types::TextDocumentPositionParams;
105 type Result = Option<SourceChange>; 105 type Result = Option<Vec<SnippetTextEdit>>;
106 const METHOD: &'static str = "rust-analyzer/onEnter"; 106 const METHOD: &'static str = "experimental/onEnter";
107} 107}
108 108
109pub enum Runnables {} 109pub enum Runnables {}
@@ -133,14 +133,6 @@ pub struct Runnable {
133 pub cwd: Option<PathBuf>, 133 pub cwd: Option<PathBuf>,
134} 134}
135 135
136#[derive(Deserialize, Serialize, Debug)]
137#[serde(rename_all = "camelCase")]
138pub struct SourceChange {
139 pub label: String,
140 pub workspace_edit: SnippetWorkspaceEdit,
141 pub cursor_position: Option<lsp_types::TextDocumentPositionParams>,
142}
143
144pub enum InlayHints {} 136pub enum InlayHints {}
145 137
146impl Request for InlayHints { 138impl Request for InlayHints {
@@ -173,8 +165,8 @@ pub enum Ssr {}
173 165
174impl Request for Ssr { 166impl Request for Ssr {
175 type Params = SsrParams; 167 type Params = SsrParams;
176 type Result = SourceChange; 168 type Result = lsp_types::WorkspaceEdit;
177 const METHOD: &'static str = "rust-analyzer/ssr"; 169 const METHOD: &'static str = "experimental/ssr";
178} 170}
179 171
180#[derive(Debug, Deserialize, Serialize)] 172#[derive(Debug, Deserialize, Serialize)]
@@ -196,6 +188,8 @@ impl Request for CodeActionRequest {
196pub struct CodeAction { 188pub struct CodeAction {
197 pub title: String, 189 pub title: String,
198 #[serde(skip_serializing_if = "Option::is_none")] 190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub group: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
199 pub kind: Option<String>, 193 pub kind: Option<String>,
200 #[serde(skip_serializing_if = "Option::is_none")] 194 #[serde(skip_serializing_if = "Option::is_none")]
201 pub command: Option<lsp_types::Command>, 195 pub command: Option<lsp_types::Command>,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 87795fffb..f1287d52c 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -509,9 +509,7 @@ fn on_request(
509 .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| { 509 .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| {
510 handlers::handle_selection_range(s.snapshot(), p) 510 handlers::handle_selection_range(s.snapshot(), p)
511 })? 511 })?
512 .on_sync::<lsp_ext::FindMatchingBrace>(|s, p| { 512 .on_sync::<lsp_ext::MatchingBrace>(|s, p| handlers::handle_matching_brace(s.snapshot(), p))?
513 handlers::handle_find_matching_brace(s.snapshot(), p)
514 })?
515 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)? 513 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)?
516 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)? 514 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)?
517 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)? 515 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)?
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 4ff8fa69e..1f910ff82 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -15,14 +15,16 @@ use lsp_types::{
15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location,
16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, 16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams,
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_cfg::CfgExpr;
20use ra_ide::{ 21use ra_ide::{
21 Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
23 TextEdit,
22}; 24};
23use ra_prof::profile; 25use ra_prof::profile;
24use ra_project_model::TargetKind; 26use ra_project_model::TargetKind;
25use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; 27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize};
26use rustc_hash::FxHashMap; 28use rustc_hash::FxHashMap;
27use serde::{Deserialize, Serialize}; 29use serde::{Deserialize, Serialize};
28use serde_json::to_value; 30use serde_json::to_value;
@@ -70,15 +72,10 @@ pub fn handle_expand_macro(
70 let _p = profile("handle_expand_macro"); 72 let _p = profile("handle_expand_macro");
71 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 73 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
72 let line_index = world.analysis().file_line_index(file_id)?; 74 let line_index = world.analysis().file_line_index(file_id)?;
73 let offset = params.position.map(|p| from_proto::offset(&line_index, p)); 75 let offset = from_proto::offset(&line_index, params.position);
74 76
75 match offset { 77 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
76 None => Ok(None), 78 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
77 Some(offset) => {
78 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
79 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
80 }
81 }
82} 79}
83 80
84pub fn handle_selection_range( 81pub fn handle_selection_range(
@@ -124,15 +121,15 @@ pub fn handle_selection_range(
124 Ok(Some(res?)) 121 Ok(Some(res?))
125} 122}
126 123
127pub fn handle_find_matching_brace( 124pub fn handle_matching_brace(
128 world: WorldSnapshot, 125 world: WorldSnapshot,
129 params: lsp_ext::FindMatchingBraceParams, 126 params: lsp_ext::MatchingBraceParams,
130) -> Result<Vec<Position>> { 127) -> Result<Vec<Position>> {
131 let _p = profile("handle_find_matching_brace"); 128 let _p = profile("handle_matching_brace");
132 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 129 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
133 let line_index = world.analysis().file_line_index(file_id)?; 130 let line_index = world.analysis().file_line_index(file_id)?;
134 let res = params 131 let res = params
135 .offsets 132 .positions
136 .into_iter() 133 .into_iter()
137 .map(|position| { 134 .map(|position| {
138 let offset = from_proto::offset(&line_index, position); 135 let offset = from_proto::offset(&line_index, position);
@@ -149,30 +146,47 @@ pub fn handle_find_matching_brace(
149pub fn handle_join_lines( 146pub fn handle_join_lines(
150 world: WorldSnapshot, 147 world: WorldSnapshot,
151 params: lsp_ext::JoinLinesParams, 148 params: lsp_ext::JoinLinesParams,
152) -> Result<lsp_ext::SourceChange> { 149) -> Result<Vec<lsp_types::TextEdit>> {
153 let _p = profile("handle_join_lines"); 150 let _p = profile("handle_join_lines");
154 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 151 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
155 let source_change = world.analysis().join_lines(frange)?; 152 let line_index = world.analysis().file_line_index(file_id)?;
156 to_proto::source_change(&world, source_change) 153 let line_endings = world.file_line_endings(file_id);
154 let mut res = TextEdit::default();
155 for range in params.ranges {
156 let range = from_proto::text_range(&line_index, range);
157 let edit = world.analysis().join_lines(FileRange { file_id, range })?;
158 match res.union(edit) {
159 Ok(()) => (),
160 Err(_edit) => {
161 // just ignore overlapping edits
162 }
163 }
164 }
165 let res = to_proto::text_edit_vec(&line_index, line_endings, res);
166 Ok(res)
157} 167}
158 168
159pub fn handle_on_enter( 169pub fn handle_on_enter(
160 world: WorldSnapshot, 170 world: WorldSnapshot,
161 params: lsp_types::TextDocumentPositionParams, 171 params: lsp_types::TextDocumentPositionParams,
162) -> Result<Option<lsp_ext::SourceChange>> { 172) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
163 let _p = profile("handle_on_enter"); 173 let _p = profile("handle_on_enter");
164 let position = from_proto::file_position(&world, params)?; 174 let position = from_proto::file_position(&world, params)?;
165 match world.analysis().on_enter(position)? { 175 let edit = match world.analysis().on_enter(position)? {
166 None => Ok(None), 176 None => return Ok(None),
167 Some(source_change) => to_proto::source_change(&world, source_change).map(Some), 177 Some(it) => it,
168 } 178 };
179 let line_index = world.analysis().file_line_index(position.file_id)?;
180 let line_endings = world.file_line_endings(position.file_id);
181 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
182 Ok(Some(edit))
169} 183}
170 184
171// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. 185// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
172pub fn handle_on_type_formatting( 186pub fn handle_on_type_formatting(
173 world: WorldSnapshot, 187 world: WorldSnapshot,
174 params: lsp_types::DocumentOnTypeFormattingParams, 188 params: lsp_types::DocumentOnTypeFormattingParams,
175) -> Result<Option<Vec<TextEdit>>> { 189) -> Result<Option<Vec<lsp_types::TextEdit>>> {
176 let _p = profile("handle_on_type_formatting"); 190 let _p = profile("handle_on_type_formatting");
177 let mut position = from_proto::file_position(&world, params.text_document_position)?; 191 let mut position = from_proto::file_position(&world, params.text_document_position)?;
178 let line_index = world.analysis().file_line_index(position.file_id)?; 192 let line_index = world.analysis().file_line_index(position.file_id)?;
@@ -267,7 +281,7 @@ pub fn handle_document_symbol(
267 kind: symbol.kind, 281 kind: symbol.kind,
268 deprecated: symbol.deprecated, 282 deprecated: symbol.deprecated,
269 location: Location::new(url.clone(), symbol.range), 283 location: Location::new(url.clone(), symbol.range),
270 container_name: container_name, 284 container_name,
271 }); 285 });
272 286
273 for child in symbol.children.iter().flatten() { 287 for child in symbol.children.iter().flatten() {
@@ -330,11 +344,8 @@ pub fn handle_goto_definition(
330 None => return Ok(None), 344 None => return Ok(None),
331 Some(it) => it, 345 Some(it) => it,
332 }; 346 };
333 let res = to_proto::goto_definition_response( 347 let src = FileRange { file_id: position.file_id, range: nav_info.range };
334 &world, 348 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
335 FileRange { file_id: position.file_id, range: nav_info.range },
336 nav_info.info,
337 )?;
338 Ok(Some(res)) 349 Ok(Some(res))
339} 350}
340 351
@@ -348,11 +359,8 @@ pub fn handle_goto_implementation(
348 None => return Ok(None), 359 None => return Ok(None),
349 Some(it) => it, 360 Some(it) => it,
350 }; 361 };
351 let res = to_proto::goto_definition_response( 362 let src = FileRange { file_id: position.file_id, range: nav_info.range };
352 &world, 363 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
353 FileRange { file_id: position.file_id, range: nav_info.range },
354 nav_info.info,
355 )?;
356 Ok(Some(res)) 364 Ok(Some(res))
357} 365}
358 366
@@ -366,26 +374,20 @@ pub fn handle_goto_type_definition(
366 None => return Ok(None), 374 None => return Ok(None),
367 Some(it) => it, 375 Some(it) => it,
368 }; 376 };
369 let res = to_proto::goto_definition_response( 377 let src = FileRange { file_id: position.file_id, range: nav_info.range };
370 &world, 378 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
371 FileRange { file_id: position.file_id, range: nav_info.range },
372 nav_info.info,
373 )?;
374 Ok(Some(res)) 379 Ok(Some(res))
375} 380}
376 381
377pub fn handle_parent_module( 382pub fn handle_parent_module(
378 world: WorldSnapshot, 383 world: WorldSnapshot,
379 params: lsp_types::TextDocumentPositionParams, 384 params: lsp_types::TextDocumentPositionParams,
380) -> Result<Vec<Location>> { 385) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
381 let _p = profile("handle_parent_module"); 386 let _p = profile("handle_parent_module");
382 let position = from_proto::file_position(&world, params)?; 387 let position = from_proto::file_position(&world, params)?;
383 world 388 let navs = world.analysis().parent_module(position)?;
384 .analysis() 389 let res = to_proto::goto_definition_response(&world, None, navs)?;
385 .parent_module(position)? 390 Ok(Some(res))
386 .into_iter()
387 .map(|it| to_proto::location(&world, it.file_range()))
388 .collect::<Result<Vec<_>>>()
389} 391}
390 392
391pub fn handle_runnables( 393pub fn handle_runnables(
@@ -618,7 +620,7 @@ pub fn handle_references(
618pub fn handle_formatting( 620pub fn handle_formatting(
619 world: WorldSnapshot, 621 world: WorldSnapshot,
620 params: DocumentFormattingParams, 622 params: DocumentFormattingParams,
621) -> Result<Option<Vec<TextEdit>>> { 623) -> Result<Option<Vec<lsp_types::TextEdit>>> {
622 let _p = profile("handle_formatting"); 624 let _p = profile("handle_formatting");
623 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 625 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
624 let file = world.analysis().file_text(file_id)?; 626 let file = world.analysis().file_text(file_id)?;
@@ -685,7 +687,7 @@ pub fn handle_formatting(
685 } 687 }
686 } 688 }
687 689
688 Ok(Some(vec![TextEdit { 690 Ok(Some(vec![lsp_types::TextEdit {
689 range: Range::new(Position::new(0, 0), end_position), 691 range: Range::new(Position::new(0, 0), end_position),
690 new_text: captured_stdout, 692 new_text: captured_stdout,
691 }])) 693 }]))
@@ -706,6 +708,7 @@ pub fn handle_code_action(
706 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 708 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
707 let line_index = world.analysis().file_line_index(file_id)?; 709 let line_index = world.analysis().file_line_index(file_id)?;
708 let range = from_proto::text_range(&line_index, params.range); 710 let range = from_proto::text_range(&line_index, params.range);
711 let frange = FileRange { file_id, range };
709 712
710 let diagnostics = world.analysis().diagnostics(file_id)?; 713 let diagnostics = world.analysis().diagnostics(file_id)?;
711 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 714 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
@@ -716,10 +719,11 @@ pub fn handle_code_action(
716 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 719 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
717 .map(|(_range, fix)| fix); 720 .map(|(_range, fix)| fix);
718 721
719 for source_edit in fixes_from_diagnostics { 722 for fix in fixes_from_diagnostics {
720 let title = source_edit.label.clone(); 723 let title = fix.label;
721 let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; 724 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?;
722 let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; 725 let action =
726 lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None };
723 res.push(action); 727 res.push(action);
724 } 728 }
725 729
@@ -731,53 +735,9 @@ pub fn handle_code_action(
731 res.push(fix.action.clone()); 735 res.push(fix.action.clone());
732 } 736 }
733 737
734 let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); 738 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() {
735 for assist in 739 res.push(to_proto::code_action(&world, assist)?.into());
736 world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter()
737 {
738 match &assist.group_label {
739 Some(label) => grouped_assists
740 .entry(label.to_owned())
741 .or_insert_with(|| {
742 let idx = res.len();
743 let dummy = lsp_ext::CodeAction {
744 title: String::new(),
745 kind: None,
746 command: None,
747 edit: None,
748 };
749 res.push(dummy);
750 (idx, Vec::new())
751 })
752 .1
753 .push(assist),
754 None => {
755 res.push(to_proto::code_action(&world, assist)?.into());
756 }
757 }
758 } 740 }
759
760 for (group_label, (idx, assists)) in grouped_assists {
761 if assists.len() == 1 {
762 res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into();
763 } else {
764 let title = group_label;
765
766 let mut arguments = Vec::with_capacity(assists.len());
767 for assist in assists {
768 let source_change = to_proto::source_change(&world, assist.source_change)?;
769 arguments.push(to_value(source_change)?);
770 }
771
772 let command = Some(Command {
773 title: title.clone(),
774 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
775 arguments: Some(vec![serde_json::Value::Array(arguments)]),
776 });
777 res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command };
778 }
779 }
780
781 Ok(Some(res)) 741 Ok(Some(res))
782} 742}
783 743
@@ -802,11 +762,11 @@ pub fn handle_code_lens(
802 for runnable in world.analysis().runnables(file_id)? { 762 for runnable in world.analysis().runnables(file_id)? {
803 let (run_title, debugee) = match &runnable.kind { 763 let (run_title, debugee) = match &runnable.kind {
804 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { 764 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => {
805 ("▶\u{fe0e}Run Test", true) 765 ("▶\u{fe0e} Run Test", true)
806 } 766 }
807 RunnableKind::DocTest { .. } => { 767 RunnableKind::DocTest { .. } => {
808 // cargo does not support -no-run for doctests 768 // cargo does not support -no-run for doctests
809 ("▶\u{fe0e}Run Doctest", false) 769 ("▶\u{fe0e} Run Doctest", false)
810 } 770 }
811 RunnableKind::Bench { .. } => { 771 RunnableKind::Bench { .. } => {
812 // Nothing wrong with bench debugging 772 // Nothing wrong with bench debugging
@@ -972,11 +932,11 @@ pub fn handle_document_highlight(
972pub fn handle_ssr( 932pub fn handle_ssr(
973 world: WorldSnapshot, 933 world: WorldSnapshot,
974 params: lsp_ext::SsrParams, 934 params: lsp_ext::SsrParams,
975) -> Result<lsp_ext::SourceChange> { 935) -> Result<lsp_types::WorkspaceEdit> {
976 let _p = profile("handle_ssr"); 936 let _p = profile("handle_ssr");
977 let source_change = 937 let source_change =
978 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 938 world.analysis().structural_search_replace(&params.query, params.parse_only)??;
979 to_proto::source_change(&world, source_change) 939 to_proto::workspace_edit(&world, source_change)
980} 940}
981 941
982pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 942pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
@@ -1006,7 +966,12 @@ fn to_lsp_runnable(
1006) -> Result<lsp_ext::Runnable> { 966) -> Result<lsp_ext::Runnable> {
1007 let spec = CargoTargetSpec::for_file(world, file_id)?; 967 let spec = CargoTargetSpec::for_file(world, file_id)?;
1008 let target = spec.as_ref().map(|s| s.target.clone()); 968 let target = spec.as_ref().map(|s| s.target.clone());
1009 let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; 969 let mut features_needed = vec![];
970 for cfg_expr in &runnable.cfg_exprs {
971 collect_minimal_features_needed(cfg_expr, &mut features_needed);
972 }
973 let (args, extra_args) =
974 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
1010 let line_index = world.analysis().file_line_index(file_id)?; 975 let line_index = world.analysis().file_line_index(file_id)?;
1011 let label = match &runnable.kind { 976 let label = match &runnable.kind {
1012 RunnableKind::Test { test_id, .. } => format!("test {}", test_id), 977 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
@@ -1032,6 +997,26 @@ fn to_lsp_runnable(
1032 }) 997 })
1033} 998}
1034 999
1000/// Fill minimal features needed
1001fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
1002 match cfg_expr {
1003 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1004 CfgExpr::All(preds) => {
1005 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1006 }
1007 CfgExpr::Any(preds) => {
1008 for cfg in preds {
1009 let len_features = features.len();
1010 collect_minimal_features_needed(cfg, features);
1011 if len_features != features.len() {
1012 break;
1013 }
1014 }
1015 }
1016 _ => {}
1017 }
1018}
1019
1035pub fn handle_inlay_hints( 1020pub fn handle_inlay_hints(
1036 world: WorldSnapshot, 1021 world: WorldSnapshot,
1037 params: InlayHintsParams, 1022 params: InlayHintsParams,
@@ -1168,3 +1153,54 @@ pub fn handle_semantic_tokens_range(
1168 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1153 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1169 Ok(Some(semantic_tokens.into())) 1154 Ok(Some(semantic_tokens.into()))
1170} 1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160
1161 use mbe::{ast_to_token_tree, TokenMap};
1162 use ra_cfg::parse_cfg;
1163 use ra_syntax::{
1164 ast::{self, AstNode},
1165 SmolStr,
1166 };
1167
1168 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
1169 let source_file = ast::SourceFile::parse(input).ok().unwrap();
1170 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
1171 ast_to_token_tree(&tt).unwrap()
1172 }
1173
1174 #[test]
1175 fn test_cfg_expr_minimal_features_needed() {
1176 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
1177 let cfg_expr = parse_cfg(&subtree);
1178 let mut min_features = vec![];
1179 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1180
1181 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1182
1183 let (subtree, _) =
1184 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
1185 let cfg_expr = parse_cfg(&subtree);
1186
1187 let mut min_features = vec![];
1188 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1189 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
1190
1191 let (subtree, _) =
1192 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
1193 let cfg_expr = parse_cfg(&subtree);
1194
1195 let mut min_features = vec![];
1196 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1197 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1198
1199 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
1200 let cfg_expr = parse_cfg(&subtree);
1201
1202 let mut min_features = vec![];
1203 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1204 assert!(min_features.is_empty());
1205 }
1206}
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc5cb119..6f125c37c 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -36,9 +36,11 @@ macro_rules! define_semantic_token_types {
36 36
37define_semantic_token_types![ 37define_semantic_token_types![
38 (ATTRIBUTE, "attribute"), 38 (ATTRIBUTE, "attribute"),
39 (BOOLEAN, "boolean"),
39 (BUILTIN_TYPE, "builtinType"), 40 (BUILTIN_TYPE, "builtinType"),
40 (ENUM_MEMBER, "enumMember"), 41 (ENUM_MEMBER, "enumMember"),
41 (LIFETIME, "lifetime"), 42 (LIFETIME, "lifetime"),
43 (SELF_KEYWORD, "selfKeyword"),
42 (TYPE_ALIAS, "typeAlias"), 44 (TYPE_ALIAS, "typeAlias"),
43 (UNION, "union"), 45 (UNION, "union"),
44 (UNRESOLVED_REFERENCE, "unresolvedReference"), 46 (UNRESOLVED_REFERENCE, "unresolvedReference"),
@@ -67,6 +69,7 @@ define_semantic_token_modifiers![
67 (CONTROL_FLOW, "controlFlow"), 69 (CONTROL_FLOW, "controlFlow"),
68 (MUTABLE, "mutable"), 70 (MUTABLE, "mutable"),
69 (UNSAFE, "unsafe"), 71 (UNSAFE, "unsafe"),
72 (ATTRIBUTE_MODIFIER, "attribute"),
70]; 73];
71 74
72#[derive(Default)] 75#[derive(Default)]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index af54f81b7..8e8e7033d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,13 +1,12 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use ra_db::{FileId, FileRange}; 2use ra_db::{FileId, FileRange};
3use ra_ide::{ 3use ra_ide::{
4 translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, 4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
5 FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, 5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
6 HighlightedRange, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity,
7 ReferenceAccess, Severity, SourceChange, SourceFileEdit, 7 SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_text_edit::{Indel, TextEdit};
11use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
12 11
13use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result};
@@ -133,10 +132,18 @@ pub(crate) fn text_edit_vec(
133 line_endings: LineEndings, 132 line_endings: LineEndings,
134 text_edit: TextEdit, 133 text_edit: TextEdit,
135) -> Vec<lsp_types::TextEdit> { 134) -> Vec<lsp_types::TextEdit> {
135 text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect()
136}
137
138pub(crate) fn snippet_text_edit_vec(
139 line_index: &LineIndex,
140 line_endings: LineEndings,
141 is_snippet: bool,
142 text_edit: TextEdit,
143) -> Vec<lsp_ext::SnippetTextEdit> {
136 text_edit 144 text_edit
137 .as_indels() 145 .into_iter()
138 .iter() 146 .map(|indel| self::snippet_text_edit(line_index, line_endings, is_snippet, indel))
139 .map(|it| self::text_edit(line_index, line_endings, it.clone()))
140 .collect() 147 .collect()
141} 148}
142 149
@@ -150,7 +157,7 @@ pub(crate) fn completion_item(
150 // LSP does not allow arbitrary edits in completion, so we have to do a 157 // LSP does not allow arbitrary edits in completion, so we have to do a
151 // non-trivial mapping here. 158 // non-trivial mapping here.
152 let source_range = completion_item.source_range(); 159 let source_range = completion_item.source_range();
153 for indel in completion_item.text_edit().as_indels() { 160 for indel in completion_item.text_edit().iter() {
154 if indel.delete.contains_range(source_range) { 161 if indel.delete.contains_range(source_range) {
155 text_edit = Some(if indel.delete == source_range { 162 text_edit = Some(if indel.delete == source_range {
156 self::text_edit(line_index, line_endings, indel.clone()) 163 self::text_edit(line_index, line_endings, indel.clone())
@@ -279,6 +286,7 @@ fn semantic_token_type_and_modifiers(
279 HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, 286 HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS,
280 HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, 287 HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE,
281 HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, 288 HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
289 HighlightTag::SelfKeyword => semantic_tokens::SELF_KEYWORD,
282 HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, 290 HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE,
283 HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY, 291 HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY,
284 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, 292 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION,
@@ -300,6 +308,7 @@ fn semantic_token_type_and_modifiers(
300 HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { 308 HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => {
301 lsp_types::SemanticTokenType::NUMBER 309 lsp_types::SemanticTokenType::NUMBER
302 } 310 }
311 HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN,
303 HighlightTag::CharLiteral | HighlightTag::StringLiteral => { 312 HighlightTag::CharLiteral | HighlightTag::StringLiteral => {
304 lsp_types::SemanticTokenType::STRING 313 lsp_types::SemanticTokenType::STRING
305 } 314 }
@@ -312,6 +321,7 @@ fn semantic_token_type_and_modifiers(
312 321
313 for modifier in highlight.modifiers.iter() { 322 for modifier in highlight.modifiers.iter() {
314 let modifier = match modifier { 323 let modifier = match modifier {
324 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
315 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, 325 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
316 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, 326 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW,
317 HighlightModifier::Mutable => semantic_tokens::MUTABLE, 327 HighlightModifier::Mutable => semantic_tokens::MUTABLE,
@@ -375,14 +385,6 @@ pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::U
375 world.file_id_to_uri(file_id) 385 world.file_id_to_uri(file_id)
376} 386}
377 387
378pub(crate) fn text_document_identifier(
379 world: &WorldSnapshot,
380 file_id: FileId,
381) -> Result<lsp_types::TextDocumentIdentifier> {
382 let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? };
383 Ok(res)
384}
385
386pub(crate) fn versioned_text_document_identifier( 388pub(crate) fn versioned_text_document_identifier(
387 world: &WorldSnapshot, 389 world: &WorldSnapshot,
388 file_id: FileId, 390 file_id: FileId,
@@ -402,13 +404,20 @@ pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_t
402 404
403pub(crate) fn location_link( 405pub(crate) fn location_link(
404 world: &WorldSnapshot, 406 world: &WorldSnapshot,
405 src: FileRange, 407 src: Option<FileRange>,
406 target: NavigationTarget, 408 target: NavigationTarget,
407) -> Result<lsp_types::LocationLink> { 409) -> Result<lsp_types::LocationLink> {
408 let src_location = location(world, src)?; 410 let origin_selection_range = match src {
411 Some(src) => {
412 let line_index = world.analysis().file_line_index(src.file_id)?;
413 let range = range(&line_index, src.range);
414 Some(range)
415 }
416 None => None,
417 };
409 let (target_uri, target_range, target_selection_range) = location_info(world, target)?; 418 let (target_uri, target_range, target_selection_range) = location_info(world, target)?;
410 let res = lsp_types::LocationLink { 419 let res = lsp_types::LocationLink {
411 origin_selection_range: Some(src_location.range), 420 origin_selection_range,
412 target_uri, 421 target_uri,
413 target_range, 422 target_range,
414 target_selection_range, 423 target_selection_range,
@@ -431,7 +440,7 @@ fn location_info(
431 440
432pub(crate) fn goto_definition_response( 441pub(crate) fn goto_definition_response(
433 world: &WorldSnapshot, 442 world: &WorldSnapshot,
434 src: FileRange, 443 src: Option<FileRange>,
435 targets: Vec<NavigationTarget>, 444 targets: Vec<NavigationTarget>,
436) -> Result<lsp_types::GotoDefinitionResponse> { 445) -> Result<lsp_types::GotoDefinitionResponse> {
437 if world.config.client_caps.location_link { 446 if world.config.client_caps.location_link {
@@ -467,9 +476,8 @@ pub(crate) fn snippet_text_document_edit(
467 let line_endings = world.file_line_endings(source_file_edit.file_id); 476 let line_endings = world.file_line_endings(source_file_edit.file_id);
468 let edits = source_file_edit 477 let edits = source_file_edit
469 .edit 478 .edit
470 .as_indels() 479 .into_iter()
471 .iter() 480 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it))
472 .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone()))
473 .collect(); 481 .collect();
474 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) 482 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
475} 483}
@@ -492,36 +500,6 @@ pub(crate) fn resource_op(
492 Ok(res) 500 Ok(res)
493} 501}
494 502
495pub(crate) fn source_change(
496 world: &WorldSnapshot,
497 source_change: SourceChange,
498) -> Result<lsp_ext::SourceChange> {
499 let cursor_position = match source_change.cursor_position {
500 None => None,
501 Some(pos) => {
502 let line_index = world.analysis().file_line_index(pos.file_id)?;
503 let edit = source_change
504 .source_file_edits
505 .iter()
506 .find(|it| it.file_id == pos.file_id)
507 .map(|it| &it.edit);
508 let line_col = match edit {
509 Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
510 None => line_index.line_col(pos.offset),
511 };
512 let position =
513 lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
514 Some(lsp_types::TextDocumentPositionParams {
515 text_document: text_document_identifier(world, pos.file_id)?,
516 position,
517 })
518 }
519 };
520 let label = source_change.label.clone();
521 let workspace_edit = self::snippet_workspace_edit(world, source_change)?;
522 Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position })
523}
524
525pub(crate) fn snippet_workspace_edit( 503pub(crate) fn snippet_workspace_edit(
526 world: &WorldSnapshot, 504 world: &WorldSnapshot,
527 source_change: SourceChange, 505 source_change: SourceChange,
@@ -639,25 +617,12 @@ fn main() <fold>{
639} 617}
640 618
641pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { 619pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> {
642 let res = if assist.source_change.cursor_position.is_none() { 620 let res = lsp_ext::CodeAction {
643 lsp_ext::CodeAction { 621 title: assist.label,
644 title: assist.label, 622 group: if world.config.client_caps.code_action_group { assist.group_label } else { None },
645 kind: Some(String::new()), 623 kind: Some(String::new()),
646 edit: Some(snippet_workspace_edit(world, assist.source_change)?), 624 edit: Some(snippet_workspace_edit(world, assist.source_change)?),
647 command: None, 625 command: None,
648 }
649 } else {
650 assert!(!assist.source_change.is_snippet);
651 let source_change = source_change(&world, assist.source_change)?;
652 let arg = serde_json::to_value(source_change)?;
653 let title = assist.label;
654 let command = lsp_types::Command {
655 title: title.clone(),
656 command: "rust-analyzer.applySourceChange".to_string(),
657 arguments: Some(vec![arg]),
658 };
659
660 lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) }
661 }; 626 };
662 Ok(res) 627 Ok(res)
663} 628}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 74676b3ee..405ddb362 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -473,29 +473,14 @@ fn main() {{}}
473 text_document: server.doc_id("src/m0.rs"), 473 text_document: server.doc_id("src/m0.rs"),
474 position: Position { line: 0, character: 5 }, 474 position: Position { line: 0, character: 5 },
475 }, 475 },
476 json!({ 476 json!([{
477 "cursorPosition": { 477 "insertTextFormat": 2,
478 "position": { "character": 4, "line": 1 }, 478 "newText": "\n/// $0",
479 "textDocument": { "uri": "file:///[..]src/m0.rs" } 479 "range": {
480 }, 480 "end": { "character": 5, "line": 0 },
481 "label": "On enter", 481 "start": { "character": 5, "line": 0 }
482 "workspaceEdit": { 482 }
483 "documentChanges": [ 483 }]),
484 {
485 "edits": [
486 {
487 "newText": "\n/// ",
488 "range": {
489 "end": { "character": 5, "line": 0 },
490 "start": { "character": 5, "line": 0 }
491 }
492 }
493 ],
494 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
495 }
496 ]
497 }
498 }),
499 ); 484 );
500 let elapsed = start.elapsed(); 485 let elapsed = start.elapsed();
501 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed); 486 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
@@ -525,29 +510,14 @@ version = \"0.0.0\"
525 text_document: server.doc_id("src/main.rs"), 510 text_document: server.doc_id("src/main.rs"),
526 position: Position { line: 0, character: 8 }, 511 position: Position { line: 0, character: 8 },
527 }, 512 },
528 json!({ 513 json!([{
529 "cursorPosition": { 514 "insertTextFormat": 2,
530 "position": { "line": 1, "character": 4 }, 515 "newText": "\r\n/// $0",
531 "textDocument": { "uri": "file:///[..]src/main.rs" } 516 "range": {
532 }, 517 "end": { "line": 0, "character": 8 },
533 "label": "On enter", 518 "start": { "line": 0, "character": 8 }
534 "workspaceEdit": { 519 }
535 "documentChanges": [ 520 }]),
536 {
537 "edits": [
538 {
539 "newText": "\r\n/// ",
540 "range": {
541 "end": { "line": 0, "character": 8 },
542 "start": { "line": 0, "character": 8 }
543 }
544 }
545 ],
546 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
547 }
548 ]
549 }
550 }),
551 ); 521 );
552} 522}
553 523
@@ -786,5 +756,5 @@ pub fn foo(_input: TokenStream) -> TokenStream {
786 }); 756 });
787 757
788 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 758 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
789 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) 759 assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
790} 760}
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 9acbae066..66a6f4d54 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -68,7 +68,7 @@ impl<'a> Project<'a> {
68 let mut paths = vec![]; 68 let mut paths = vec![];
69 69
70 for entry in parse_fixture(self.fixture) { 70 for entry in parse_fixture(self.fixture) {
71 let path = tmp_dir.path().join(entry.meta); 71 let path = tmp_dir.path().join(entry.meta.path().as_str());
72 fs::create_dir_all(path.parent().unwrap()).unwrap(); 72 fs::create_dir_all(path.parent().unwrap()).unwrap();
73 fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); 73 fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
74 paths.push((path, entry.text)); 74 paths.push((path, entry.text));
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 8ec986bcb..4d185b01c 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -11,3 +11,7 @@ doctest = false
11difference = "2.0.0" 11difference = "2.0.0"
12text-size = "1.0.0" 12text-size = "1.0.0"
13serde_json = "1.0.48" 13serde_json = "1.0.48"
14relative-path = "1.0.0"
15rustc-hash = "1.1.0"
16
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index be2cfbaa2..1bd97215c 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -14,6 +14,10 @@ use std::{
14 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
15}; 15};
16 16
17pub use ra_cfg::CfgOptions;
18
19pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap;
17use serde_json::Value; 21use serde_json::Value;
18use text_size::{TextRange, TextSize}; 22use text_size::{TextRange, TextSize};
19 23
@@ -157,10 +161,82 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
157 161
158#[derive(Debug, Eq, PartialEq)] 162#[derive(Debug, Eq, PartialEq)]
159pub struct FixtureEntry { 163pub struct FixtureEntry {
160 pub meta: String, 164 pub meta: FixtureMeta,
161 pub text: String, 165 pub text: String,
162} 166}
163 167
168#[derive(Debug, Eq, PartialEq)]
169pub enum FixtureMeta {
170 Root { path: RelativePathBuf },
171 File(FileMeta),
172}
173
174#[derive(Debug, Eq, PartialEq)]
175pub struct FileMeta {
176 pub path: RelativePathBuf,
177 pub crate_name: Option<String>,
178 pub deps: Vec<String>,
179 pub cfg: CfgOptions,
180 pub edition: Option<String>,
181 pub env: FxHashMap<String, String>,
182}
183
184impl FixtureMeta {
185 pub fn path(&self) -> &RelativePath {
186 match self {
187 FixtureMeta::Root { path } => &path,
188 FixtureMeta::File(f) => &f.path,
189 }
190 }
191
192 pub fn crate_name(&self) -> Option<&String> {
193 match self {
194 FixtureMeta::File(f) => f.crate_name.as_ref(),
195 _ => None,
196 }
197 }
198
199 pub fn cfg_options(&self) -> Option<&CfgOptions> {
200 match self {
201 FixtureMeta::File(f) => Some(&f.cfg),
202 _ => None,
203 }
204 }
205
206 pub fn edition(&self) -> Option<&String> {
207 match self {
208 FixtureMeta::File(f) => f.edition.as_ref(),
209 _ => None,
210 }
211 }
212
213 pub fn env(&self) -> impl Iterator<Item = (&String, &String)> {
214 struct EnvIter<'a> {
215 iter: Option<std::collections::hash_map::Iter<'a, String, String>>,
216 }
217
218 impl<'a> EnvIter<'a> {
219 fn new(meta: &'a FixtureMeta) -> Self {
220 Self {
221 iter: match meta {
222 FixtureMeta::File(f) => Some(f.env.iter()),
223 _ => None,
224 },
225 }
226 }
227 }
228
229 impl<'a> Iterator for EnvIter<'a> {
230 type Item = (&'a String, &'a String);
231 fn next(&mut self) -> Option<Self::Item> {
232 self.iter.as_mut().and_then(|i| i.next())
233 }
234 }
235
236 EnvIter::new(self)
237 }
238}
239
164/// Parses text which looks like this: 240/// Parses text which looks like this:
165/// 241///
166/// ```not_rust 242/// ```not_rust
@@ -169,8 +245,8 @@ pub struct FixtureEntry {
169/// line 2 245/// line 2
170/// // - other meta 246/// // - other meta
171/// ``` 247/// ```
172pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { 248pub fn parse_fixture(ra_fixture: &str) -> Vec<FixtureEntry> {
173 let fixture = indent_first_line(fixture); 249 let fixture = indent_first_line(ra_fixture);
174 let margin = fixture_margin(&fixture); 250 let margin = fixture_margin(&fixture);
175 251
176 let mut lines = fixture 252 let mut lines = fixture
@@ -200,6 +276,7 @@ The offending line: {:?}"#,
200 for line in lines.by_ref() { 276 for line in lines.by_ref() {
201 if line.starts_with("//-") { 277 if line.starts_with("//-") {
202 let meta = line["//-".len()..].trim().to_string(); 278 let meta = line["//-".len()..].trim().to_string();
279 let meta = parse_meta(&meta);
203 res.push(FixtureEntry { meta, text: String::new() }) 280 res.push(FixtureEntry { meta, text: String::new() })
204 } else if let Some(entry) = res.last_mut() { 281 } else if let Some(entry) = res.last_mut() {
205 entry.text.push_str(line); 282 entry.text.push_str(line);
@@ -209,6 +286,57 @@ The offending line: {:?}"#,
209 res 286 res
210} 287}
211 288
289//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
290fn parse_meta(meta: &str) -> FixtureMeta {
291 let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
292
293 if components[0] == "root" {
294 let path: RelativePathBuf = components[1].into();
295 assert!(path.starts_with("/") && path.ends_with("/"));
296 return FixtureMeta::Root { path };
297 }
298
299 let path: RelativePathBuf = components[0].into();
300 assert!(path.starts_with("/"));
301
302 let mut krate = None;
303 let mut deps = Vec::new();
304 let mut edition = None;
305 let mut cfg = CfgOptions::default();
306 let mut env = FxHashMap::default();
307 for component in components[1..].iter() {
308 let (key, value) = split1(component, ':').unwrap();
309 match key {
310 "crate" => krate = Some(value.to_string()),
311 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
312 "edition" => edition = Some(value.to_string()),
313 "cfg" => {
314 for key in value.split(',') {
315 match split1(key, '=') {
316 None => cfg.insert_atom(key.into()),
317 Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
318 }
319 }
320 }
321 "env" => {
322 for key in value.split(',') {
323 if let Some((k, v)) = split1(key, '=') {
324 env.insert(k.into(), v.into());
325 }
326 }
327 }
328 _ => panic!("bad component: {:?}", component),
329 }
330 }
331
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env })
333}
334
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
212/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. 340/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
213/// This allows fixtures to start off in a different indentation, e.g. to align the first line with 341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
214/// the other lines visually: 342/// the other lines visually:
@@ -288,13 +416,33 @@ struct Bar;
288 ) 416 )
289} 417}
290 418
419#[test]
420fn parse_fixture_gets_full_meta() {
421 let parsed = parse_fixture(
422 r"
423 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
424 mod m;
425 ",
426 );
427 assert_eq!(1, parsed.len());
428
429 let parsed = &parsed[0];
430 assert_eq!("mod m;\n\n", parsed.text);
431
432 let meta = &parsed.meta;
433 assert_eq!("foo", meta.crate_name().unwrap());
434 assert_eq!("/lib.rs", meta.path());
435 assert!(meta.cfg_options().is_some());
436 assert_eq!(2, meta.env().count());
437}
438
291/// Same as `parse_fixture`, except it allow empty fixture 439/// Same as `parse_fixture`, except it allow empty fixture
292pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { 440pub fn parse_single_fixture(ra_fixture: &str) -> Option<FixtureEntry> {
293 if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { 441 if !ra_fixture.lines().any(|it| it.trim_start().starts_with("//-")) {
294 return None; 442 return None;
295 } 443 }
296 444
297 let fixtures = parse_fixture(fixture); 445 let fixtures = parse_fixture(ra_fixture);
298 if fixtures.len() > 1 { 446 if fixtures.len() > 1 {
299 panic!("too many fixtures"); 447 panic!("too many fixtures");
300 } 448 }
diff --git a/docs/dev/README.md b/docs/dev/README.md
index a20ead0b6..65cc9fc12 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -74,7 +74,7 @@ relevant test and execute it (VS Code includes an action for running a single
74test). 74test).
75 75
76However, launching a VS Code instance with locally build language server is 76However, launching a VS Code instance with locally build language server is
77possible. There's **"Run Extension (Dev Server)"** launch configuration for this. 77possible. There's **"Run Extension (Debug Build)"** launch configuration for this.
78 78
79In general, I use one of the following workflows for fixing bugs and 79In general, I use one of the following workflows for fixing bugs and
80implementing features. 80implementing features.
@@ -86,7 +86,7 @@ then just do printf-driven development/debugging. As a sanity check after I'm
86done, I use `cargo xtask install --server` and **Reload Window** action in VS 86done, I use `cargo xtask install --server` and **Reload Window** action in VS
87Code to sanity check that the thing works as I expect. 87Code to sanity check that the thing works as I expect.
88 88
89If the problem concerns only the VS Code extension, I use **Run Extension** 89If the problem concerns only the VS Code extension, I use **Run Installed Extension**
90launch configuration from `launch.json`. Notably, this uses the usual 90launch configuration from `launch.json`. Notably, this uses the usual
91`rust-analyzer` binary from `PATH`. For this it is important to have the following 91`rust-analyzer` binary from `PATH`. For this it is important to have the following
92in `setting.json` file: 92in `setting.json` file:
diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md
index 1aa392935..59a83f7d7 100644
--- a/docs/dev/debugging.md
+++ b/docs/dev/debugging.md
@@ -22,8 +22,8 @@ where **only** the `rust-analyzer` extension being debugged is enabled.
22 22
23## Debug TypeScript VSCode extension 23## Debug TypeScript VSCode extension
24 24
25- `Run Extension` - runs the extension with the globally installed `rust-analyzer` binary. 25- `Run Installed Extension` - runs the extension with the globally installed `rust-analyzer` binary.
26- `Run Extension (Dev Server)` - runs extension with the locally built LSP server (`target/debug/rust-analyzer`). 26- `Run Extension (Debug Build)` - runs extension with the locally built LSP server (`target/debug/rust-analyzer`).
27 27
28TypeScript debugging is configured to watch your source edits and recompile. 28TypeScript debugging is configured to watch your source edits and recompile.
29To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</kbd> and run the following command in your `[Extension Development Host]` 29To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</kbd> and run the following command in your `[Extension Development Host]`
@@ -47,7 +47,7 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k
47 debug = 2 47 debug = 2
48 ``` 48 ```
49 49
50- Select `Run Extension (Dev Server)` to run your locally built `target/debug/rust-analyzer`. 50- Select `Run Extension (Debug Build)` to run your locally built `target/debug/rust-analyzer`.
51 51
52- In the original VSCode window once again select the `Attach To Server` debug configuration. 52- In the original VSCode window once again select the `Attach To Server` debug configuration.
53 53
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index d2ec6c021..209f470eb 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -3,17 +3,15 @@
3This document describes LSP extensions used by rust-analyzer. 3This document describes LSP extensions used by rust-analyzer.
4It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ). 4It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ).
5We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. 5We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority.
6All capabilities are enabled via `experimental` field of `ClientCapabilities`. 6All capabilities are enabled via `experimental` field of `ClientCapabilities` or `ServerCapabilities`.
7Requests which we hope to upstream live under `experimental/` namespace.
8Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace.
7 9
8## `SnippetTextEdit` 10## Snippet `TextEdit`
9 11
10**Capability** 12**Issue:** https://github.com/microsoft/language-server-protocol/issues/724
11 13
12```typescript 14**Client Capability:** `{ "snippetTextEdit": boolean }`
13{
14 "snippetTextEdit": boolean
15}
16```
17 15
18If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: 16If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s:
19 17
@@ -32,3 +30,335 @@ export interface TextDocumentEdit {
32 30
33When applying such code action, the editor should insert snippet, with tab stops and placeholder. 31When applying such code action, the editor should insert snippet, with tab stops and placeholder.
34At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. 32At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`.
33
34### Example
35
36"Add `derive`" code action transforms `struct S;` into `#[derive($0)] struct S;`
37
38### Unresolved Questions
39
40* Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)?
41* Can snippets span multiple files (so far, no)?
42
43## `CodeAction` Groups
44
45**Issue:** https://github.com/microsoft/language-server-protocol/issues/994
46
47**Client Capability:** `{ "codeActionGroup": boolean }`
48
49If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
50
51```typescript
52interface CodeAction {
53 title: string;
54 group?: string;
55 ...
56}
57```
58
59All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu.
60The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as
61
62```
63💡
64 +-------------+
65 | foo |
66 +-------------+-----+
67 | frobnicate >| bar |
68 +-------------+-----+
69 | baz |
70 +-----+
71```
72
73Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`.
74
75### Example
76
77```rust
78fn main() {
79 let x: Entry/*cursor here*/ = todo!();
80}
81```
82
83Invoking code action at this position will yield two code actions for importing `Entry` from either `collections::HashMap` or `collection::BTreeMap`, grouped under a single "import" group.
84
85### Unresolved Questions
86
87* Is a fixed two-level structure enough?
88* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
89
90## Parent Module
91
92**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
93
94**Server Capability:** `{ "parentModule": boolean }`
95
96This request is send from client to server to handle "Goto Parent Module" editor action.
97
98**Method:** `experimental/parentModule`
99
100**Request:** `TextDocumentPositionParams`
101
102**Response:** `Location | Location[] | LocationLink[] | null`
103
104
105### Example
106
107```rust
108// src/main.rs
109mod foo;
110// src/foo.rs
111
112/* cursor here*/
113```
114
115`experimental/parentModule` returns a single `Link` to the `mod foo;` declaration.
116
117### Unresolved Question
118
119* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
120 This is the approach IntelliJ Rust is takeing.
121 However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
122 If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising.
123
124## Join Lines
125
126**Issue:** https://github.com/microsoft/language-server-protocol/issues/992
127
128**Server Capability:** `{ "joinLines": boolean }`
129
130This request is send from client to server to handle "Join Lines" editor action.
131
132**Method:** `experimental/joinLines`
133
134**Request:**
135
136```typescript
137interface JoinLinesParams {
138 textDocument: TextDocumentIdentifier,
139 /// Currently active selections/cursor offsets.
140 /// This is an array to support multiple cursors.
141 ranges: Range[],
142}
143```
144
145**Response:** `TextEdit[]`
146
147### Example
148
149```rust
150fn main() {
151 /*cursor here*/let x = {
152 92
153 };
154}
155```
156
157`experimental/joinLines` yields (curly braces are automagiacally removed)
158
159```rust
160fn main() {
161 let x = 92;
162}
163```
164
165### Unresolved Question
166
167* What is the position of the cursor after `joinLines`?
168 Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets.
169 However, it then becomes unclear how it works with multi cursor.
170
171## On Enter
172
173**Issue:** https://github.com/microsoft/language-server-protocol/issues/1001
174
175**Server Capability:** `{ "onEnter": boolean }`
176
177This request is send from client to server to handle <kbd>Enter</kbd> keypress.
178
179**Method:** `experimental/onEnter`
180
181**Request:**: `TextDocumentPositionParams`
182
183**Response:**
184
185```typescript
186SnippetTextEdit[]
187```
188
189### Example
190
191```rust
192fn main() {
193 // Some /*cursor here*/ docs
194 let x = 92;
195}
196```
197
198`experimental/onEnter` returns the following snippet
199
200```rust
201fn main() {
202 // Some
203 // $0 docs
204 let x = 92;
205}
206```
207
208The primary goal of `onEnter` is to handle automatic indentation when opening a new line.
209This is not yet implemented.
210The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals.
211
212As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTextEdit`.
213
214### Unresolved Question
215
216* How to deal with synchronicity of the request?
217 One option is to require the client to block until the server returns the response.
218 Another option is to do a OT-style merging of edits from client and server.
219 A third option is to do a record-replay: client applies heuristic on enter immediatelly, then applies all user's keypresses.
220 When the server is ready with the response, the client rollbacks all the changes and applies the recorded actions on top of the correct response.
221* How to deal with multiple carets?
222* Should we extend this to arbitrary typed events and not just `onEnter`?
223
224## Structural Search Replace (SSR)
225
226**Server Capability:** `{ "ssr": boolean }`
227
228This request is send from client to server to handle structural search replace -- automated syntax tree based transformation of the source.
229
230**Method:** `experimental/ssr`
231
232**Request:**
233
234```typescript
235interface SsrParams {
236 /// Search query.
237 /// The specific syntax is specified outside of the protocol.
238 query: string,
239 /// If true, only check the syntax of the query and don't compute the actual edit.
240 parseOnly: bool,
241}
242```
243
244**Response:**
245
246```typescript
247WorkspaceEdit
248```
249
250### Example
251
252SSR with query `foo($a:expr, $b:expr) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)` into `(y + 5).foo(z)`.
253
254### Unresolved Question
255
256* Probably needs search without replace mode
257* Needs a way to limit the scope to certain files.
258
259## Matching Brace
260
261**Issue:** https://github.com/microsoft/language-server-protocol/issues/999
262
263**Server Capability:** `{ "matchingBrace": boolean }`
264
265This request is send from client to server to handle "Matching Brace" editor action.
266
267**Method:** `experimental/matchingBrace`
268
269**Request:**
270
271```typescript
272interface MatchingBraceParams {
273 textDocument: TextDocumentIdentifier,
274 /// Position for each cursor
275 positions: Position[],
276}
277```
278
279**Response:**
280
281```typescript
282Position[]
283```
284
285### Example
286
287```rust
288fn main() {
289 let x: Vec<()>/*cursor here*/ = vec![]
290}
291```
292
293`experimental/matchingBrace` yields the position of `<`.
294In many cases, matching braces can be handled by the editor.
295However, some cases (like disambiguating between generics and comparison operations) need a real parser.
296Moreover, it would be cool if editors didn't need to implement even basic language parsing
297
298### Unresolved Question
299
300* Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
301 This is how `SelectionRange` request works.
302* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
303
304## Analyzer Status
305
306**Method:** `rust-analyzer/analyzerStatus`
307
308**Request:** `null`
309
310**Response:** `string`
311
312Returns internal status message, mostly for debugging purposes.
313
314## Collect Garbage
315
316**Method:** `rust-analyzer/collectGarbage`
317
318**Request:** `null`
319
320**Response:** `null`
321
322Frees some caches. For internal use, and is mostly broken at the moment.
323
324## Syntax Tree
325
326**Method:** `rust-analyzer/syntaxTree`
327
328**Request:**
329
330```typescript
331interface SyntaxTeeParams {
332 textDocument: TextDocumentIdentifier,
333 range?: Range,
334}
335```
336
337**Response:** `string`
338
339Returns textual representation of a parse tree for the file/selected region.
340Primarily for debugging, but very useful for all people working on rust-analyzer itself.
341
342## Expand Macro
343
344**Method:** `rust-analyzer/expandMacro`
345
346**Request:**
347
348```typescript
349interface ExpandMacroParams {
350 textDocument: TextDocumentIdentifier,
351 position: Position,
352}
353```
354
355**Response:**
356
357```typescript
358interface ExpandedMacro {
359 name: string,
360 expansion: string,
361}
362```
363
364Expands macro call at a given position.
diff --git a/docs/user/assists.md b/docs/user/assists.md
index 51807ffda..4ad7ea59d 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -426,7 +426,7 @@ fn main() {
426 426
427// AFTER 427// AFTER
428fn main() { 428fn main() {
429 let var_name = (1 + 2); 429 let $0var_name = (1 + 2);
430 var_name * 4; 430 var_name * 4;
431} 431}
432``` 432```
@@ -733,7 +733,7 @@ fn main() {
733 let x: Result<i32, i32> = Result::Ok(92); 733 let x: Result<i32, i32> = Result::Ok(92);
734 let y = match x { 734 let y = match x {
735 Ok(a) => a, 735 Ok(a) => a,
736 _ => unreachable!(), 736 $0_ => unreachable!(),
737 }; 737 };
738} 738}
739``` 739```
diff --git a/docs/user/features.md b/docs/user/features.md
index 340bce835..12ecdec13 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -93,6 +93,12 @@ Shows internal statistic about memory usage of rust-analyzer.
93 93
94Show current rust-analyzer version. 94Show current rust-analyzer version.
95 95
96#### Toggle inlay hints
97
98Toggle inlay hints view for the current workspace.
99It is recommended to assign a shortcut for this command to quickly turn off
100inlay hints when they prevent you from reading/writing the code.
101
96#### Run Garbage Collection 102#### Run Garbage Collection
97 103
98Manually triggers GC. 104Manually triggers GC.
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 03836e6e2..40ed54809 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -249,7 +249,7 @@ If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side
249 249
250If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary. 250If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.
251 251
252=== Gnome Builder 252=== GNOME Builder
253 253
254Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. 254Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
255 255
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index 257b744bf..7149ab799 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -1,5 +1,5 @@
1** 1**
2!out/main.js 2!out/src/main.js
3!package.json 3!package.json
4!package-lock.json 4!package-lock.json
5!ra_syntax_tree.tmGrammar.json 5!ra_syntax_tree.tmGrammar.json
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 06178990f..3b4a31682 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -13,21 +13,27 @@
13 "@babel/highlight": "^7.8.3" 13 "@babel/highlight": "^7.8.3"
14 } 14 }
15 }, 15 },
16 "@babel/helper-validator-identifier": {
17 "version": "7.9.5",
18 "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
19 "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
20 "dev": true
21 },
16 "@babel/highlight": { 22 "@babel/highlight": {
17 "version": "7.8.3", 23 "version": "7.9.0",
18 "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", 24 "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
19 "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", 25 "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
20 "dev": true, 26 "dev": true,
21 "requires": { 27 "requires": {
28 "@babel/helper-validator-identifier": "^7.9.0",
22 "chalk": "^2.0.0", 29 "chalk": "^2.0.0",
23 "esutils": "^2.0.2",
24 "js-tokens": "^4.0.0" 30 "js-tokens": "^4.0.0"
25 } 31 }
26 }, 32 },
27 "@rollup/plugin-commonjs": { 33 "@rollup/plugin-commonjs": {
28 "version": "11.1.0", 34 "version": "12.0.0",
29 "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz", 35 "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-12.0.0.tgz",
30 "integrity": "sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA==", 36 "integrity": "sha512-8+mDQt1QUmN+4Y9D3yCG8AJNewuTSLYPJVzKKUZ+lGeQrI+bV12Tc5HCyt2WdlnG6ihIL/DPbKRJlB40DX40mw==",
31 "dev": true, 37 "dev": true,
32 "requires": { 38 "requires": {
33 "@rollup/pluginutils": "^3.0.8", 39 "@rollup/pluginutils": "^3.0.8",
@@ -40,27 +46,29 @@
40 } 46 }
41 }, 47 },
42 "@rollup/plugin-node-resolve": { 48 "@rollup/plugin-node-resolve": {
43 "version": "7.1.3", 49 "version": "8.0.0",
44 "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz", 50 "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.0.0.tgz",
45 "integrity": "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==", 51 "integrity": "sha512-5poJCChrkVggXXND/sQ7yNqwjUNT4fP31gpRWCnSNnlXuUXTCMHT33xZrTGxgjm5Rl18MHj7iEzlCT8rYWwQSA==",
46 "dev": true, 52 "dev": true,
47 "requires": { 53 "requires": {
48 "@rollup/pluginutils": "^3.0.8", 54 "@rollup/pluginutils": "^3.0.8",
49 "@types/resolve": "0.0.8", 55 "@types/resolve": "0.0.8",
50 "builtin-modules": "^3.1.0", 56 "builtin-modules": "^3.1.0",
57 "deep-freeze": "^0.0.1",
58 "deepmerge": "^4.2.2",
51 "is-module": "^1.0.0", 59 "is-module": "^1.0.0",
52 "resolve": "^1.14.2" 60 "resolve": "^1.14.2"
53 } 61 }
54 }, 62 },
55 "@rollup/pluginutils": { 63 "@rollup/pluginutils": {
56 "version": "3.0.9", 64 "version": "3.0.10",
57 "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.9.tgz", 65 "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz",
58 "integrity": "sha512-TLZavlfPAZYI7v33wQh4mTP6zojne14yok3DNSLcjoG/Hirxfkonn6icP5rrNWRn8nZsirJBFFpijVOJzkUHDg==", 66 "integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==",
59 "dev": true, 67 "dev": true,
60 "requires": { 68 "requires": {
61 "@types/estree": "0.0.39", 69 "@types/estree": "0.0.39",
62 "estree-walker": "^1.0.1", 70 "estree-walker": "^1.0.1",
63 "micromatch": "^4.0.2" 71 "picomatch": "^2.2.2"
64 } 72 }
65 }, 73 },
66 "@types/color-name": { 74 "@types/color-name": {
@@ -81,16 +89,45 @@
81 "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 89 "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
82 "dev": true 90 "dev": true
83 }, 91 },
92 "@types/events": {
93 "version": "3.0.0",
94 "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
95 "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
96 "dev": true
97 },
98 "@types/glob": {
99 "version": "7.1.1",
100 "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
101 "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
102 "dev": true,
103 "requires": {
104 "@types/events": "*",
105 "@types/minimatch": "*",
106 "@types/node": "*"
107 }
108 },
84 "@types/json-schema": { 109 "@types/json-schema": {
85 "version": "7.0.4", 110 "version": "7.0.4",
86 "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", 111 "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
87 "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", 112 "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
88 "dev": true 113 "dev": true
89 }, 114 },
115 "@types/minimatch": {
116 "version": "3.0.3",
117 "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
118 "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
119 "dev": true
120 },
121 "@types/mocha": {
122 "version": "7.0.2",
123 "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz",
124 "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==",
125 "dev": true
126 },
90 "@types/node": { 127 "@types/node": {
91 "version": "12.12.39", 128 "version": "14.0.5",
92 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.39.tgz", 129 "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz",
93 "integrity": "sha512-pADGfwnDkr6zagDwEiCVE4yQrv7XDkoeVa4OfA9Ju/zRTk6YNDLGtQbkdL4/56mCQQCs4AhNrBIag6jrp7ZuOg==", 130 "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==",
94 "dev": true 131 "dev": true
95 }, 132 },
96 "@types/node-fetch": { 133 "@types/node-fetch": {
@@ -119,56 +156,54 @@
119 "dev": true 156 "dev": true
120 }, 157 },
121 "@typescript-eslint/eslint-plugin": { 158 "@typescript-eslint/eslint-plugin": {
122 "version": "2.33.0", 159 "version": "3.0.0",
123 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.33.0.tgz", 160 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.0.0.tgz",
124 "integrity": "sha512-QV6P32Btu1sCI/kTqjTNI/8OpCYyvlGjW5vD8MpTIg+HGE5S88HtT1G+880M4bXlvXj/NjsJJG0aGcVh0DdbeQ==", 161 "integrity": "sha512-lcZ0M6jD4cqGccYOERKdMtg+VWpoq3NSnWVxpc/AwAy0zhkUYVioOUZmfNqiNH8/eBNGhCn6HXd6mKIGRgNc1Q==",
125 "dev": true, 162 "dev": true,
126 "requires": { 163 "requires": {
127 "@typescript-eslint/experimental-utils": "2.33.0", 164 "@typescript-eslint/experimental-utils": "3.0.0",
128 "functional-red-black-tree": "^1.0.1", 165 "functional-red-black-tree": "^1.0.1",
129 "regexpp": "^3.0.0", 166 "regexpp": "^3.0.0",
167 "semver": "^7.3.2",
130 "tsutils": "^3.17.1" 168 "tsutils": "^3.17.1"
169 },
170 "dependencies": {
171 "semver": {
172 "version": "7.3.2",
173 "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
174 "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
175 "dev": true
176 }
131 } 177 }
132 }, 178 },
133 "@typescript-eslint/experimental-utils": { 179 "@typescript-eslint/experimental-utils": {
134 "version": "2.33.0", 180 "version": "3.0.0",
135 "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.33.0.tgz", 181 "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.0.0.tgz",
136 "integrity": "sha512-qzPM2AuxtMrRq78LwyZa8Qn6gcY8obkIrBs1ehqmQADwkYzTE1Pb4y2W+U3rE/iFkSWcWHG2LS6MJfj6SmHApg==", 182 "integrity": "sha512-BN0vmr9N79M9s2ctITtChRuP1+Dls0x/wlg0RXW1yQ7WJKPurg6X3Xirv61J2sjPif4F8SLsFMs5Nzte0WYoTQ==",
137 "dev": true, 183 "dev": true,
138 "requires": { 184 "requires": {
139 "@types/json-schema": "^7.0.3", 185 "@types/json-schema": "^7.0.3",
140 "@typescript-eslint/typescript-estree": "2.33.0", 186 "@typescript-eslint/typescript-estree": "3.0.0",
141 "eslint-scope": "^5.0.0", 187 "eslint-scope": "^5.0.0",
142 "eslint-utils": "^2.0.0" 188 "eslint-utils": "^2.0.0"
143 },
144 "dependencies": {
145 "eslint-utils": {
146 "version": "2.0.0",
147 "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
148 "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
149 "dev": true,
150 "requires": {
151 "eslint-visitor-keys": "^1.1.0"
152 }
153 }
154 } 189 }
155 }, 190 },
156 "@typescript-eslint/parser": { 191 "@typescript-eslint/parser": {
157 "version": "2.33.0", 192 "version": "3.0.0",
158 "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.33.0.tgz", 193 "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.0.0.tgz",
159 "integrity": "sha512-AUtmwUUhJoH6yrtxZMHbRUEMsC2G6z5NSxg9KsROOGqNXasM71I8P2NihtumlWTUCRld70vqIZ6Pm4E5PAziEA==", 194 "integrity": "sha512-8RRCA9KLxoFNO0mQlrLZA0reGPd/MsobxZS/yPFj+0/XgMdS8+mO8mF3BDj2ZYQj03rkayhSJtF1HAohQ3iylw==",
160 "dev": true, 195 "dev": true,
161 "requires": { 196 "requires": {
162 "@types/eslint-visitor-keys": "^1.0.0", 197 "@types/eslint-visitor-keys": "^1.0.0",
163 "@typescript-eslint/experimental-utils": "2.33.0", 198 "@typescript-eslint/experimental-utils": "3.0.0",
164 "@typescript-eslint/typescript-estree": "2.33.0", 199 "@typescript-eslint/typescript-estree": "3.0.0",
165 "eslint-visitor-keys": "^1.1.0" 200 "eslint-visitor-keys": "^1.1.0"
166 } 201 }
167 }, 202 },
168 "@typescript-eslint/typescript-estree": { 203 "@typescript-eslint/typescript-estree": {
169 "version": "2.33.0", 204 "version": "3.0.0",
170 "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.33.0.tgz", 205 "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.0.0.tgz",
171 "integrity": "sha512-d8rY6/yUxb0+mEwTShCQF2zYQdLlqihukNfG9IUlLYz5y1CH6G/9XYbrxQLq3Z14RNvkCC6oe+OcFlyUpwUbkg==", 206 "integrity": "sha512-nevQvHyNghsfLrrByzVIH4ZG3NROgJ8LZlfh3ddwPPH4CH7W4GAiSx5qu+xHuX5pWsq6q/eqMc1io840ZhAnUg==",
172 "dev": true, 207 "dev": true,
173 "requires": { 208 "requires": {
174 "debug": "^4.1.1", 209 "debug": "^4.1.1",
@@ -189,9 +224,9 @@
189 } 224 }
190 }, 225 },
191 "acorn": { 226 "acorn": {
192 "version": "7.1.1", 227 "version": "7.2.0",
193 "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", 228 "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
194 "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", 229 "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==",
195 "dev": true 230 "dev": true
196 }, 231 },
197 "acorn-jsx": { 232 "acorn-jsx": {
@@ -200,10 +235,19 @@
200 "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", 235 "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
201 "dev": true 236 "dev": true
202 }, 237 },
238 "agent-base": {
239 "version": "4.3.0",
240 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
241 "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
242 "dev": true,
243 "requires": {
244 "es6-promisify": "^5.0.0"
245 }
246 },
203 "ajv": { 247 "ajv": {
204 "version": "6.12.0", 248 "version": "6.12.2",
205 "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", 249 "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
206 "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", 250 "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
207 "dev": true, 251 "dev": true,
208 "requires": { 252 "requires": {
209 "fast-deep-equal": "^3.1.1", 253 "fast-deep-equal": "^3.1.1",
@@ -212,6 +256,12 @@
212 "uri-js": "^4.2.2" 256 "uri-js": "^4.2.2"
213 } 257 }
214 }, 258 },
259 "ansi-colors": {
260 "version": "3.2.3",
261 "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
262 "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
263 "dev": true
264 },
215 "ansi-escapes": { 265 "ansi-escapes": {
216 "version": "4.3.1", 266 "version": "4.3.1",
217 "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", 267 "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
@@ -244,6 +294,16 @@
244 "color-convert": "^1.9.0" 294 "color-convert": "^1.9.0"
245 } 295 }
246 }, 296 },
297 "anymatch": {
298 "version": "3.1.1",
299 "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
300 "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
301 "dev": true,
302 "requires": {
303 "normalize-path": "^3.0.0",
304 "picomatch": "^2.0.4"
305 }
306 },
247 "argparse": { 307 "argparse": {
248 "version": "1.0.10", 308 "version": "1.0.10",
249 "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 309 "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -283,6 +343,12 @@
283 "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 343 "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
284 "dev": true 344 "dev": true
285 }, 345 },
346 "binary-extensions": {
347 "version": "2.0.0",
348 "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
349 "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
350 "dev": true
351 },
286 "boolbase": { 352 "boolbase": {
287 "version": "1.0.0", 353 "version": "1.0.0",
288 "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 354 "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -308,6 +374,12 @@
308 "fill-range": "^7.0.1" 374 "fill-range": "^7.0.1"
309 } 375 }
310 }, 376 },
377 "browser-stdout": {
378 "version": "1.3.1",
379 "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
380 "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
381 "dev": true
382 },
311 "buffer-crc32": { 383 "buffer-crc32": {
312 "version": "0.2.13", 384 "version": "0.2.13",
313 "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 385 "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -326,6 +398,12 @@
326 "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 398 "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
327 "dev": true 399 "dev": true
328 }, 400 },
401 "camelcase": {
402 "version": "5.3.1",
403 "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
404 "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
405 "dev": true
406 },
329 "chalk": { 407 "chalk": {
330 "version": "2.4.2", 408 "version": "2.4.2",
331 "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 409 "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -357,6 +435,22 @@
357 "parse5": "^3.0.1" 435 "parse5": "^3.0.1"
358 } 436 }
359 }, 437 },
438 "chokidar": {
439 "version": "3.3.0",
440 "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
441 "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
442 "dev": true,
443 "requires": {
444 "anymatch": "~3.1.1",
445 "braces": "~3.0.2",
446 "fsevents": "~2.1.1",
447 "glob-parent": "~5.1.0",
448 "is-binary-path": "~2.1.0",
449 "is-glob": "~4.0.1",
450 "normalize-path": "~3.0.0",
451 "readdirp": "~3.2.0"
452 }
453 },
360 "cli-cursor": { 454 "cli-cursor": {
361 "version": "3.1.0", 455 "version": "3.1.0",
362 "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", 456 "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -367,11 +461,62 @@
367 } 461 }
368 }, 462 },
369 "cli-width": { 463 "cli-width": {
370 "version": "2.2.0", 464 "version": "2.2.1",
371 "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 465 "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
372 "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 466 "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
373 "dev": true 467 "dev": true
374 }, 468 },
469 "cliui": {
470 "version": "5.0.0",
471 "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
472 "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
473 "dev": true,
474 "requires": {
475 "string-width": "^3.1.0",
476 "strip-ansi": "^5.2.0",
477 "wrap-ansi": "^5.1.0"
478 },
479 "dependencies": {
480 "ansi-regex": {
481 "version": "4.1.0",
482 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
483 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
484 "dev": true
485 },
486 "emoji-regex": {
487 "version": "7.0.3",
488 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
489 "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
490 "dev": true
491 },
492 "is-fullwidth-code-point": {
493 "version": "2.0.0",
494 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
495 "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
496 "dev": true
497 },
498 "string-width": {
499 "version": "3.1.0",
500 "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
501 "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
502 "dev": true,
503 "requires": {
504 "emoji-regex": "^7.0.1",
505 "is-fullwidth-code-point": "^2.0.0",
506 "strip-ansi": "^5.1.0"
507 }
508 },
509 "strip-ansi": {
510 "version": "5.2.0",
511 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
512 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
513 "dev": true,
514 "requires": {
515 "ansi-regex": "^4.1.0"
516 }
517 }
518 }
519 },
375 "color-convert": { 520 "color-convert": {
376 "version": "1.9.3", 521 "version": "1.9.3",
377 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 522 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -421,24 +566,14 @@
421 "dev": true 566 "dev": true
422 }, 567 },
423 "cross-spawn": { 568 "cross-spawn": {
424 "version": "6.0.5", 569 "version": "7.0.2",
425 "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 570 "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
426 "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 571 "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
427 "dev": true, 572 "dev": true,
428 "requires": { 573 "requires": {
429 "nice-try": "^1.0.4", 574 "path-key": "^3.1.0",
430 "path-key": "^2.0.1", 575 "shebang-command": "^2.0.0",
431 "semver": "^5.5.0", 576 "which": "^2.0.1"
432 "shebang-command": "^1.2.0",
433 "which": "^1.2.9"
434 },
435 "dependencies": {
436 "semver": {
437 "version": "5.7.1",
438 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
439 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
440 "dev": true
441 }
442 } 577 }
443 }, 578 },
444 "css-select": { 579 "css-select": {
@@ -468,12 +603,39 @@
468 "ms": "^2.1.1" 603 "ms": "^2.1.1"
469 } 604 }
470 }, 605 },
606 "decamelize": {
607 "version": "1.2.0",
608 "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
609 "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
610 "dev": true
611 },
612 "deep-freeze": {
613 "version": "0.0.1",
614 "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz",
615 "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=",
616 "dev": true
617 },
471 "deep-is": { 618 "deep-is": {
472 "version": "0.1.3", 619 "version": "0.1.3",
473 "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 620 "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
474 "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 621 "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
475 "dev": true 622 "dev": true
476 }, 623 },
624 "deepmerge": {
625 "version": "4.2.2",
626 "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
627 "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
628 "dev": true
629 },
630 "define-properties": {
631 "version": "1.1.3",
632 "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
633 "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
634 "dev": true,
635 "requires": {
636 "object-keys": "^1.0.12"
637 }
638 },
477 "delayed-stream": { 639 "delayed-stream": {
478 "version": "1.0.0", 640 "version": "1.0.0",
479 "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 641 "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -486,6 +648,12 @@
486 "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=", 648 "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=",
487 "dev": true 649 "dev": true
488 }, 650 },
651 "diff": {
652 "version": "3.5.0",
653 "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
654 "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
655 "dev": true
656 },
489 "doctrine": { 657 "doctrine": {
490 "version": "3.0.0", 658 "version": "3.0.0",
491 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 659 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -562,6 +730,51 @@
562 "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", 730 "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
563 "dev": true 731 "dev": true
564 }, 732 },
733 "es-abstract": {
734 "version": "1.17.5",
735 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
736 "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
737 "dev": true,
738 "requires": {
739 "es-to-primitive": "^1.2.1",
740 "function-bind": "^1.1.1",
741 "has": "^1.0.3",
742 "has-symbols": "^1.0.1",
743 "is-callable": "^1.1.5",
744 "is-regex": "^1.0.5",
745 "object-inspect": "^1.7.0",
746 "object-keys": "^1.1.1",
747 "object.assign": "^4.1.0",
748 "string.prototype.trimleft": "^2.1.1",
749 "string.prototype.trimright": "^2.1.1"
750 }
751 },
752 "es-to-primitive": {
753 "version": "1.2.1",
754 "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
755 "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
756 "dev": true,
757 "requires": {
758 "is-callable": "^1.1.4",
759 "is-date-object": "^1.0.1",
760 "is-symbol": "^1.0.2"
761 }
762 },
763 "es6-promise": {
764 "version": "4.2.8",
765 "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
766 "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
767 "dev": true
768 },
769 "es6-promisify": {
770 "version": "5.0.0",
771 "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
772 "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
773 "dev": true,
774 "requires": {
775 "es6-promise": "^4.0.3"
776 }
777 },
565 "escape-string-regexp": { 778 "escape-string-regexp": {
566 "version": "1.0.5", 779 "version": "1.0.5",
567 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 780 "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -569,22 +782,22 @@
569 "dev": true 782 "dev": true
570 }, 783 },
571 "eslint": { 784 "eslint": {
572 "version": "6.8.0", 785 "version": "7.1.0",
573 "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", 786 "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.1.0.tgz",
574 "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", 787 "integrity": "sha512-DfS3b8iHMK5z/YLSme8K5cge168I8j8o1uiVmFCgnnjxZQbCGyraF8bMl7Ju4yfBmCuxD7shOF7eqGkcuIHfsA==",
575 "dev": true, 788 "dev": true,
576 "requires": { 789 "requires": {
577 "@babel/code-frame": "^7.0.0", 790 "@babel/code-frame": "^7.0.0",
578 "ajv": "^6.10.0", 791 "ajv": "^6.10.0",
579 "chalk": "^2.1.0", 792 "chalk": "^4.0.0",
580 "cross-spawn": "^6.0.5", 793 "cross-spawn": "^7.0.2",
581 "debug": "^4.0.1", 794 "debug": "^4.0.1",
582 "doctrine": "^3.0.0", 795 "doctrine": "^3.0.0",
583 "eslint-scope": "^5.0.0", 796 "eslint-scope": "^5.0.0",
584 "eslint-utils": "^1.4.3", 797 "eslint-utils": "^2.0.0",
585 "eslint-visitor-keys": "^1.1.0", 798 "eslint-visitor-keys": "^1.1.0",
586 "espree": "^6.1.2", 799 "espree": "^7.0.0",
587 "esquery": "^1.0.1", 800 "esquery": "^1.2.0",
588 "esutils": "^2.0.2", 801 "esutils": "^2.0.2",
589 "file-entry-cache": "^5.0.1", 802 "file-entry-cache": "^5.0.1",
590 "functional-red-black-tree": "^1.0.1", 803 "functional-red-black-tree": "^1.0.1",
@@ -597,27 +810,76 @@
597 "is-glob": "^4.0.0", 810 "is-glob": "^4.0.0",
598 "js-yaml": "^3.13.1", 811 "js-yaml": "^3.13.1",
599 "json-stable-stringify-without-jsonify": "^1.0.1", 812 "json-stable-stringify-without-jsonify": "^1.0.1",
600 "levn": "^0.3.0", 813 "levn": "^0.4.1",
601 "lodash": "^4.17.14", 814 "lodash": "^4.17.14",
602 "minimatch": "^3.0.4", 815 "minimatch": "^3.0.4",
603 "mkdirp": "^0.5.1",
604 "natural-compare": "^1.4.0", 816 "natural-compare": "^1.4.0",
605 "optionator": "^0.8.3", 817 "optionator": "^0.9.1",
606 "progress": "^2.0.0", 818 "progress": "^2.0.0",
607 "regexpp": "^2.0.1", 819 "regexpp": "^3.1.0",
608 "semver": "^6.1.2", 820 "semver": "^7.2.1",
609 "strip-ansi": "^5.2.0", 821 "strip-ansi": "^6.0.0",
610 "strip-json-comments": "^3.0.1", 822 "strip-json-comments": "^3.1.0",
611 "table": "^5.2.3", 823 "table": "^5.2.3",
612 "text-table": "^0.2.0", 824 "text-table": "^0.2.0",
613 "v8-compile-cache": "^2.0.3" 825 "v8-compile-cache": "^2.0.3"
614 }, 826 },
615 "dependencies": { 827 "dependencies": {
616 "regexpp": { 828 "ansi-styles": {
829 "version": "4.2.1",
830 "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
831 "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
832 "dev": true,
833 "requires": {
834 "@types/color-name": "^1.1.1",
835 "color-convert": "^2.0.1"
836 }
837 },
838 "chalk": {
839 "version": "4.0.0",
840 "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
841 "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
842 "dev": true,
843 "requires": {
844 "ansi-styles": "^4.1.0",
845 "supports-color": "^7.1.0"
846 }
847 },
848 "color-convert": {
617 "version": "2.0.1", 849 "version": "2.0.1",
618 "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", 850 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
619 "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", 851 "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
852 "dev": true,
853 "requires": {
854 "color-name": "~1.1.4"
855 }
856 },
857 "color-name": {
858 "version": "1.1.4",
859 "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
860 "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
861 "dev": true
862 },
863 "has-flag": {
864 "version": "4.0.0",
865 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
866 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
867 "dev": true
868 },
869 "semver": {
870 "version": "7.3.2",
871 "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
872 "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
620 "dev": true 873 "dev": true
874 },
875 "supports-color": {
876 "version": "7.1.0",
877 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
878 "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
879 "dev": true,
880 "requires": {
881 "has-flag": "^4.0.0"
882 }
621 } 883 }
622 } 884 }
623 }, 885 },
@@ -632,9 +894,9 @@
632 } 894 }
633 }, 895 },
634 "eslint-utils": { 896 "eslint-utils": {
635 "version": "1.4.3", 897 "version": "2.0.0",
636 "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", 898 "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
637 "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", 899 "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
638 "dev": true, 900 "dev": true,
639 "requires": { 901 "requires": {
640 "eslint-visitor-keys": "^1.1.0" 902 "eslint-visitor-keys": "^1.1.0"
@@ -647,9 +909,9 @@
647 "dev": true 909 "dev": true
648 }, 910 },
649 "espree": { 911 "espree": {
650 "version": "6.2.1", 912 "version": "7.0.0",
651 "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", 913 "resolved": "https://registry.npmjs.org/espree/-/espree-7.0.0.tgz",
652 "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", 914 "integrity": "sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==",
653 "dev": true, 915 "dev": true,
654 "requires": { 916 "requires": {
655 "acorn": "^7.1.1", 917 "acorn": "^7.1.1",
@@ -664,12 +926,20 @@
664 "dev": true 926 "dev": true
665 }, 927 },
666 "esquery": { 928 "esquery": {
667 "version": "1.1.0", 929 "version": "1.3.1",
668 "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", 930 "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
669 "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", 931 "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
670 "dev": true, 932 "dev": true,
671 "requires": { 933 "requires": {
672 "estraverse": "^4.0.0" 934 "estraverse": "^5.1.0"
935 },
936 "dependencies": {
937 "estraverse": {
938 "version": "5.1.0",
939 "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz",
940 "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==",
941 "dev": true
942 }
673 } 943 }
674 }, 944 },
675 "esrecurse": { 945 "esrecurse": {
@@ -764,6 +1034,24 @@
764 "to-regex-range": "^5.0.1" 1034 "to-regex-range": "^5.0.1"
765 } 1035 }
766 }, 1036 },
1037 "find-up": {
1038 "version": "3.0.0",
1039 "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
1040 "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
1041 "dev": true,
1042 "requires": {
1043 "locate-path": "^3.0.0"
1044 }
1045 },
1046 "flat": {
1047 "version": "4.1.0",
1048 "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
1049 "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
1050 "dev": true,
1051 "requires": {
1052 "is-buffer": "~2.0.3"
1053 }
1054 },
767 "flat-cache": { 1055 "flat-cache": {
768 "version": "2.0.1", 1056 "version": "2.0.1",
769 "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 1057 "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@@ -776,9 +1064,9 @@
776 } 1064 }
777 }, 1065 },
778 "flatted": { 1066 "flatted": {
779 "version": "2.0.1", 1067 "version": "2.0.2",
780 "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", 1068 "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
781 "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", 1069 "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
782 "dev": true 1070 "dev": true
783 }, 1071 },
784 "form-data": { 1072 "form-data": {
@@ -805,12 +1093,24 @@
805 "dev": true, 1093 "dev": true,
806 "optional": true 1094 "optional": true
807 }, 1095 },
1096 "function-bind": {
1097 "version": "1.1.1",
1098 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
1099 "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
1100 "dev": true
1101 },
808 "functional-red-black-tree": { 1102 "functional-red-black-tree": {
809 "version": "1.0.1", 1103 "version": "1.0.1",
810 "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 1104 "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
811 "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 1105 "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
812 "dev": true 1106 "dev": true
813 }, 1107 },
1108 "get-caller-file": {
1109 "version": "2.0.5",
1110 "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
1111 "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
1112 "dev": true
1113 },
814 "glob": { 1114 "glob": {
815 "version": "7.1.6", 1115 "version": "7.1.6",
816 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 1116 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -826,9 +1126,9 @@
826 } 1126 }
827 }, 1127 },
828 "glob-parent": { 1128 "glob-parent": {
829 "version": "5.1.0", 1129 "version": "5.1.1",
830 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", 1130 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
831 "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", 1131 "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
832 "dev": true, 1132 "dev": true,
833 "requires": { 1133 "requires": {
834 "is-glob": "^4.0.1" 1134 "is-glob": "^4.0.1"
@@ -843,12 +1143,39 @@
843 "type-fest": "^0.8.1" 1143 "type-fest": "^0.8.1"
844 } 1144 }
845 }, 1145 },
1146 "growl": {
1147 "version": "1.10.5",
1148 "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
1149 "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
1150 "dev": true
1151 },
1152 "has": {
1153 "version": "1.0.3",
1154 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
1155 "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
1156 "dev": true,
1157 "requires": {
1158 "function-bind": "^1.1.1"
1159 }
1160 },
846 "has-flag": { 1161 "has-flag": {
847 "version": "3.0.0", 1162 "version": "3.0.0",
848 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1163 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
849 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1164 "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
850 "dev": true 1165 "dev": true
851 }, 1166 },
1167 "has-symbols": {
1168 "version": "1.0.1",
1169 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
1170 "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
1171 "dev": true
1172 },
1173 "he": {
1174 "version": "1.2.0",
1175 "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
1176 "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
1177 "dev": true
1178 },
852 "htmlparser2": { 1179 "htmlparser2": {
853 "version": "3.10.1", 1180 "version": "3.10.1",
854 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 1181 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -863,6 +1190,54 @@
863 "readable-stream": "^3.1.1" 1190 "readable-stream": "^3.1.1"
864 } 1191 }
865 }, 1192 },
1193 "http-proxy-agent": {
1194 "version": "2.1.0",
1195 "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
1196 "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
1197 "dev": true,
1198 "requires": {
1199 "agent-base": "4",
1200 "debug": "3.1.0"
1201 },
1202 "dependencies": {
1203 "debug": {
1204 "version": "3.1.0",
1205 "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
1206 "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
1207 "dev": true,
1208 "requires": {
1209 "ms": "2.0.0"
1210 }
1211 },
1212 "ms": {
1213 "version": "2.0.0",
1214 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1215 "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
1216 "dev": true
1217 }
1218 }
1219 },
1220 "https-proxy-agent": {
1221 "version": "2.2.4",
1222 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
1223 "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
1224 "dev": true,
1225 "requires": {
1226 "agent-base": "^4.3.0",
1227 "debug": "^3.1.0"
1228 },
1229 "dependencies": {
1230 "debug": {
1231 "version": "3.2.6",
1232 "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
1233 "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
1234 "dev": true,
1235 "requires": {
1236 "ms": "^2.1.1"
1237 }
1238 }
1239 }
1240 },
866 "iconv-lite": { 1241 "iconv-lite": {
867 "version": "0.4.24", 1242 "version": "0.4.24",
868 "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1243 "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -972,15 +1347,6 @@
972 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1347 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
973 "dev": true 1348 "dev": true
974 }, 1349 },
975 "strip-ansi": {
976 "version": "6.0.0",
977 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
978 "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
979 "dev": true,
980 "requires": {
981 "ansi-regex": "^5.0.0"
982 }
983 },
984 "supports-color": { 1350 "supports-color": {
985 "version": "7.1.0", 1351 "version": "7.1.0",
986 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 1352 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -992,6 +1358,33 @@
992 } 1358 }
993 } 1359 }
994 }, 1360 },
1361 "is-binary-path": {
1362 "version": "2.1.0",
1363 "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1364 "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1365 "dev": true,
1366 "requires": {
1367 "binary-extensions": "^2.0.0"
1368 }
1369 },
1370 "is-buffer": {
1371 "version": "2.0.4",
1372 "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
1373 "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
1374 "dev": true
1375 },
1376 "is-callable": {
1377 "version": "1.1.5",
1378 "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
1379 "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
1380 "dev": true
1381 },
1382 "is-date-object": {
1383 "version": "1.0.2",
1384 "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
1385 "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
1386 "dev": true
1387 },
995 "is-extglob": { 1388 "is-extglob": {
996 "version": "2.1.1", 1389 "version": "2.1.1",
997 "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1390 "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1025,12 +1418,6 @@
1025 "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1418 "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1026 "dev": true 1419 "dev": true
1027 }, 1420 },
1028 "is-promise": {
1029 "version": "2.1.0",
1030 "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
1031 "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
1032 "dev": true
1033 },
1034 "is-reference": { 1421 "is-reference": {
1035 "version": "1.1.4", 1422 "version": "1.1.4",
1036 "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz", 1423 "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz",
@@ -1040,6 +1427,24 @@
1040 "@types/estree": "0.0.39" 1427 "@types/estree": "0.0.39"
1041 } 1428 }
1042 }, 1429 },
1430 "is-regex": {
1431 "version": "1.0.5",
1432 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
1433 "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
1434 "dev": true,
1435 "requires": {
1436 "has": "^1.0.3"
1437 }
1438 },
1439 "is-symbol": {
1440 "version": "1.0.3",
1441 "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
1442 "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
1443 "dev": true,
1444 "requires": {
1445 "has-symbols": "^1.0.1"
1446 }
1447 },
1043 "isexe": { 1448 "isexe": {
1044 "version": "2.0.0", 1449 "version": "2.0.0",
1045 "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1450 "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1053,9 +1458,9 @@
1053 "dev": true 1458 "dev": true
1054 }, 1459 },
1055 "js-yaml": { 1460 "js-yaml": {
1056 "version": "3.13.1", 1461 "version": "3.14.0",
1057 "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1462 "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
1058 "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1463 "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
1059 "dev": true, 1464 "dev": true,
1060 "requires": { 1465 "requires": {
1061 "argparse": "^1.0.7", 1466 "argparse": "^1.0.7",
@@ -1081,13 +1486,13 @@
1081 "dev": true 1486 "dev": true
1082 }, 1487 },
1083 "levn": { 1488 "levn": {
1084 "version": "0.3.0", 1489 "version": "0.4.1",
1085 "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1490 "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
1086 "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 1491 "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
1087 "dev": true, 1492 "dev": true,
1088 "requires": { 1493 "requires": {
1089 "prelude-ls": "~1.1.2", 1494 "prelude-ls": "^1.2.1",
1090 "type-check": "~0.3.2" 1495 "type-check": "~0.4.0"
1091 } 1496 }
1092 }, 1497 },
1093 "linkify-it": { 1498 "linkify-it": {
@@ -1099,12 +1504,31 @@
1099 "uc.micro": "^1.0.1" 1504 "uc.micro": "^1.0.1"
1100 } 1505 }
1101 }, 1506 },
1507 "locate-path": {
1508 "version": "3.0.0",
1509 "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
1510 "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
1511 "dev": true,
1512 "requires": {
1513 "p-locate": "^3.0.0",
1514 "path-exists": "^3.0.0"
1515 }
1516 },
1102 "lodash": { 1517 "lodash": {
1103 "version": "4.17.15", 1518 "version": "4.17.15",
1104 "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 1519 "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
1105 "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 1520 "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
1106 "dev": true 1521 "dev": true
1107 }, 1522 },
1523 "log-symbols": {
1524 "version": "3.0.0",
1525 "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
1526 "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
1527 "dev": true,
1528 "requires": {
1529 "chalk": "^2.4.2"
1530 }
1531 },
1108 "lru-cache": { 1532 "lru-cache": {
1109 "version": "4.1.5", 1533 "version": "4.1.5",
1110 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 1534 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -1151,16 +1575,6 @@
1151 "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", 1575 "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
1152 "dev": true 1576 "dev": true
1153 }, 1577 },
1154 "micromatch": {
1155 "version": "4.0.2",
1156 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
1157 "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
1158 "dev": true,
1159 "requires": {
1160 "braces": "^3.0.1",
1161 "picomatch": "^2.0.5"
1162 }
1163 },
1164 "mime": { 1578 "mime": {
1165 "version": "1.6.0", 1579 "version": "1.6.0",
1166 "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1580 "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -1204,14 +1618,111 @@
1204 "dev": true 1618 "dev": true
1205 }, 1619 },
1206 "mkdirp": { 1620 "mkdirp": {
1207 "version": "0.5.3", 1621 "version": "0.5.5",
1208 "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", 1622 "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
1209 "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", 1623 "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
1210 "dev": true, 1624 "dev": true,
1211 "requires": { 1625 "requires": {
1212 "minimist": "^1.2.5" 1626 "minimist": "^1.2.5"
1213 } 1627 }
1214 }, 1628 },
1629 "mocha": {
1630 "version": "7.2.0",
1631 "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz",
1632 "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==",
1633 "dev": true,
1634 "requires": {
1635 "ansi-colors": "3.2.3",
1636 "browser-stdout": "1.3.1",
1637 "chokidar": "3.3.0",
1638 "debug": "3.2.6",
1639 "diff": "3.5.0",
1640 "escape-string-regexp": "1.0.5",
1641 "find-up": "3.0.0",
1642 "glob": "7.1.3",
1643 "growl": "1.10.5",
1644 "he": "1.2.0",
1645 "js-yaml": "3.13.1",
1646 "log-symbols": "3.0.0",
1647 "minimatch": "3.0.4",
1648 "mkdirp": "0.5.5",
1649 "ms": "2.1.1",
1650 "node-environment-flags": "1.0.6",
1651 "object.assign": "4.1.0",
1652 "strip-json-comments": "2.0.1",
1653 "supports-color": "6.0.0",
1654 "which": "1.3.1",
1655 "wide-align": "1.1.3",
1656 "yargs": "13.3.2",
1657 "yargs-parser": "13.1.2",
1658 "yargs-unparser": "1.6.0"
1659 },
1660 "dependencies": {
1661 "debug": {
1662 "version": "3.2.6",
1663 "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
1664 "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
1665 "dev": true,
1666 "requires": {
1667 "ms": "^2.1.1"
1668 }
1669 },
1670 "glob": {
1671 "version": "7.1.3",
1672 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
1673 "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
1674 "dev": true,
1675 "requires": {
1676 "fs.realpath": "^1.0.0",
1677 "inflight": "^1.0.4",
1678 "inherits": "2",
1679 "minimatch": "^3.0.4",
1680 "once": "^1.3.0",
1681 "path-is-absolute": "^1.0.0"
1682 }
1683 },
1684 "js-yaml": {
1685 "version": "3.13.1",
1686 "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
1687 "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
1688 "dev": true,
1689 "requires": {
1690 "argparse": "^1.0.7",
1691 "esprima": "^4.0.0"
1692 }
1693 },
1694 "ms": {
1695 "version": "2.1.1",
1696 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
1697 "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
1698 "dev": true
1699 },
1700 "strip-json-comments": {
1701 "version": "2.0.1",
1702 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
1703 "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
1704 "dev": true
1705 },
1706 "supports-color": {
1707 "version": "6.0.0",
1708 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
1709 "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
1710 "dev": true,
1711 "requires": {
1712 "has-flag": "^3.0.0"
1713 }
1714 },
1715 "which": {
1716 "version": "1.3.1",
1717 "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1718 "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1719 "dev": true,
1720 "requires": {
1721 "isexe": "^2.0.0"
1722 }
1723 }
1724 }
1725 },
1215 "ms": { 1726 "ms": {
1216 "version": "2.1.2", 1727 "version": "2.1.2",
1217 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1728 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -1230,17 +1741,35 @@
1230 "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1741 "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
1231 "dev": true 1742 "dev": true
1232 }, 1743 },
1233 "nice-try": { 1744 "node-environment-flags": {
1234 "version": "1.0.5", 1745 "version": "1.0.6",
1235 "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 1746 "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
1236 "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 1747 "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
1237 "dev": true 1748 "dev": true,
1749 "requires": {
1750 "object.getownpropertydescriptors": "^2.0.3",
1751 "semver": "^5.7.0"
1752 },
1753 "dependencies": {
1754 "semver": {
1755 "version": "5.7.1",
1756 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1757 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1758 "dev": true
1759 }
1760 }
1238 }, 1761 },
1239 "node-fetch": { 1762 "node-fetch": {
1240 "version": "2.6.0", 1763 "version": "2.6.0",
1241 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", 1764 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
1242 "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" 1765 "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
1243 }, 1766 },
1767 "normalize-path": {
1768 "version": "3.0.0",
1769 "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1770 "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1771 "dev": true
1772 },
1244 "nth-check": { 1773 "nth-check": {
1245 "version": "1.0.2", 1774 "version": "1.0.2",
1246 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 1775 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
@@ -1250,6 +1779,40 @@
1250 "boolbase": "~1.0.0" 1779 "boolbase": "~1.0.0"
1251 } 1780 }
1252 }, 1781 },
1782 "object-inspect": {
1783 "version": "1.7.0",
1784 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
1785 "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
1786 "dev": true
1787 },
1788 "object-keys": {
1789 "version": "1.1.1",
1790 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
1791 "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
1792 "dev": true
1793 },
1794 "object.assign": {
1795 "version": "4.1.0",
1796 "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
1797 "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
1798 "dev": true,
1799 "requires": {
1800 "define-properties": "^1.1.2",
1801 "function-bind": "^1.1.1",
1802 "has-symbols": "^1.0.0",
1803 "object-keys": "^1.0.11"
1804 }
1805 },
1806 "object.getownpropertydescriptors": {
1807 "version": "2.1.0",
1808 "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
1809 "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
1810 "dev": true,
1811 "requires": {
1812 "define-properties": "^1.1.3",
1813 "es-abstract": "^1.17.0-next.1"
1814 }
1815 },
1253 "once": { 1816 "once": {
1254 "version": "1.4.0", 1817 "version": "1.4.0",
1255 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1818 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1269,17 +1832,17 @@
1269 } 1832 }
1270 }, 1833 },
1271 "optionator": { 1834 "optionator": {
1272 "version": "0.8.3", 1835 "version": "0.9.1",
1273 "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", 1836 "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
1274 "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", 1837 "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
1275 "dev": true, 1838 "dev": true,
1276 "requires": { 1839 "requires": {
1277 "deep-is": "~0.1.3", 1840 "deep-is": "^0.1.3",
1278 "fast-levenshtein": "~2.0.6", 1841 "fast-levenshtein": "^2.0.6",
1279 "levn": "~0.3.0", 1842 "levn": "^0.4.1",
1280 "prelude-ls": "~1.1.2", 1843 "prelude-ls": "^1.2.1",
1281 "type-check": "~0.3.2", 1844 "type-check": "^0.4.0",
1282 "word-wrap": "~1.2.3" 1845 "word-wrap": "^1.2.3"
1283 } 1846 }
1284 }, 1847 },
1285 "os": { 1848 "os": {
@@ -1310,6 +1873,30 @@
1310 "os-tmpdir": "^1.0.0" 1873 "os-tmpdir": "^1.0.0"
1311 } 1874 }
1312 }, 1875 },
1876 "p-limit": {
1877 "version": "2.3.0",
1878 "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
1879 "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
1880 "dev": true,
1881 "requires": {
1882 "p-try": "^2.0.0"
1883 }
1884 },
1885 "p-locate": {
1886 "version": "3.0.0",
1887 "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
1888 "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
1889 "dev": true,
1890 "requires": {
1891 "p-limit": "^2.0.0"
1892 }
1893 },
1894 "p-try": {
1895 "version": "2.2.0",
1896 "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
1897 "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
1898 "dev": true
1899 },
1313 "parent-module": { 1900 "parent-module": {
1314 "version": "1.0.1", 1901 "version": "1.0.1",
1315 "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1902 "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -1345,6 +1932,12 @@
1345 "@types/node": "*" 1932 "@types/node": "*"
1346 } 1933 }
1347 }, 1934 },
1935 "path-exists": {
1936 "version": "3.0.0",
1937 "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
1938 "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
1939 "dev": true
1940 },
1348 "path-is-absolute": { 1941 "path-is-absolute": {
1349 "version": "1.0.1", 1942 "version": "1.0.1",
1350 "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1943 "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -1352,9 +1945,9 @@
1352 "dev": true 1945 "dev": true
1353 }, 1946 },
1354 "path-key": { 1947 "path-key": {
1355 "version": "2.0.1", 1948 "version": "3.1.1",
1356 "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 1949 "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
1357 "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 1950 "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
1358 "dev": true 1951 "dev": true
1359 }, 1952 },
1360 "path-parse": { 1953 "path-parse": {
@@ -1376,9 +1969,9 @@
1376 "dev": true 1969 "dev": true
1377 }, 1970 },
1378 "prelude-ls": { 1971 "prelude-ls": {
1379 "version": "1.1.2", 1972 "version": "1.2.1",
1380 "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1973 "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
1381 "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1974 "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
1382 "dev": true 1975 "dev": true
1383 }, 1976 },
1384 "progress": { 1977 "progress": {
@@ -1419,16 +2012,37 @@
1419 "util-deprecate": "^1.0.1" 2012 "util-deprecate": "^1.0.1"
1420 } 2013 }
1421 }, 2014 },
2015 "readdirp": {
2016 "version": "3.2.0",
2017 "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
2018 "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
2019 "dev": true,
2020 "requires": {
2021 "picomatch": "^2.0.4"
2022 }
2023 },
1422 "regexpp": { 2024 "regexpp": {
1423 "version": "3.1.0", 2025 "version": "3.1.0",
1424 "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 2026 "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
1425 "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 2027 "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
1426 "dev": true 2028 "dev": true
1427 }, 2029 },
2030 "require-directory": {
2031 "version": "2.1.1",
2032 "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
2033 "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
2034 "dev": true
2035 },
2036 "require-main-filename": {
2037 "version": "2.0.0",
2038 "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
2039 "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
2040 "dev": true
2041 },
1428 "resolve": { 2042 "resolve": {
1429 "version": "1.16.1", 2043 "version": "1.17.0",
1430 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz", 2044 "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
1431 "integrity": "sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==", 2045 "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
1432 "dev": true, 2046 "dev": true,
1433 "requires": { 2047 "requires": {
1434 "path-parse": "^1.0.6" 2048 "path-parse": "^1.0.6"
@@ -1460,30 +2074,35 @@
1460 } 2074 }
1461 }, 2075 },
1462 "rollup": { 2076 "rollup": {
1463 "version": "2.10.0", 2077 "version": "2.10.9",
1464 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.0.tgz", 2078 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.9.tgz",
1465 "integrity": "sha512-7BmpEfUN9P6esJzWIn3DmR//90mW6YwYB1t3y48LpF8ITpYtL8s1kEirMKqUu44dVH/6a/rs0EuwYVL3FuRDoA==", 2079 "integrity": "sha512-dY/EbjiWC17ZCUSyk14hkxATAMAShkMsD43XmZGWjLrgFj15M3Dw2kEkA9ns64BiLFm9PKN6vTQw8neHwK74eg==",
1466 "dev": true, 2080 "dev": true,
1467 "requires": { 2081 "requires": {
1468 "fsevents": "~2.1.2" 2082 "fsevents": "~2.1.2"
1469 } 2083 }
1470 }, 2084 },
1471 "run-async": { 2085 "run-async": {
1472 "version": "2.4.0", 2086 "version": "2.4.1",
1473 "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", 2087 "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
1474 "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", 2088 "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
1475 "dev": true, 2089 "dev": true
1476 "requires": {
1477 "is-promise": "^2.1.0"
1478 }
1479 }, 2090 },
1480 "rxjs": { 2091 "rxjs": {
1481 "version": "6.5.4", 2092 "version": "6.5.5",
1482 "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", 2093 "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz",
1483 "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", 2094 "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
1484 "dev": true, 2095 "dev": true,
1485 "requires": { 2096 "requires": {
1486 "tslib": "^1.9.0" 2097 "tslib": "^1.9.0"
2098 },
2099 "dependencies": {
2100 "tslib": {
2101 "version": "1.13.0",
2102 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
2103 "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
2104 "dev": true
2105 }
1487 } 2106 }
1488 }, 2107 },
1489 "safe-buffer": { 2108 "safe-buffer": {
@@ -1503,19 +2122,25 @@
1503 "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 2122 "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
1504 "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 2123 "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
1505 }, 2124 },
2125 "set-blocking": {
2126 "version": "2.0.0",
2127 "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
2128 "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
2129 "dev": true
2130 },
1506 "shebang-command": { 2131 "shebang-command": {
1507 "version": "1.2.0", 2132 "version": "2.0.0",
1508 "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 2133 "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
1509 "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 2134 "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1510 "dev": true, 2135 "dev": true,
1511 "requires": { 2136 "requires": {
1512 "shebang-regex": "^1.0.0" 2137 "shebang-regex": "^3.0.0"
1513 } 2138 }
1514 }, 2139 },
1515 "shebang-regex": { 2140 "shebang-regex": {
1516 "version": "1.0.0", 2141 "version": "3.0.0",
1517 "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 2142 "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1518 "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 2143 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
1519 "dev": true 2144 "dev": true
1520 }, 2145 },
1521 "sigmund": { 2146 "sigmund": {
@@ -1525,9 +2150,9 @@
1525 "dev": true 2150 "dev": true
1526 }, 2151 },
1527 "signal-exit": { 2152 "signal-exit": {
1528 "version": "3.0.2", 2153 "version": "3.0.3",
1529 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 2154 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
1530 "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 2155 "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
1531 "dev": true 2156 "dev": true
1532 }, 2157 },
1533 "slice-ansi": { 2158 "slice-ansi": {
@@ -1570,17 +2195,48 @@
1570 "emoji-regex": "^8.0.0", 2195 "emoji-regex": "^8.0.0",
1571 "is-fullwidth-code-point": "^3.0.0", 2196 "is-fullwidth-code-point": "^3.0.0",
1572 "strip-ansi": "^6.0.0" 2197 "strip-ansi": "^6.0.0"
1573 }, 2198 }
1574 "dependencies": { 2199 },
1575 "strip-ansi": { 2200 "string.prototype.trimend": {
1576 "version": "6.0.0", 2201 "version": "1.0.1",
1577 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 2202 "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
1578 "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 2203 "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
1579 "dev": true, 2204 "dev": true,
1580 "requires": { 2205 "requires": {
1581 "ansi-regex": "^5.0.0" 2206 "define-properties": "^1.1.3",
1582 } 2207 "es-abstract": "^1.17.5"
1583 } 2208 }
2209 },
2210 "string.prototype.trimleft": {
2211 "version": "2.1.2",
2212 "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
2213 "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
2214 "dev": true,
2215 "requires": {
2216 "define-properties": "^1.1.3",
2217 "es-abstract": "^1.17.5",
2218 "string.prototype.trimstart": "^1.0.0"
2219 }
2220 },
2221 "string.prototype.trimright": {
2222 "version": "2.1.2",
2223 "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
2224 "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
2225 "dev": true,
2226 "requires": {
2227 "define-properties": "^1.1.3",
2228 "es-abstract": "^1.17.5",
2229 "string.prototype.trimend": "^1.0.0"
2230 }
2231 },
2232 "string.prototype.trimstart": {
2233 "version": "1.0.1",
2234 "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
2235 "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
2236 "dev": true,
2237 "requires": {
2238 "define-properties": "^1.1.3",
2239 "es-abstract": "^1.17.5"
1584 } 2240 }
1585 }, 2241 },
1586 "string_decoder": { 2242 "string_decoder": {
@@ -1593,26 +2249,18 @@
1593 } 2249 }
1594 }, 2250 },
1595 "strip-ansi": { 2251 "strip-ansi": {
1596 "version": "5.2.0", 2252 "version": "6.0.0",
1597 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 2253 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
1598 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 2254 "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
1599 "dev": true, 2255 "dev": true,
1600 "requires": { 2256 "requires": {
1601 "ansi-regex": "^4.1.0" 2257 "ansi-regex": "^5.0.0"
1602 },
1603 "dependencies": {
1604 "ansi-regex": {
1605 "version": "4.1.0",
1606 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
1607 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
1608 "dev": true
1609 }
1610 } 2258 }
1611 }, 2259 },
1612 "strip-json-comments": { 2260 "strip-json-comments": {
1613 "version": "3.0.1", 2261 "version": "3.1.0",
1614 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", 2262 "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz",
1615 "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", 2263 "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
1616 "dev": true 2264 "dev": true
1617 }, 2265 },
1618 "supports-color": { 2266 "supports-color": {
@@ -1636,6 +2284,12 @@
1636 "string-width": "^3.0.0" 2284 "string-width": "^3.0.0"
1637 }, 2285 },
1638 "dependencies": { 2286 "dependencies": {
2287 "ansi-regex": {
2288 "version": "4.1.0",
2289 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
2290 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
2291 "dev": true
2292 },
1639 "emoji-regex": { 2293 "emoji-regex": {
1640 "version": "7.0.3", 2294 "version": "7.0.3",
1641 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 2295 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -1658,6 +2312,15 @@
1658 "is-fullwidth-code-point": "^2.0.0", 2312 "is-fullwidth-code-point": "^2.0.0",
1659 "strip-ansi": "^5.1.0" 2313 "strip-ansi": "^5.1.0"
1660 } 2314 }
2315 },
2316 "strip-ansi": {
2317 "version": "5.2.0",
2318 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
2319 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
2320 "dev": true,
2321 "requires": {
2322 "ansi-regex": "^4.1.0"
2323 }
1661 } 2324 }
1662 } 2325 }
1663 }, 2326 },
@@ -1692,9 +2355,9 @@
1692 } 2355 }
1693 }, 2356 },
1694 "tslib": { 2357 "tslib": {
1695 "version": "1.12.0", 2358 "version": "2.0.0",
1696 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.12.0.tgz", 2359 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz",
1697 "integrity": "sha512-5rxCQkP0kytf4H1T4xz1imjxaUUPMvc5aWp0rJ/VMIN7ClRiH1FwFvBt8wOeMasp/epeUnmSW6CixSIePtiLqA==", 2360 "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==",
1698 "dev": true 2361 "dev": true
1699 }, 2362 },
1700 "tsutils": { 2363 "tsutils": {
@@ -1704,6 +2367,14 @@
1704 "dev": true, 2367 "dev": true,
1705 "requires": { 2368 "requires": {
1706 "tslib": "^1.8.1" 2369 "tslib": "^1.8.1"
2370 },
2371 "dependencies": {
2372 "tslib": {
2373 "version": "1.13.0",
2374 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
2375 "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
2376 "dev": true
2377 }
1707 } 2378 }
1708 }, 2379 },
1709 "tunnel": { 2380 "tunnel": {
@@ -1713,12 +2384,12 @@
1713 "dev": true 2384 "dev": true
1714 }, 2385 },
1715 "type-check": { 2386 "type-check": {
1716 "version": "0.3.2", 2387 "version": "0.4.0",
1717 "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 2388 "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
1718 "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 2389 "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
1719 "dev": true, 2390 "dev": true,
1720 "requires": { 2391 "requires": {
1721 "prelude-ls": "~1.1.2" 2392 "prelude-ls": "^1.2.1"
1722 } 2393 }
1723 }, 2394 },
1724 "type-fest": { 2395 "type-fest": {
@@ -1738,9 +2409,9 @@
1738 } 2409 }
1739 }, 2410 },
1740 "typescript": { 2411 "typescript": {
1741 "version": "3.9.2", 2412 "version": "3.9.3",
1742 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz", 2413 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz",
1743 "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==", 2414 "integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==",
1744 "dev": true 2415 "dev": true
1745 }, 2416 },
1746 "typescript-formatter": { 2417 "typescript-formatter": {
@@ -1865,21 +2536,131 @@
1865 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.1.tgz", 2536 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.1.tgz",
1866 "integrity": "sha512-tZFUSbyjUcrh+qQf13ALX4QDdOfDX0cVaBFgy7ktJ0VwS7AW/yRKgGPSxVqqP9OCMNPdqP57O5q47w2pEwfaUg==" 2537 "integrity": "sha512-tZFUSbyjUcrh+qQf13ALX4QDdOfDX0cVaBFgy7ktJ0VwS7AW/yRKgGPSxVqqP9OCMNPdqP57O5q47w2pEwfaUg=="
1867 }, 2538 },
2539 "vscode-test": {
2540 "version": "1.3.0",
2541 "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.3.0.tgz",
2542 "integrity": "sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==",
2543 "dev": true,
2544 "requires": {
2545 "http-proxy-agent": "^2.1.0",
2546 "https-proxy-agent": "^2.2.4",
2547 "rimraf": "^2.6.3"
2548 }
2549 },
1868 "which": { 2550 "which": {
1869 "version": "1.3.1", 2551 "version": "2.0.2",
1870 "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 2552 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
1871 "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 2553 "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1872 "dev": true, 2554 "dev": true,
1873 "requires": { 2555 "requires": {
1874 "isexe": "^2.0.0" 2556 "isexe": "^2.0.0"
1875 } 2557 }
1876 }, 2558 },
2559 "which-module": {
2560 "version": "2.0.0",
2561 "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
2562 "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
2563 "dev": true
2564 },
2565 "wide-align": {
2566 "version": "1.1.3",
2567 "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
2568 "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
2569 "dev": true,
2570 "requires": {
2571 "string-width": "^1.0.2 || 2"
2572 },
2573 "dependencies": {
2574 "ansi-regex": {
2575 "version": "3.0.0",
2576 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
2577 "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
2578 "dev": true
2579 },
2580 "is-fullwidth-code-point": {
2581 "version": "2.0.0",
2582 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
2583 "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
2584 "dev": true
2585 },
2586 "string-width": {
2587 "version": "2.1.1",
2588 "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
2589 "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
2590 "dev": true,
2591 "requires": {
2592 "is-fullwidth-code-point": "^2.0.0",
2593 "strip-ansi": "^4.0.0"
2594 }
2595 },
2596 "strip-ansi": {
2597 "version": "4.0.0",
2598 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
2599 "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
2600 "dev": true,
2601 "requires": {
2602 "ansi-regex": "^3.0.0"
2603 }
2604 }
2605 }
2606 },
1877 "word-wrap": { 2607 "word-wrap": {
1878 "version": "1.2.3", 2608 "version": "1.2.3",
1879 "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 2609 "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
1880 "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 2610 "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
1881 "dev": true 2611 "dev": true
1882 }, 2612 },
2613 "wrap-ansi": {
2614 "version": "5.1.0",
2615 "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
2616 "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
2617 "dev": true,
2618 "requires": {
2619 "ansi-styles": "^3.2.0",
2620 "string-width": "^3.0.0",
2621 "strip-ansi": "^5.0.0"
2622 },
2623 "dependencies": {
2624 "ansi-regex": {
2625 "version": "4.1.0",
2626 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
2627 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
2628 "dev": true
2629 },
2630 "emoji-regex": {
2631 "version": "7.0.3",
2632 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
2633 "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
2634 "dev": true
2635 },
2636 "is-fullwidth-code-point": {
2637 "version": "2.0.0",
2638 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
2639 "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
2640 "dev": true
2641 },
2642 "string-width": {
2643 "version": "3.1.0",
2644 "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
2645 "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
2646 "dev": true,
2647 "requires": {
2648 "emoji-regex": "^7.0.1",
2649 "is-fullwidth-code-point": "^2.0.0",
2650 "strip-ansi": "^5.1.0"
2651 }
2652 },
2653 "strip-ansi": {
2654 "version": "5.2.0",
2655 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
2656 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
2657 "dev": true,
2658 "requires": {
2659 "ansi-regex": "^4.1.0"
2660 }
2661 }
2662 }
2663 },
1883 "wrappy": { 2664 "wrappy": {
1884 "version": "1.0.2", 2665 "version": "1.0.2",
1885 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2666 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -1895,12 +2676,97 @@
1895 "mkdirp": "^0.5.1" 2676 "mkdirp": "^0.5.1"
1896 } 2677 }
1897 }, 2678 },
2679 "y18n": {
2680 "version": "4.0.0",
2681 "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
2682 "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
2683 "dev": true
2684 },
1898 "yallist": { 2685 "yallist": {
1899 "version": "2.1.2", 2686 "version": "2.1.2",
1900 "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 2687 "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
1901 "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 2688 "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
1902 "dev": true 2689 "dev": true
1903 }, 2690 },
2691 "yargs": {
2692 "version": "13.3.2",
2693 "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
2694 "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
2695 "dev": true,
2696 "requires": {
2697 "cliui": "^5.0.0",
2698 "find-up": "^3.0.0",
2699 "get-caller-file": "^2.0.1",
2700 "require-directory": "^2.1.1",
2701 "require-main-filename": "^2.0.0",
2702 "set-blocking": "^2.0.0",
2703 "string-width": "^3.0.0",
2704 "which-module": "^2.0.0",
2705 "y18n": "^4.0.0",
2706 "yargs-parser": "^13.1.2"
2707 },
2708 "dependencies": {
2709 "ansi-regex": {
2710 "version": "4.1.0",
2711 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
2712 "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
2713 "dev": true
2714 },
2715 "emoji-regex": {
2716 "version": "7.0.3",
2717 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
2718 "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
2719 "dev": true
2720 },
2721 "is-fullwidth-code-point": {
2722 "version": "2.0.0",
2723 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
2724 "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
2725 "dev": true
2726 },
2727 "string-width": {
2728 "version": "3.1.0",
2729 "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
2730 "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
2731 "dev": true,
2732 "requires": {
2733 "emoji-regex": "^7.0.1",
2734 "is-fullwidth-code-point": "^2.0.0",
2735 "strip-ansi": "^5.1.0"
2736 }
2737 },
2738 "strip-ansi": {
2739 "version": "5.2.0",
2740 "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
2741 "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
2742 "dev": true,
2743 "requires": {
2744 "ansi-regex": "^4.1.0"
2745 }
2746 }
2747 }
2748 },
2749 "yargs-parser": {
2750 "version": "13.1.2",
2751 "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
2752 "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
2753 "dev": true,
2754 "requires": {
2755 "camelcase": "^5.0.0",
2756 "decamelize": "^1.2.0"
2757 }
2758 },
2759 "yargs-unparser": {
2760 "version": "1.6.0",
2761 "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
2762 "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
2763 "dev": true,
2764 "requires": {
2765 "flat": "^4.1.0",
2766 "lodash": "^4.17.15",
2767 "yargs": "^13.3.0"
2768 }
2769 },
1904 "yauzl": { 2770 "yauzl": {
1905 "version": "2.10.0", 2771 "version": "2.10.0",
1906 "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 2772 "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index d899f60e3..acf3ca4b5 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
21 "Programming Languages" 21 "Programming Languages"
22 ], 22 ],
23 "engines": { 23 "engines": {
24 "vscode": "^1.44.0" 24 "vscode": "^1.44.1"
25 }, 25 },
26 "enableProposedApi": true, 26 "enableProposedApi": true,
27 "scripts": { 27 "scripts": {
@@ -29,27 +29,34 @@
29 "package": "vsce package -o rust-analyzer.vsix", 29 "package": "vsce package -o rust-analyzer.vsix",
30 "build": "tsc", 30 "build": "tsc",
31 "watch": "tsc --watch", 31 "watch": "tsc --watch",
32 "lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src", 32 "lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src ./tests",
33 "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix" 33 "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
34 "pretest": "npm run build",
35 "test": "node ./out/tests/runTests.js"
34 }, 36 },
35 "dependencies": { 37 "dependencies": {
36 "node-fetch": "^2.6.0", 38 "node-fetch": "^2.6.0",
37 "vscode-languageclient": "7.0.0-next.1" 39 "vscode-languageclient": "7.0.0-next.1"
38 }, 40 },
39 "devDependencies": { 41 "devDependencies": {
40 "@rollup/plugin-commonjs": "^11.1.0", 42 "@rollup/plugin-commonjs": "^12.0.0",
41 "@rollup/plugin-node-resolve": "^7.1.3", 43 "@rollup/plugin-node-resolve": "^8.0.0",
42 "@types/node": "^12.12.39", 44 "@types/glob": "^7.1.1",
45 "@types/mocha": "^7.0.2",
46 "@types/node": "^14.0.5",
43 "@types/node-fetch": "^2.5.7", 47 "@types/node-fetch": "^2.5.7",
44 "@types/vscode": "^1.44.0", 48 "@types/vscode": "^1.44.1",
45 "@typescript-eslint/eslint-plugin": "^2.33.0", 49 "@typescript-eslint/eslint-plugin": "^3.0.0",
46 "@typescript-eslint/parser": "^2.33.0", 50 "@typescript-eslint/parser": "^3.0.0",
47 "eslint": "^6.8.0", 51 "eslint": "^7.0.0",
48 "rollup": "^2.10.0", 52 "glob": "^7.1.6",
49 "tslib": "^1.12.0", 53 "mocha": "^7.1.2",
50 "typescript": "^3.9.2", 54 "rollup": "^2.10.7",
55 "tslib": "^2.0.0",
56 "typescript": "^3.9.3",
51 "typescript-formatter": "^7.2.2", 57 "typescript-formatter": "^7.2.2",
52 "vsce": "^1.75.0" 58 "vsce": "^1.75.0",
59 "vscode-test": "^1.3.0"
53 }, 60 },
54 "activationEvents": [ 61 "activationEvents": [
55 "onLanguage:rust", 62 "onLanguage:rust",
@@ -57,7 +64,7 @@
57 "onCommand:rust-analyzer.collectGarbage", 64 "onCommand:rust-analyzer.collectGarbage",
58 "workspaceContains:**/Cargo.toml" 65 "workspaceContains:**/Cargo.toml"
59 ], 66 ],
60 "main": "./out/main", 67 "main": "./out/src/main",
61 "contributes": { 68 "contributes": {
62 "taskDefinitions": [ 69 "taskDefinitions": [
63 { 70 {
@@ -159,6 +166,11 @@
159 "command": "rust-analyzer.serverVersion", 166 "command": "rust-analyzer.serverVersion",
160 "title": "Show RA Version", 167 "title": "Show RA Version",
161 "category": "Rust Analyzer" 168 "category": "Rust Analyzer"
169 },
170 {
171 "command": "rust-analyzer.toggleInlayHints",
172 "title": "Toggle inlay hints",
173 "category": "Rust Analyzer"
162 } 174 }
163 ], 175 ],
164 "keybindings": [ 176 "keybindings": [
@@ -313,22 +325,22 @@
313 "rust-analyzer.inlayHints.enable": { 325 "rust-analyzer.inlayHints.enable": {
314 "type": "boolean", 326 "type": "boolean",
315 "default": true, 327 "default": true,
316 "description": "Disable all inlay hints" 328 "description": "Whether to show inlay hints"
317 }, 329 },
318 "rust-analyzer.inlayHints.typeHints": { 330 "rust-analyzer.inlayHints.typeHints": {
319 "type": "boolean", 331 "type": "boolean",
320 "default": true, 332 "default": true,
321 "description": "Whether to show inlay type hints" 333 "description": "Whether to show inlay type hints for variables."
322 }, 334 },
323 "rust-analyzer.inlayHints.chainingHints": { 335 "rust-analyzer.inlayHints.chainingHints": {
324 "type": "boolean", 336 "type": "boolean",
325 "default": true, 337 "default": true,
326 "description": "Whether to show inlay type hints for method chains" 338 "description": "Whether to show inlay type hints for method chains."
327 }, 339 },
328 "rust-analyzer.inlayHints.parameterHints": { 340 "rust-analyzer.inlayHints.parameterHints": {
329 "type": "boolean", 341 "type": "boolean",
330 "default": true, 342 "default": true,
331 "description": "Whether to show function parameter name inlay hints at the call site" 343 "description": "Whether to show function parameter name inlay hints at the call site."
332 }, 344 },
333 "rust-analyzer.inlayHints.maxLength": { 345 "rust-analyzer.inlayHints.maxLength": {
334 "type": [ 346 "type": [
@@ -575,6 +587,11 @@
575 "description": "Style for attributes" 587 "description": "Style for attributes"
576 }, 588 },
577 { 589 {
590 "id": "boolean",
591 "description": "Style for boolean literals",
592 "superType": "keyword"
593 },
594 {
578 "id": "builtinType", 595 "id": "builtinType",
579 "description": "Style for builtin types", 596 "description": "Style for builtin types",
580 "superType": "type" 597 "superType": "type"
@@ -584,6 +601,11 @@
584 "description": "Style for lifetimes" 601 "description": "Style for lifetimes"
585 }, 602 },
586 { 603 {
604 "id": "selfKeyword",
605 "description": "Style for the self keyword",
606 "superType": "keyword"
607 },
608 {
587 "id": "typeAlias", 609 "id": "typeAlias",
588 "description": "Style for type aliases", 610 "description": "Style for type aliases",
589 "superType": "type" 611 "superType": "type"
@@ -604,6 +626,10 @@
604 ], 626 ],
605 "semanticTokenModifiers": [ 627 "semanticTokenModifiers": [
606 { 628 {
629 "id": "attribute",
630 "description": "Style for elements within attributes"
631 },
632 {
607 "id": "constant", 633 "id": "constant",
608 "description": "Style for compile-time constants" 634 "description": "Style for compile-time constants"
609 }, 635 },
@@ -630,6 +656,12 @@
630 "attribute": [ 656 "attribute": [
631 "meta.attribute.rust" 657 "meta.attribute.rust"
632 ], 658 ],
659 "function.attribute": [
660 "entity.name.function.attribute.rust"
661 ],
662 "boolean": [
663 "constant.language.boolean.rust"
664 ],
633 "builtinType": [ 665 "builtinType": [
634 "support.type.primitive.rust" 666 "support.type.primitive.rust"
635 ], 667 ],
diff --git a/editors/code/rollup.config.js b/editors/code/rollup.config.js
index 2ca27694d..58360eabb 100644
--- a/editors/code/rollup.config.js
+++ b/editors/code/rollup.config.js
@@ -6,7 +6,7 @@ import nodeBuiltins from 'builtin-modules';
6 6
7/** @type { import('rollup').RollupOptions } */ 7/** @type { import('rollup').RollupOptions } */
8export default { 8export default {
9 input: 'out/main.js', 9 input: 'out/src/main.js',
10 plugins: [ 10 plugins: [
11 resolve({ 11 resolve({
12 preferBuiltins: true 12 preferBuiltins: true
@@ -20,7 +20,7 @@ export default {
20 ], 20 ],
21 external: [...nodeBuiltins, 'vscode'], 21 external: [...nodeBuiltins, 'vscode'],
22 output: { 22 output: {
23 file: './out/main.js', 23 file: './out/src/main.js',
24 format: 'cjs', 24 format: 'cjs',
25 exports: 'named' 25 exports: 'named'
26 } 26 }
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index aa0811326..cdcd557dc 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -75,8 +75,13 @@
75 { 75 {
76 "comment": "Attribute", 76 "comment": "Attribute",
77 "name": "meta.attribute.rust", 77 "name": "meta.attribute.rust",
78 "begin": "#\\!?\\[", 78 "begin": "#\\!?\\[(\\w*)",
79 "end": "\\]", 79 "end": "\\]",
80 "captures": {
81 "1": {
82 "name": "entity.name.function.attribute.rust"
83 }
84 },
80 "patterns": [ 85 "patterns": [
81 { 86 {
82 "include": "#string_literal" 87 "include": "#string_literal"
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/ast_inspector.ts
index a5446c327..4fdd167bd 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/ast_inspector.ts
@@ -1,93 +1,15 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api';
3
4import { Ctx, Cmd, Disposable } from '../ctx';
5import { isRustDocument, RustEditor, isRustEditor, sleep } from '../util';
6
7const AST_FILE_SCHEME = "rust-analyzer";
8
9// Opens the virtual file that will show the syntax tree
10//
11// The contents of the file come from the `TextDocumentContentProvider`
12export function syntaxTree(ctx: Ctx): Cmd {
13 const tdcp = new TextDocumentContentProvider(ctx);
14
15 void new AstInspector(ctx);
16
17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
18 ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
19 brackets: [["[", ")"]],
20 }));
21
22 return async () => {
23 const editor = vscode.window.activeTextEditor;
24 const rangeEnabled = !!editor && !editor.selection.isEmpty;
25
26 const uri = rangeEnabled
27 ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
28 : tdcp.uri;
29
30 const document = await vscode.workspace.openTextDocument(uri);
31
32 tdcp.eventEmitter.fire(uri);
33
34 void await vscode.window.showTextDocument(document, {
35 viewColumn: vscode.ViewColumn.Two,
36 preserveFocus: true
37 });
38 };
39}
40
41class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
42 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
43 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
44
45
46 constructor(private readonly ctx: Ctx) {
47 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
48 vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
49 }
50
51 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
52 if (isRustDocument(event.document)) {
53 // We need to order this after language server updates, but there's no API for that.
54 // Hence, good old sleep().
55 void sleep(10).then(() => this.eventEmitter.fire(this.uri));
56 }
57 }
58 private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
59 if (editor && isRustEditor(editor)) {
60 this.eventEmitter.fire(this.uri);
61 }
62 }
63
64 provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
65 const rustEditor = this.ctx.activeRustEditor;
66 if (!rustEditor) return '';
67
68 // When the range based query is enabled we take the range of the selection
69 const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
70 ? this.ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
71 : null;
72
73 const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
74 return this.ctx.client.sendRequest(ra.syntaxTree, params, ct);
75 }
76
77 get onDidChange(): vscode.Event<vscode.Uri> {
78 return this.eventEmitter.event;
79 }
80}
81 2
3import { Ctx, Disposable } from './ctx';
4import { RustEditor, isRustEditor } from './util';
82 5
83// FIXME: consider implementing this via the Tree View API? 6// FIXME: consider implementing this via the Tree View API?
84// https://code.visualstudio.com/api/extension-guides/tree-view 7// https://code.visualstudio.com/api/extension-guides/tree-view
85class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable { 8export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
86 private readonly astDecorationType = vscode.window.createTextEditorDecorationType({ 9 private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
87 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'), 10 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
88 borderStyle: "solid", 11 borderStyle: "solid",
89 borderWidth: "2px", 12 borderWidth: "2px",
90
91 }); 13 });
92 private rustEditor: undefined | RustEditor; 14 private rustEditor: undefined | RustEditor;
93 15
@@ -113,7 +35,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D
113 }); 35 });
114 36
115 constructor(ctx: Ctx) { 37 constructor(ctx: Ctx) {
116 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); 38 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: 'rust-analyzer' }, this));
117 ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this)); 39 ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
118 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); 40 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
119 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions); 41 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
@@ -146,7 +68,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D
146 } 68 }
147 69
148 private findAstTextEditor(): undefined | vscode.TextEditor { 70 private findAstTextEditor(): undefined | vscode.TextEditor {
149 return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME); 71 return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === 'rust-analyzer');
150 } 72 }
151 73
152 private setRustEditor(newRustEditor: undefined | RustEditor) { 74 private setRustEditor(newRustEditor: undefined | RustEditor) {
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
index 6a41873d0..a55b2f860 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/cargo.ts
@@ -12,14 +12,44 @@ interface CompilationArtifact {
12 isTest: boolean; 12 isTest: boolean;
13} 13}
14 14
15export interface ArtifactSpec {
16 cargoArgs: string[];
17 filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
18}
19
20export function artifactSpec(args: readonly string[]): ArtifactSpec {
21 const cargoArgs = [...args, "--message-format=json"];
22
23 // arguments for a runnable from the quick pick should be updated.
24 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
25 switch (cargoArgs[0]) {
26 case "run": cargoArgs[0] = "build"; break;
27 case "test": {
28 if (!cargoArgs.includes("--no-run")) {
29 cargoArgs.push("--no-run");
30 }
31 break;
32 }
33 }
34
35 const result: ArtifactSpec = { cargoArgs: cargoArgs };
36 if (cargoArgs[0] === "test") {
37 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
38 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
39 result.filter = (artifacts) => artifacts.filter(it => it.isTest);
40 }
41
42 return result;
43}
44
15export class Cargo { 45export class Cargo {
16 constructor(readonly rootFolder: string, readonly output: OutputChannel) { } 46 constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
17 47
18 private async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> { 48 private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> {
19 const artifacts: CompilationArtifact[] = []; 49 const artifacts: CompilationArtifact[] = [];
20 50
21 try { 51 try {
22 await this.runCargo(cargoArgs, 52 await this.runCargo(spec.cargoArgs,
23 message => { 53 message => {
24 if (message.reason === 'compiler-artifact' && message.executable) { 54 if (message.reason === 'compiler-artifact' && message.executable) {
25 const isBinary = message.target.crate_types.includes('bin'); 55 const isBinary = message.target.crate_types.includes('bin');
@@ -43,30 +73,11 @@ export class Cargo {
43 throw new Error(`Cargo invocation has failed: ${err}`); 73 throw new Error(`Cargo invocation has failed: ${err}`);
44 } 74 }
45 75
46 return artifacts; 76 return spec.filter?.(artifacts) ?? artifacts;
47 } 77 }
48 78
49 async executableFromArgs(args: readonly string[]): Promise<string> { 79 async executableFromArgs(args: readonly string[]): Promise<string> {
50 const cargoArgs = [...args, "--message-format=json"]; 80 const artifacts = await this.getArtifacts(artifactSpec(args));
51
52 // arguments for a runnable from the quick pick should be updated.
53 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
54 switch (cargoArgs[0]) {
55 case "run": cargoArgs[0] = "build"; break;
56 case "test": {
57 if (cargoArgs.indexOf("--no-run") === -1) {
58 cargoArgs.push("--no-run");
59 }
60 break;
61 }
62 }
63
64 let artifacts = await this.artifactsFromArgs(cargoArgs);
65 if (cargoArgs[0] === "test") {
66 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
67 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
68 artifacts = artifacts.filter(a => a.isTest);
69 }
70 81
71 if (artifacts.length === 0) { 82 if (artifacts.length === 0) {
72 throw new Error('No compilation artifacts'); 83 throw new Error('No compilation artifacts');
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index fac1a0be3..d64f9a3f9 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -41,10 +41,12 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
41 return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { 41 return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => {
42 if (values === null) return undefined; 42 if (values === null) return undefined;
43 const result: (vscode.CodeAction | vscode.Command)[] = []; 43 const result: (vscode.CodeAction | vscode.Command)[] = [];
44 const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
44 for (const item of values) { 45 for (const item of values) {
45 if (lc.CodeAction.is(item)) { 46 if (lc.CodeAction.is(item)) {
46 const action = client.protocol2CodeConverter.asCodeAction(item); 47 const action = client.protocol2CodeConverter.asCodeAction(item);
47 if (isSnippetEdit(item)) { 48 const group = actionGroup(item);
49 if (isSnippetEdit(item) || group) {
48 action.command = { 50 action.command = {
49 command: "rust-analyzer.applySnippetWorkspaceEdit", 51 command: "rust-analyzer.applySnippetWorkspaceEdit",
50 title: "", 52 title: "",
@@ -52,12 +54,38 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
52 }; 54 };
53 action.edit = undefined; 55 action.edit = undefined;
54 } 56 }
55 result.push(action); 57
58 if (group) {
59 let entry = groups.get(group);
60 if (!entry) {
61 entry = { index: result.length, items: [] };
62 groups.set(group, entry);
63 result.push(action);
64 }
65 entry.items.push(action);
66 } else {
67 result.push(action);
68 }
56 } else { 69 } else {
57 const command = client.protocol2CodeConverter.asCommand(item); 70 const command = client.protocol2CodeConverter.asCommand(item);
58 result.push(command); 71 result.push(command);
59 } 72 }
60 } 73 }
74 for (const [group, { index, items }] of groups) {
75 if (items.length === 1) {
76 result[index] = items[0];
77 } else {
78 const action = new vscode.CodeAction(group);
79 action.command = {
80 command: "rust-analyzer.applyActionGroup",
81 title: "",
82 arguments: [items.map((item) => {
83 return { label: item.title, edit: item.command!!.arguments!![0] };
84 })],
85 };
86 result[index] = action;
87 }
88 }
61 return result; 89 return result;
62 }, 90 },
63 (_error) => undefined 91 (_error) => undefined
@@ -81,15 +109,16 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
81 // implementations are still in the "proposed" category for 3.16. 109 // implementations are still in the "proposed" category for 3.16.
82 client.registerFeature(new CallHierarchyFeature(client)); 110 client.registerFeature(new CallHierarchyFeature(client));
83 client.registerFeature(new SemanticTokensFeature(client)); 111 client.registerFeature(new SemanticTokensFeature(client));
84 client.registerFeature(new SnippetTextEditFeature()); 112 client.registerFeature(new ExperimentalFeatures());
85 113
86 return client; 114 return client;
87} 115}
88 116
89class SnippetTextEditFeature implements lc.StaticFeature { 117class ExperimentalFeatures implements lc.StaticFeature {
90 fillClientCapabilities(capabilities: lc.ClientCapabilities): void { 118 fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
91 const caps: any = capabilities.experimental ?? {}; 119 const caps: any = capabilities.experimental ?? {};
92 caps.snippetTextEdit = true; 120 caps.snippetTextEdit = true;
121 caps.codeActionGroup = true;
93 capabilities.experimental = caps; 122 capabilities.experimental = caps;
94 } 123 }
95 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 124 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
@@ -107,3 +136,7 @@ function isSnippetEdit(action: lc.CodeAction): boolean {
107 } 136 }
108 return false; 137 return false;
109} 138}
139
140function actionGroup(action: lc.CodeAction): string | undefined {
141 return (action as any).group;
142}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
new file mode 100644
index 000000000..86302db37
--- /dev/null
+++ b/editors/code/src/commands.ts
@@ -0,0 +1,370 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import * as ra from './lsp_ext';
4
5import { Ctx, Cmd } from './ctx';
6import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
7import { spawnSync } from 'child_process';
8import { RunnableQuickPick, selectRunnable, createTask } from './run';
9import { AstInspector } from './ast_inspector';
10import { isRustDocument, sleep, isRustEditor } from './util';
11
12export * from './ast_inspector';
13export * from './run';
14
15export function analyzerStatus(ctx: Ctx): Cmd {
16 const tdcp = new class implements vscode.TextDocumentContentProvider {
17 readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
18 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
19
20 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
21 if (!vscode.window.activeTextEditor) return '';
22
23 return ctx.client.sendRequest(ra.analyzerStatus, null);
24 }
25
26 get onDidChange(): vscode.Event<vscode.Uri> {
27 return this.eventEmitter.event;
28 }
29 }();
30
31 let poller: NodeJS.Timer | undefined = undefined;
32
33 ctx.pushCleanup(
34 vscode.workspace.registerTextDocumentContentProvider(
35 'rust-analyzer-status',
36 tdcp,
37 ),
38 );
39
40 ctx.pushCleanup({
41 dispose() {
42 if (poller !== undefined) {
43 clearInterval(poller);
44 }
45 },
46 });
47
48 return async () => {
49 if (poller === undefined) {
50 poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
51 }
52 const document = await vscode.workspace.openTextDocument(tdcp.uri);
53 return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
54 };
55}
56
57export function matchingBrace(ctx: Ctx): Cmd {
58 return async () => {
59 const editor = ctx.activeRustEditor;
60 const client = ctx.client;
61 if (!editor || !client) return;
62
63 const response = await client.sendRequest(ra.matchingBrace, {
64 textDocument: { uri: editor.document.uri.toString() },
65 positions: editor.selections.map(s =>
66 client.code2ProtocolConverter.asPosition(s.active),
67 ),
68 });
69 editor.selections = editor.selections.map((sel, idx) => {
70 const active = client.protocol2CodeConverter.asPosition(
71 response[idx],
72 );
73 const anchor = sel.isEmpty ? active : sel.anchor;
74 return new vscode.Selection(anchor, active);
75 });
76 editor.revealRange(editor.selection);
77 };
78}
79
80export function joinLines(ctx: Ctx): Cmd {
81 return async () => {
82 const editor = ctx.activeRustEditor;
83 const client = ctx.client;
84 if (!editor || !client) return;
85
86 const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
87 ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
88 textDocument: { uri: editor.document.uri.toString() },
89 });
90 editor.edit((builder) => {
91 client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
92 builder.replace(edit.range, edit.newText);
93 });
94 });
95 };
96}
97
98export function onEnter(ctx: Ctx): Cmd {
99 async function handleKeypress() {
100 const editor = ctx.activeRustEditor;
101 const client = ctx.client;
102
103 if (!editor || !client) return false;
104
105 const lcEdits = await client.sendRequest(ra.onEnter, {
106 textDocument: { uri: editor.document.uri.toString() },
107 position: client.code2ProtocolConverter.asPosition(
108 editor.selection.active,
109 ),
110 }).catch(_error => {
111 // client.logFailedRequest(OnEnterRequest.type, error);
112 return null;
113 });
114 if (!lcEdits) return false;
115
116 const edits = client.protocol2CodeConverter.asTextEdits(lcEdits);
117 await applySnippetTextEdits(editor, edits);
118 return true;
119 }
120
121 return async () => {
122 if (await handleKeypress()) return;
123
124 await vscode.commands.executeCommand('default:type', { text: '\n' });
125 };
126}
127
128export function parentModule(ctx: Ctx): Cmd {
129 return async () => {
130 const editor = ctx.activeRustEditor;
131 const client = ctx.client;
132 if (!editor || !client) return;
133
134 const response = await client.sendRequest(ra.parentModule, {
135 textDocument: { uri: editor.document.uri.toString() },
136 position: client.code2ProtocolConverter.asPosition(
137 editor.selection.active,
138 ),
139 });
140 const loc = response[0];
141 if (!loc) return;
142
143 const uri = client.protocol2CodeConverter.asUri(loc.targetUri);
144 const range = client.protocol2CodeConverter.asRange(loc.targetRange);
145
146 const doc = await vscode.workspace.openTextDocument(uri);
147 const e = await vscode.window.showTextDocument(doc);
148 e.selection = new vscode.Selection(range.start, range.start);
149 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
150 };
151}
152
153export function ssr(ctx: Ctx): Cmd {
154 return async () => {
155 const client = ctx.client;
156 if (!client) return;
157
158 const options: vscode.InputBoxOptions = {
159 value: "() ==>> ()",
160 prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
161 validateInput: async (x: string) => {
162 try {
163 await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
164 } catch (e) {
165 return e.toString();
166 }
167 return null;
168 }
169 };
170 const request = await vscode.window.showInputBox(options);
171 if (!request) return;
172
173 const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
174
175 await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit));
176 };
177}
178
179export function serverVersion(ctx: Ctx): Cmd {
180 return async () => {
181 const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
182 const commitHash = stdout.slice(`rust-analyzer `.length).trim();
183 const { releaseTag } = ctx.config.package;
184
185 void vscode.window.showInformationMessage(
186 `rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})`
187 );
188 };
189}
190
191export function toggleInlayHints(ctx: Ctx): Cmd {
192 return async () => {
193 await vscode
194 .workspace
195 .getConfiguration(`${ctx.config.rootSection}.inlayHints`)
196 .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
197 };
198}
199
200export function run(ctx: Ctx): Cmd {
201 let prevRunnable: RunnableQuickPick | undefined;
202
203 return async () => {
204 const item = await selectRunnable(ctx, prevRunnable);
205 if (!item) return;
206
207 item.detail = 'rerun';
208 prevRunnable = item;
209 const task = createTask(item.runnable);
210 return await vscode.tasks.executeTask(task);
211 };
212}
213
214// Opens the virtual file that will show the syntax tree
215//
216// The contents of the file come from the `TextDocumentContentProvider`
217export function syntaxTree(ctx: Ctx): Cmd {
218 const tdcp = new class implements vscode.TextDocumentContentProvider {
219 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
220 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
221 constructor() {
222 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
223 vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
224 }
225
226 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
227 if (isRustDocument(event.document)) {
228 // We need to order this after language server updates, but there's no API for that.
229 // Hence, good old sleep().
230 void sleep(10).then(() => this.eventEmitter.fire(this.uri));
231 }
232 }
233 private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
234 if (editor && isRustEditor(editor)) {
235 this.eventEmitter.fire(this.uri);
236 }
237 }
238
239 provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
240 const rustEditor = ctx.activeRustEditor;
241 if (!rustEditor) return '';
242
243 // When the range based query is enabled we take the range of the selection
244 const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
245 ? ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
246 : null;
247
248 const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
249 return ctx.client.sendRequest(ra.syntaxTree, params, ct);
250 }
251
252 get onDidChange(): vscode.Event<vscode.Uri> {
253 return this.eventEmitter.event;
254 }
255 };
256
257 void new AstInspector(ctx);
258
259 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
260 ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
261 brackets: [["[", ")"]],
262 }));
263
264 return async () => {
265 const editor = vscode.window.activeTextEditor;
266 const rangeEnabled = !!editor && !editor.selection.isEmpty;
267
268 const uri = rangeEnabled
269 ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
270 : tdcp.uri;
271
272 const document = await vscode.workspace.openTextDocument(uri);
273
274 tdcp.eventEmitter.fire(uri);
275
276 void await vscode.window.showTextDocument(document, {
277 viewColumn: vscode.ViewColumn.Two,
278 preserveFocus: true
279 });
280 };
281}
282
283
284// Opens the virtual file that will show the syntax tree
285//
286// The contents of the file come from the `TextDocumentContentProvider`
287export function expandMacro(ctx: Ctx): Cmd {
288 function codeFormat(expanded: ra.ExpandedMacro): string {
289 let result = `// Recursive expansion of ${expanded.name}! macro\n`;
290 result += '// ' + '='.repeat(result.length - 3);
291 result += '\n\n';
292 result += expanded.expansion;
293
294 return result;
295 }
296
297 const tdcp = new class implements vscode.TextDocumentContentProvider {
298 uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
299 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
300 async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
301 const editor = vscode.window.activeTextEditor;
302 const client = ctx.client;
303 if (!editor || !client) return '';
304
305 const position = editor.selection.active;
306
307 const expanded = await client.sendRequest(ra.expandMacro, {
308 textDocument: { uri: editor.document.uri.toString() },
309 position,
310 });
311
312 if (expanded == null) return 'Not available';
313
314 return codeFormat(expanded);
315 }
316
317 get onDidChange(): vscode.Event<vscode.Uri> {
318 return this.eventEmitter.event;
319 }
320 }();
321
322 ctx.pushCleanup(
323 vscode.workspace.registerTextDocumentContentProvider(
324 'rust-analyzer',
325 tdcp,
326 ),
327 );
328
329 return async () => {
330 const document = await vscode.workspace.openTextDocument(tdcp.uri);
331 tdcp.eventEmitter.fire(tdcp.uri);
332 return vscode.window.showTextDocument(
333 document,
334 vscode.ViewColumn.Two,
335 true,
336 );
337 };
338}
339
340export function collectGarbage(ctx: Ctx): Cmd {
341 return async () => ctx.client.sendRequest(ra.collectGarbage, null);
342}
343
344export function showReferences(ctx: Ctx): Cmd {
345 return (uri: string, position: lc.Position, locations: lc.Location[]) => {
346 const client = ctx.client;
347 if (client) {
348 vscode.commands.executeCommand(
349 'editor.action.showReferences',
350 vscode.Uri.parse(uri),
351 client.protocol2CodeConverter.asPosition(position),
352 locations.map(client.protocol2CodeConverter.asLocation),
353 );
354 }
355 };
356}
357
358export function applyActionGroup(_ctx: Ctx): Cmd {
359 return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => {
360 const selectedAction = await vscode.window.showQuickPick(actions);
361 if (!selectedAction) return;
362 await applySnippetWorkspaceEdit(selectedAction.edit);
363 };
364}
365
366export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
367 return async (edit: vscode.WorkspaceEdit) => {
368 await applySnippetWorkspaceEdit(edit);
369 };
370}
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
deleted file mode 100644
index 09daa3402..000000000
--- a/editors/code/src/commands/analyzer_status.ts
+++ /dev/null
@@ -1,51 +0,0 @@
1import * as vscode from 'vscode';
2
3import * as ra from '../rust-analyzer-api';
4import { Ctx, Cmd } from '../ctx';
5
6// Shows status of rust-analyzer (for debugging)
7export function analyzerStatus(ctx: Ctx): Cmd {
8 let poller: NodeJS.Timer | undefined = undefined;
9 const tdcp = new TextDocumentContentProvider(ctx);
10
11 ctx.pushCleanup(
12 vscode.workspace.registerTextDocumentContentProvider(
13 'rust-analyzer-status',
14 tdcp,
15 ),
16 );
17
18 ctx.pushCleanup({
19 dispose() {
20 if (poller !== undefined) {
21 clearInterval(poller);
22 }
23 },
24 });
25
26 return async () => {
27 if (poller === undefined) {
28 poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
29 }
30 const document = await vscode.workspace.openTextDocument(tdcp.uri);
31 return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
32 };
33}
34
35class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
36 readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
37 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
38
39 constructor(private readonly ctx: Ctx) {
40 }
41
42 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
43 if (!vscode.window.activeTextEditor) return '';
44
45 return this.ctx.client.sendRequest(ra.analyzerStatus, null);
46 }
47
48 get onDidChange(): vscode.Event<vscode.Uri> {
49 return this.eventEmitter.event;
50 }
51}
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
deleted file mode 100644
index 23f2ef1d5..000000000
--- a/editors/code/src/commands/expand_macro.ts
+++ /dev/null
@@ -1,66 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api';
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 expandMacro(ctx: Ctx): Cmd {
10 const tdcp = new TextDocumentContentProvider(ctx);
11 ctx.pushCleanup(
12 vscode.workspace.registerTextDocumentContentProvider(
13 'rust-analyzer',
14 tdcp,
15 ),
16 );
17
18 return async () => {
19 const document = await vscode.workspace.openTextDocument(tdcp.uri);
20 tdcp.eventEmitter.fire(tdcp.uri);
21 return vscode.window.showTextDocument(
22 document,
23 vscode.ViewColumn.Two,
24 true,
25 );
26 };
27}
28
29function codeFormat(expanded: ra.ExpandedMacro): string {
30 let result = `// Recursive expansion of ${expanded.name}! macro\n`;
31 result += '// ' + '='.repeat(result.length - 3);
32 result += '\n\n';
33 result += expanded.expansion;
34
35 return result;
36}
37
38class TextDocumentContentProvider
39 implements vscode.TextDocumentContentProvider {
40 uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
41 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
42
43 constructor(private readonly ctx: Ctx) {
44 }
45
46 async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
47 const editor = vscode.window.activeTextEditor;
48 const client = this.ctx.client;
49 if (!editor || !client) return '';
50
51 const position = editor.selection.active;
52
53 const expanded = await client.sendRequest(ra.expandMacro, {
54 textDocument: { uri: editor.document.uri.toString() },
55 position,
56 });
57
58 if (expanded == null) return 'Not available';
59
60 return codeFormat(expanded);
61 }
62
63 get onDidChange(): vscode.Event<vscode.Uri> {
64 return this.eventEmitter.event;
65 }
66}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
deleted file mode 100644
index 0937b495c..000000000
--- a/editors/code/src/commands/index.ts
+++ /dev/null
@@ -1,88 +0,0 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api';
4
5import { Ctx, Cmd } from '../ctx';
6import * as sourceChange from '../source_change';
7import { assert } from '../util';
8
9export * from './analyzer_status';
10export * from './matching_brace';
11export * from './join_lines';
12export * from './on_enter';
13export * from './parent_module';
14export * from './syntax_tree';
15export * from './expand_macro';
16export * from './runnables';
17export * from './ssr';
18export * from './server_version';
19
20export function collectGarbage(ctx: Ctx): Cmd {
21 return async () => ctx.client.sendRequest(ra.collectGarbage, null);
22}
23
24export function showReferences(ctx: Ctx): Cmd {
25 return (uri: string, position: lc.Position, locations: lc.Location[]) => {
26 const client = ctx.client;
27 if (client) {
28 vscode.commands.executeCommand(
29 'editor.action.showReferences',
30 vscode.Uri.parse(uri),
31 client.protocol2CodeConverter.asPosition(position),
32 locations.map(client.protocol2CodeConverter.asLocation),
33 );
34 }
35 };
36}
37
38export function applySourceChange(ctx: Ctx): Cmd {
39 return async (change: ra.SourceChange) => {
40 await sourceChange.applySourceChange(ctx, change);
41 };
42}
43
44export function selectAndApplySourceChange(ctx: Ctx): Cmd {
45 return async (changes: ra.SourceChange[]) => {
46 if (changes.length === 1) {
47 await sourceChange.applySourceChange(ctx, changes[0]);
48 } else if (changes.length > 0) {
49 const selectedChange = await vscode.window.showQuickPick(changes);
50 if (!selectedChange) return;
51 await sourceChange.applySourceChange(ctx, selectedChange);
52 }
53 };
54}
55
56export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd {
57 return async (edit: vscode.WorkspaceEdit) => {
58 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
59 const [uri, edits] = edit.entries()[0];
60
61 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
62 if (!editor) return;
63
64 let editWithSnippet: vscode.TextEdit | undefined = undefined;
65 let lineDelta = 0;
66 await editor.edit((builder) => {
67 for (const indel of edits) {
68 const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1;
69 if (isSnippet) {
70 editWithSnippet = indel;
71 } else {
72 if (!editWithSnippet) {
73 lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line);
74 }
75 builder.replace(indel.range, indel.newText);
76 }
77 }
78 });
79 if (editWithSnippet) {
80 const snip = editWithSnippet as vscode.TextEdit;
81 const range = snip.range.with(
82 snip.range.start.with(snip.range.start.line + lineDelta),
83 snip.range.end.with(snip.range.end.line + lineDelta),
84 );
85 await editor.insertSnippet(new vscode.SnippetString(snip.newText), range);
86 }
87 };
88}
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
deleted file mode 100644
index de0614653..000000000
--- a/editors/code/src/commands/join_lines.ts
+++ /dev/null
@@ -1,18 +0,0 @@
1import * as ra from '../rust-analyzer-api';
2
3import { Ctx, Cmd } from '../ctx';
4import { applySourceChange } from '../source_change';
5
6export function joinLines(ctx: Ctx): Cmd {
7 return async () => {
8 const editor = ctx.activeRustEditor;
9 const client = ctx.client;
10 if (!editor || !client) return;
11
12 const change = await client.sendRequest(ra.joinLines, {
13 range: client.code2ProtocolConverter.asRange(editor.selection),
14 textDocument: { uri: editor.document.uri.toString() },
15 });
16 await applySourceChange(ctx, change);
17 };
18}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
deleted file mode 100644
index a60776e2d..000000000
--- a/editors/code/src/commands/matching_brace.ts
+++ /dev/null
@@ -1,27 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api';
3
4import { Ctx, Cmd } from '../ctx';
5
6export function matchingBrace(ctx: Ctx): Cmd {
7 return async () => {
8 const editor = ctx.activeRustEditor;
9 const client = ctx.client;
10 if (!editor || !client) return;
11
12 const response = await client.sendRequest(ra.findMatchingBrace, {
13 textDocument: { uri: editor.document.uri.toString() },
14 offsets: editor.selections.map(s =>
15 client.code2ProtocolConverter.asPosition(s.active),
16 ),
17 });
18 editor.selections = editor.selections.map((sel, idx) => {
19 const active = client.protocol2CodeConverter.asPosition(
20 response[idx],
21 );
22 const anchor = sel.isEmpty ? active : sel.anchor;
23 return new vscode.Selection(anchor, active);
24 });
25 editor.revealRange(editor.selection);
26 };
27}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
deleted file mode 100644
index 285849db7..000000000
--- a/editors/code/src/commands/on_enter.ts
+++ /dev/null
@@ -1,34 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api';
3
4import { applySourceChange } from '../source_change';
5import { Cmd, Ctx } from '../ctx';
6
7async function handleKeypress(ctx: Ctx) {
8 const editor = ctx.activeRustEditor;
9 const client = ctx.client;
10
11 if (!editor || !client) return false;
12
13 const change = await client.sendRequest(ra.onEnter, {
14 textDocument: { uri: editor.document.uri.toString() },
15 position: client.code2ProtocolConverter.asPosition(
16 editor.selection.active,
17 ),
18 }).catch(_error => {
19 // client.logFailedRequest(OnEnterRequest.type, error);
20 return null;
21 });
22 if (!change) return false;
23
24 await applySourceChange(ctx, change);
25 return true;
26}
27
28export function onEnter(ctx: Ctx): Cmd {
29 return async () => {
30 if (await handleKeypress(ctx)) return;
31
32 await vscode.commands.executeCommand('default:type', { text: '\n' });
33 };
34}
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
deleted file mode 100644
index 8f78ddd71..000000000
--- a/editors/code/src/commands/parent_module.ts
+++ /dev/null
@@ -1,29 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api';
3
4import { Ctx, Cmd } from '../ctx';
5
6export function parentModule(ctx: Ctx): Cmd {
7 return async () => {
8 const editor = ctx.activeRustEditor;
9 const client = ctx.client;
10 if (!editor || !client) return;
11
12 const response = await client.sendRequest(ra.parentModule, {
13 textDocument: { uri: editor.document.uri.toString() },
14 position: client.code2ProtocolConverter.asPosition(
15 editor.selection.active,
16 ),
17 });
18 const loc = response[0];
19 if (loc == null) return;
20
21 const uri = client.protocol2CodeConverter.asUri(loc.uri);
22 const range = client.protocol2CodeConverter.asRange(loc.range);
23
24 const doc = await vscode.workspace.openTextDocument(uri);
25 const e = await vscode.window.showTextDocument(doc);
26 e.selection = new vscode.Selection(range.start, range.start);
27 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
28 };
29}
diff --git a/editors/code/src/commands/server_version.ts b/editors/code/src/commands/server_version.ts
deleted file mode 100644
index d64ac726e..000000000
--- a/editors/code/src/commands/server_version.ts
+++ /dev/null
@@ -1,15 +0,0 @@
1import * as vscode from "vscode";
2import { spawnSync } from "child_process";
3import { Ctx, Cmd } from '../ctx';
4
5export function serverVersion(ctx: Ctx): Cmd {
6 return async () => {
7 const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
8 const commitHash = stdout.slice(`rust-analyzer `.length).trim();
9 const { releaseTag } = ctx.config.package;
10
11 void vscode.window.showInformationMessage(
12 `rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})`
13 );
14 };
15}
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
deleted file mode 100644
index 4ef8cdf04..000000000
--- a/editors/code/src/commands/ssr.ts
+++ /dev/null
@@ -1,32 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from "../rust-analyzer-api";
3
4import { Ctx, Cmd } from '../ctx';
5import { applySourceChange } from '../source_change';
6
7export function ssr(ctx: Ctx): Cmd {
8 return async () => {
9 const client = ctx.client;
10 if (!client) return;
11
12 const options: vscode.InputBoxOptions = {
13 value: "() ==>> ()",
14 prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
15 validateInput: async (x: string) => {
16 try {
17 await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
18 } catch (e) {
19 return e.toString();
20 }
21 return null;
22 }
23 };
24 const request = await vscode.window.showInputBox(options);
25
26 if (!request) return;
27
28 const change = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
29
30 await applySourceChange(ctx, change);
31 };
32}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index ee294fbe3..e8abf8284 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -8,7 +8,7 @@ export const NIGHTLY_TAG = "nightly";
8export class Config { 8export class Config {
9 readonly extensionId = "matklad.rust-analyzer"; 9 readonly extensionId = "matklad.rust-analyzer";
10 10
11 private readonly rootSection = "rust-analyzer"; 11 readonly rootSection = "rust-analyzer";
12 private readonly requiresReloadOpts = [ 12 private readonly requiresReloadOpts = [
13 "serverPath", 13 "serverPath",
14 "cargo", 14 "cargo",
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index d3fe588e8..027504ecd 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -1,7 +1,7 @@
1import * as os from "os"; 1import * as os from "os";
2import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
3import * as path from 'path'; 3import * as path from 'path';
4import * as ra from './rust-analyzer-api'; 4import * as ra from './lsp_ext';
5 5
6import { Cargo } from './cargo'; 6import { Cargo } from './cargo';
7import { Ctx } from "./ctx"; 7import { Ctx } from "./ctx";
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index a2b07d003..9e6d6045f 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -1,6 +1,6 @@
1import * as lc from "vscode-languageclient"; 1import * as lc from "vscode-languageclient";
2import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
3import * as ra from './rust-analyzer-api'; 3import * as ra from './lsp_ext';
4 4
5import { Ctx, Disposable } from './ctx'; 5import { Ctx, Disposable } from './ctx';
6import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util'; 6import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util';
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
new file mode 100644
index 000000000..4da12eb30
--- /dev/null
+++ b/editors/code/src/lsp_ext.ts
@@ -0,0 +1,84 @@
1/**
2 * This file mirrors `crates/rust-analyzer/src/req.rs` declarations.
3 */
4
5import * as lc from "vscode-languageclient";
6
7export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus");
8
9export const collectGarbage = new lc.RequestType<null, null, void>("rust-analyzer/collectGarbage");
10
11export interface SyntaxTreeParams {
12 textDocument: lc.TextDocumentIdentifier;
13 range: lc.Range | null;
14}
15export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree");
16
17
18export interface ExpandMacroParams {
19 textDocument: lc.TextDocumentIdentifier;
20 position: lc.Position;
21}
22export interface ExpandedMacro {
23 name: string;
24 expansion: string;
25}
26export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro | null, void>("rust-analyzer/expandMacro");
27
28export interface MatchingBraceParams {
29 textDocument: lc.TextDocumentIdentifier;
30 positions: lc.Position[];
31}
32export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>("experimental/matchingBrace");
33
34export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule");
35
36export interface JoinLinesParams {
37 textDocument: lc.TextDocumentIdentifier;
38 ranges: lc.Range[];
39}
40export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], void>("experimental/joinLines");
41
42export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>("experimental/onEnter");
43
44export interface RunnablesParams {
45 textDocument: lc.TextDocumentIdentifier;
46 position: lc.Position | null;
47}
48export interface Runnable {
49 range: lc.Range;
50 label: string;
51 bin: string;
52 args: string[];
53 extraArgs: string[];
54 env: { [key: string]: string };
55 cwd: string | null;
56}
57export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("rust-analyzer/runnables");
58
59export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
60
61export namespace InlayHint {
62 export const enum Kind {
63 TypeHint = "TypeHint",
64 ParamHint = "ParameterHint",
65 ChainingHint = "ChainingHint",
66 }
67 interface Common {
68 range: lc.Range;
69 label: string;
70 }
71 export type TypeHint = Common & { kind: Kind.TypeHint };
72 export type ParamHint = Common & { kind: Kind.ParamHint };
73 export type ChainingHint = Common & { kind: Kind.ChainingHint };
74}
75export interface InlayHintsParams {
76 textDocument: lc.TextDocumentIdentifier;
77}
78export const inlayHints = new lc.RequestType<InlayHintsParams, InlayHint[], void>("rust-analyzer/inlayHints");
79
80export interface SsrParams {
81 query: string;
82 parseOnly: boolean;
83}
84export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index ac3bb365e..31ac81ee8 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -1,7 +1,7 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as path from "path"; 2import * as path from "path";
3import * as os from "os"; 3import * as os from "os";
4import { promises as fs } from "fs"; 4import { promises as fs, PathLike } from "fs";
5 5
6import * as commands from './commands'; 6import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 7import { activateInlayHints } from './inlay_hints';
@@ -12,6 +12,7 @@ import { log, assert, isValidExecutable } from './util';
12import { PersistentState } from './persistent_state'; 12import { PersistentState } from './persistent_state';
13import { fetchRelease, download } from './net'; 13import { fetchRelease, download } from './net';
14import { activateTaskProvider } from './tasks'; 14import { activateTaskProvider } from './tasks';
15import { exec } from 'child_process';
15 16
16let ctx: Ctx | undefined; 17let ctx: Ctx | undefined;
17 18
@@ -85,14 +86,14 @@ export async function activate(context: vscode.ExtensionContext) {
85 86
86 ctx.registerCommand('ssr', commands.ssr); 87 ctx.registerCommand('ssr', commands.ssr);
87 ctx.registerCommand('serverVersion', commands.serverVersion); 88 ctx.registerCommand('serverVersion', commands.serverVersion);
89 ctx.registerCommand('toggleInlayHints', commands.toggleInlayHints);
88 90
89 // Internal commands which are invoked by the server. 91 // Internal commands which are invoked by the server.
90 ctx.registerCommand('runSingle', commands.runSingle); 92 ctx.registerCommand('runSingle', commands.runSingle);
91 ctx.registerCommand('debugSingle', commands.debugSingle); 93 ctx.registerCommand('debugSingle', commands.debugSingle);
92 ctx.registerCommand('showReferences', commands.showReferences); 94 ctx.registerCommand('showReferences', commands.showReferences);
93 ctx.registerCommand('applySourceChange', commands.applySourceChange); 95 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
94 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit); 96 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
95 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
96 97
97 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 98 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
98 99
@@ -188,6 +189,46 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
188 return path; 189 return path;
189} 190}
190 191
192async function patchelf(dest: PathLike): Promise<void> {
193 await vscode.window.withProgress(
194 {
195 location: vscode.ProgressLocation.Notification,
196 title: "Patching rust-analyzer for NixOS"
197 },
198 async (progress, _) => {
199 const expression = `
200 {src, pkgs ? import <nixpkgs> {}}:
201 pkgs.stdenv.mkDerivation {
202 name = "rust-analyzer";
203 inherit src;
204 phases = [ "installPhase" "fixupPhase" ];
205 installPhase = "cp $src $out";
206 fixupPhase = ''
207 chmod 755 $out
208 patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
209 '';
210 }
211 `;
212 const origFile = dest + "-orig";
213 await fs.rename(dest, origFile);
214 progress.report({ message: "Patching executable", increment: 20 });
215 await new Promise((resolve, reject) => {
216 const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`,
217 (err, stdout, stderr) => {
218 if (err != null) {
219 reject(Error(stderr));
220 } else {
221 resolve(stdout);
222 }
223 });
224 handle.stdin?.write(expression);
225 handle.stdin?.end();
226 });
227 await fs.unlink(origFile);
228 }
229 );
230}
231
191async function getServer(config: Config, state: PersistentState): Promise<string | undefined> { 232async function getServer(config: Config, state: PersistentState): Promise<string | undefined> {
192 const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; 233 const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath;
193 if (explicitPath) { 234 if (explicitPath) {
@@ -237,6 +278,12 @@ async function getServer(config: Config, state: PersistentState): Promise<string
237 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 278 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
238 279
239 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 280 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 });
281
282 // Patching executable if that's NixOS.
283 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {
284 await patchelf(dest);
285 }
286
240 await state.updateServerVersion(config.package.version); 287 await state.updateServerVersion(config.package.version);
241 return dest; 288 return dest;
242} 289}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/run.ts
index 0bd30fb07..2a7a429cf 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/run.ts
@@ -1,13 +1,13 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api'; 3import * as ra from './lsp_ext';
4 4
5import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from './ctx';
6import { startDebugSession, getDebugConfiguration } from '../debug'; 6import { startDebugSession, getDebugConfiguration } from './debug';
7 7
8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; 8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
9 9
10async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { 10export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> {
11 const editor = ctx.activeRustEditor; 11 const editor = ctx.activeRustEditor;
12 const client = ctx.client; 12 const client = ctx.client;
13 if (!editor || !client) return; 13 if (!editor || !client) return;
@@ -83,20 +83,6 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debugg
83 }); 83 });
84} 84}
85 85
86export function run(ctx: Ctx): Cmd {
87 let prevRunnable: RunnableQuickPick | undefined;
88
89 return async () => {
90 const item = await selectRunnable(ctx, prevRunnable);
91 if (!item) return;
92
93 item.detail = 'rerun';
94 prevRunnable = item;
95 const task = createTask(item.runnable);
96 return await vscode.tasks.executeTask(task);
97 };
98}
99
100export function runSingle(ctx: Ctx): Cmd { 86export function runSingle(ctx: Ctx): Cmd {
101 return async (runnable: ra.Runnable) => { 87 return async (runnable: ra.Runnable) => {
102 const editor = ctx.activeRustEditor; 88 const editor = ctx.activeRustEditor;
@@ -165,7 +151,7 @@ export function newDebugConfig(ctx: Ctx): Cmd {
165 }; 151 };
166} 152}
167 153
168class RunnableQuickPick implements vscode.QuickPickItem { 154export class RunnableQuickPick implements vscode.QuickPickItem {
169 public label: string; 155 public label: string;
170 public description?: string | undefined; 156 public description?: string | undefined;
171 public detail?: string | undefined; 157 public detail?: string | undefined;
@@ -184,7 +170,7 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
184 env?: { [key: string]: string }; 170 env?: { [key: string]: string };
185} 171}
186 172
187function createTask(spec: ra.Runnable): vscode.Task { 173export function createTask(spec: ra.Runnable): vscode.Task {
188 const TASK_SOURCE = 'Rust'; 174 const TASK_SOURCE = 'Rust';
189 const definition: CargoTaskDefinition = { 175 const definition: CargoTaskDefinition = {
190 type: 'cargo', 176 type: 'cargo',
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
deleted file mode 100644
index 400ac3714..000000000
--- a/editors/code/src/rust-analyzer-api.ts
+++ /dev/null
@@ -1,125 +0,0 @@
1/**
2 * This file mirrors `crates/rust-analyzer/src/req.rs` declarations.
3 */
4
5import * as lc from "vscode-languageclient";
6
7type Option<T> = null | T;
8type Vec<T> = T[];
9type FxHashMap<K extends PropertyKey, V> = Record<K, V>;
10
11function request<TParams, TResult>(method: string) {
12 return new lc.RequestType<TParams, TResult, unknown>(`rust-analyzer/${method}`);
13}
14function notification<TParam>(method: string) {
15 return new lc.NotificationType<TParam>(method);
16}
17
18
19export const analyzerStatus = request<null, string>("analyzerStatus");
20
21
22export const collectGarbage = request<null, null>("collectGarbage");
23
24
25export interface SyntaxTreeParams {
26 textDocument: lc.TextDocumentIdentifier;
27 range: Option<lc.Range>;
28}
29export const syntaxTree = request<SyntaxTreeParams, string>("syntaxTree");
30
31
32export interface ExpandMacroParams {
33 textDocument: lc.TextDocumentIdentifier;
34 position: Option<lc.Position>;
35}
36export interface ExpandedMacro {
37 name: string;
38 expansion: string;
39}
40export const expandMacro = request<ExpandMacroParams, Option<ExpandedMacro>>("expandMacro");
41
42
43export interface FindMatchingBraceParams {
44 textDocument: lc.TextDocumentIdentifier;
45 offsets: Vec<lc.Position>;
46}
47export const findMatchingBrace = request<FindMatchingBraceParams, Vec<lc.Position>>("findMatchingBrace");
48
49
50export interface PublishDecorationsParams {
51 uri: string;
52 decorations: Vec<Decoration>;
53}
54export interface Decoration {
55 range: lc.Range;
56 tag: string;
57 bindingHash: Option<string>;
58}
59export const decorationsRequest = request<lc.TextDocumentIdentifier, Vec<Decoration>>("decorationsRequest");
60
61
62export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Location>>("parentModule");
63
64
65export interface JoinLinesParams {
66 textDocument: lc.TextDocumentIdentifier;
67 range: lc.Range;
68}
69export const joinLines = request<JoinLinesParams, SourceChange>("joinLines");
70
71
72export const onEnter = request<lc.TextDocumentPositionParams, Option<SourceChange>>("onEnter");
73
74export interface RunnablesParams {
75 textDocument: lc.TextDocumentIdentifier;
76 position: Option<lc.Position>;
77}
78export interface Runnable {
79 range: lc.Range;
80 label: string;
81 bin: string;
82 args: Vec<string>;
83 extraArgs: Vec<string>;
84 env: FxHashMap<string, string>;
85 cwd: Option<string>;
86}
87export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables");
88
89export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
90
91export namespace InlayHint {
92 export const enum Kind {
93 TypeHint = "TypeHint",
94 ParamHint = "ParameterHint",
95 ChainingHint = "ChainingHint",
96 }
97 interface Common {
98 range: lc.Range;
99 label: string;
100 }
101 export type TypeHint = Common & { kind: Kind.TypeHint };
102 export type ParamHint = Common & { kind: Kind.ParamHint };
103 export type ChainingHint = Common & { kind: Kind.ChainingHint };
104}
105export interface InlayHintsParams {
106 textDocument: lc.TextDocumentIdentifier;
107}
108export const inlayHints = request<InlayHintsParams, Vec<InlayHint>>("inlayHints");
109
110
111export interface SsrParams {
112 query: string;
113 parseOnly: boolean;
114}
115export const ssr = request<SsrParams, SourceChange>("ssr");
116
117
118export const publishDecorations = notification<PublishDecorationsParams>("publishDecorations");
119
120
121export interface SourceChange {
122 label: string;
123 workspaceEdit: lc.WorkspaceEdit;
124 cursorPosition: Option<lc.TextDocumentPositionParams>;
125}
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
new file mode 100644
index 000000000..bcb3f2cc7
--- /dev/null
+++ b/editors/code/src/snippets.ts
@@ -0,0 +1,55 @@
1import * as vscode from 'vscode';
2
3import { assert } from './util';
4
5export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
6 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
7 const [uri, edits] = edit.entries()[0];
8
9 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
10 if (!editor) return;
11 await applySnippetTextEdits(editor, edits);
12}
13
14export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
15 let selection: vscode.Selection | undefined = undefined;
16 let lineDelta = 0;
17 await editor.edit((builder) => {
18 for (const indel of edits) {
19 const parsed = parseSnippet(indel.newText);
20 if (parsed) {
21 const [newText, [placeholderStart, placeholderLength]] = parsed;
22 const prefix = newText.substr(0, placeholderStart);
23 const lastNewline = prefix.lastIndexOf('\n');
24
25 const startLine = indel.range.start.line + lineDelta + countLines(prefix);
26 const startColumn = lastNewline === -1 ?
27 indel.range.start.character + placeholderStart
28 : prefix.length - lastNewline - 1;
29 const endColumn = startColumn + placeholderLength;
30 selection = new vscode.Selection(
31 new vscode.Position(startLine, startColumn),
32 new vscode.Position(startLine, endColumn),
33 );
34 builder.replace(indel.range, newText);
35 } else {
36 lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
37 builder.replace(indel.range, indel.newText);
38 }
39 }
40 });
41 if (selection) editor.selection = selection;
42}
43
44function parseSnippet(snip: string): [string, [number, number]] | undefined {
45 const m = snip.match(/\$(0|\{0:([^}]*)\})/);
46 if (!m) return undefined;
47 const placeholder = m[2] ?? "";
48 const range: [number, number] = [m.index!!, placeholder.length];
49 const insert = snip.replace(m[0], placeholder);
50 return [insert, range];
51}
52
53function countLines(text: string): number {
54 return (text.match(/\n/g) || []).length;
55}
diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts
deleted file mode 100644
index af8f1df51..000000000
--- a/editors/code/src/source_change.ts
+++ /dev/null
@@ -1,54 +0,0 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import * as ra from './rust-analyzer-api';
4
5import { Ctx } from './ctx';
6
7export async function applySourceChange(ctx: Ctx, change: ra.SourceChange) {
8 const client = ctx.client;
9 if (!client) return;
10
11 const wsEdit = client.protocol2CodeConverter.asWorkspaceEdit(
12 change.workspaceEdit,
13 );
14 let created;
15 let moved;
16 if (change.workspaceEdit.documentChanges) {
17 for (const docChange of change.workspaceEdit.documentChanges) {
18 if (lc.CreateFile.is(docChange)) {
19 created = docChange.uri;
20 } else if (lc.RenameFile.is(docChange)) {
21 moved = docChange.newUri;
22 }
23 }
24 }
25 const toOpen = created || moved;
26 const toReveal = change.cursorPosition;
27 await vscode.workspace.applyEdit(wsEdit);
28 if (toOpen) {
29 const toOpenUri = vscode.Uri.parse(toOpen);
30 const doc = await vscode.workspace.openTextDocument(toOpenUri);
31 await vscode.window.showTextDocument(doc);
32 } else if (toReveal) {
33 const uri = client.protocol2CodeConverter.asUri(
34 toReveal.textDocument.uri,
35 );
36 const position = client.protocol2CodeConverter.asPosition(
37 toReveal.position,
38 );
39 const editor = vscode.window.activeTextEditor;
40 if (!editor || !editor.selection.isEmpty) {
41 return;
42 }
43
44 if (editor.document.uri !== uri) {
45 const doc = await vscode.workspace.openTextDocument(uri);
46 await vscode.window.showTextDocument(doc);
47 }
48 editor.selection = new vscode.Selection(position, position);
49 editor.revealRange(
50 new vscode.Range(position, position),
51 vscode.TextEditorRevealType.Default,
52 );
53 }
54}
diff --git a/editors/code/tests/runTests.ts b/editors/code/tests/runTests.ts
new file mode 100644
index 000000000..22df80ad3
--- /dev/null
+++ b/editors/code/tests/runTests.ts
@@ -0,0 +1,43 @@
1import * as path from 'path';
2import * as fs from 'fs';
3
4import { runTests } from 'vscode-test';
5
6async function main() {
7 // The folder containing the Extension Manifest package.json
8 // Passed to `--extensionDevelopmentPath`
9 const extensionDevelopmentPath = path.resolve(__dirname, '../../');
10
11 // Minimum supported version.
12 const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
13 const json = JSON.parse(jsonData.toString());
14 let minimalVersion: string = json.engines.vscode;
15 if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
16
17 const launchArgs = ["--disable-extensions"];
18
19 // All test suites (either unit tests or integration tests) should be in subfolders.
20 const extensionTestsPath = path.resolve(__dirname, './unit/index');
21
22 // Run tests using the minimal supported version.
23 await runTests({
24 version: minimalVersion,
25 launchArgs,
26 extensionDevelopmentPath,
27 extensionTestsPath
28 });
29
30 // and the latest one
31 await runTests({
32 version: 'stable',
33 launchArgs,
34 extensionDevelopmentPath,
35 extensionTestsPath
36 });
37}
38
39main().catch(err => {
40 // eslint-disable-next-line no-console
41 console.error('Failed to run tests', err);
42 process.exit(1);
43});
diff --git a/editors/code/tests/unit/index.ts b/editors/code/tests/unit/index.ts
new file mode 100644
index 000000000..5165720b4
--- /dev/null
+++ b/editors/code/tests/unit/index.ts
@@ -0,0 +1,38 @@
1import * as path from 'path';
2import Mocha from 'mocha';
3import glob from 'glob';
4
5export function run(): Promise<void> {
6 // Create the mocha test
7 const mocha = new Mocha({
8 ui: 'tdd',
9 color: true
10 });
11
12 const testsRoot = __dirname;
13
14 return new Promise((resolve, reject) => {
15 glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
16 if (err) {
17 return reject(err);
18 }
19
20 // Add files to the test suite
21 files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
22
23 try {
24 // Run the mocha test
25 mocha.timeout(100000);
26 mocha.run(failures => {
27 if (failures > 0) {
28 reject(new Error(`${failures} tests failed.`));
29 } else {
30 resolve();
31 }
32 });
33 } catch (err) {
34 reject(err);
35 }
36 });
37 });
38}
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts
new file mode 100644
index 000000000..d5cf1b74e
--- /dev/null
+++ b/editors/code/tests/unit/launch_config.test.ts
@@ -0,0 +1,52 @@
1import * as assert from 'assert';
2import * as cargo from '../../src/cargo';
3
4suite('Launch configuration', () => {
5
6 suite('Lens', () => {
7 test('A binary', async () => {
8 const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
9
10 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
11 assert.deepEqual(args.filter, undefined);
12 });
13
14 test('One of Multiple Binaries', async () => {
15 const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
16
17 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
18 assert.deepEqual(args.filter, undefined);
19 });
20
21 test('A test', async () => {
22 const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
23
24 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
25 assert.notDeepEqual(args.filter, undefined);
26 });
27 });
28
29 suite('QuickPick', () => {
30 test('A binary', async () => {
31 const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
32
33 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
34 assert.deepEqual(args.filter, undefined);
35 });
36
37
38 test('One of Multiple Binaries', async () => {
39 const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
40
41 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
42 assert.deepEqual(args.filter, undefined);
43 });
44
45 test('A test', async () => {
46 const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
47
48 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
49 assert.notDeepEqual(args.filter, undefined);
50 });
51 });
52});
diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json
index ad134865a..32d1a865f 100644
--- a/editors/code/tsconfig.json
+++ b/editors/code/tsconfig.json
@@ -9,7 +9,7 @@
9 "esModuleInterop": true, 9 "esModuleInterop": true,
10 "allowSyntheticDefaultImports": true, 10 "allowSyntheticDefaultImports": true,
11 "sourceMap": true, 11 "sourceMap": true,
12 "rootDir": "src", 12 "rootDir": ".",
13 "strict": true, 13 "strict": true,
14 "noUnusedLocals": true, 14 "noUnusedLocals": true,
15 "noUnusedParameters": true, 15 "noUnusedParameters": true,
@@ -18,6 +18,11 @@
18 "newLine": "LF" 18 "newLine": "LF"
19 }, 19 },
20 "exclude": [ 20 "exclude": [
21 "node_modules" 21 "node_modules",
22 ".vscode-test"
23 ],
24 "include": [
25 "src",
26 "tests"
22 ] 27 ]
23} 28}