diff options
443 files changed, 26819 insertions, 17921 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3f52f31f8..29ac89549 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml | |||
@@ -20,7 +20,7 @@ jobs: | |||
20 | runs-on: ${{ matrix.os }} | 20 | runs-on: ${{ matrix.os }} |
21 | strategy: | 21 | strategy: |
22 | matrix: | 22 | matrix: |
23 | os: [ubuntu-latest, windows-latest, macos-latest] | 23 | os: [ubuntu-16.04, windows-latest, macos-latest] |
24 | 24 | ||
25 | steps: | 25 | steps: |
26 | - name: Checkout repository | 26 | - name: Checkout repository |
@@ -42,25 +42,25 @@ jobs: | |||
42 | override: true | 42 | override: true |
43 | 43 | ||
44 | - name: Install Nodejs | 44 | - name: Install Nodejs |
45 | if: matrix.os == 'ubuntu-latest' | 45 | if: matrix.os == 'ubuntu-16.04' |
46 | uses: actions/setup-node@v1 | 46 | uses: actions/setup-node@v1 |
47 | with: | 47 | with: |
48 | node-version: 12.x | 48 | node-version: 12.x |
49 | 49 | ||
50 | - name: Dist | 50 | - name: Dist |
51 | if: matrix.os == 'ubuntu-latest' && github.ref == 'refs/heads/release' | 51 | if: matrix.os == 'ubuntu-16.04' && github.ref == 'refs/heads/release' |
52 | run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER | 52 | run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER |
53 | 53 | ||
54 | - name: Dist | 54 | - name: Dist |
55 | if: matrix.os == 'ubuntu-latest' && github.ref != 'refs/heads/release' | 55 | if: matrix.os == 'ubuntu-16.04' && github.ref != 'refs/heads/release' |
56 | run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly | 56 | run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly |
57 | 57 | ||
58 | - name: Dist | 58 | - name: Dist |
59 | if: matrix.os != 'ubuntu-latest' | 59 | if: matrix.os != 'ubuntu-16.04' |
60 | run: cargo xtask dist | 60 | run: cargo xtask dist |
61 | 61 | ||
62 | - name: Nightly analysis-stats check | 62 | - name: Nightly analysis-stats check |
63 | if: matrix.os == 'ubuntu-latest' && github.ref != 'refs/heads/release' | 63 | if: matrix.os == 'ubuntu-16.04' && github.ref != 'refs/heads/release' |
64 | run: ./dist/rust-analyzer-linux analysis-stats . | 64 | run: ./dist/rust-analyzer-linux analysis-stats . |
65 | 65 | ||
66 | - name: Upload artifacts | 66 | - name: Upload artifacts |
@@ -71,7 +71,7 @@ jobs: | |||
71 | 71 | ||
72 | publish: | 72 | publish: |
73 | name: publish | 73 | name: publish |
74 | runs-on: ubuntu-latest | 74 | runs-on: ubuntu-16.04 |
75 | needs: ['dist'] | 75 | needs: ['dist'] |
76 | steps: | 76 | steps: |
77 | - name: Install Nodejs | 77 | - name: Install Nodejs |
@@ -94,7 +94,7 @@ jobs: | |||
94 | path: dist | 94 | path: dist |
95 | - uses: actions/download-artifact@v1 | 95 | - uses: actions/download-artifact@v1 |
96 | with: | 96 | with: |
97 | name: dist-ubuntu-latest | 97 | name: dist-ubuntu-16.04 |
98 | path: dist | 98 | path: dist |
99 | - uses: actions/download-artifact@v1 | 99 | - uses: actions/download-artifact@v1 |
100 | with: | 100 | with: |
diff --git a/Cargo.lock b/Cargo.lock index 85ea4f178..c06236692 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1,6 +1,15 @@ | |||
1 | # This file is automatically @generated by Cargo. | 1 | # This file is automatically @generated by Cargo. |
2 | # It is not intended for manual editing. | 2 | # It is not intended for manual editing. |
3 | [[package]] | 3 | [[package]] |
4 | name = "addr2line" | ||
5 | version = "0.12.1" | ||
6 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
7 | checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" | ||
8 | dependencies = [ | ||
9 | "gimli", | ||
10 | ] | ||
11 | |||
12 | [[package]] | ||
4 | name = "aho-corasick" | 13 | name = "aho-corasick" |
5 | version = "0.7.10" | 14 | version = "0.7.10" |
6 | source = "registry+https://github.com/rust-lang/crates.io-index" | 15 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -11,9 +20,9 @@ dependencies = [ | |||
11 | 20 | ||
12 | [[package]] | 21 | [[package]] |
13 | name = "anyhow" | 22 | name = "anyhow" |
14 | version = "1.0.28" | 23 | version = "1.0.31" |
15 | source = "registry+https://github.com/rust-lang/crates.io-index" | 24 | source = "registry+https://github.com/rust-lang/crates.io-index" |
16 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" | 25 | checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" |
17 | 26 | ||
18 | [[package]] | 27 | [[package]] |
19 | name = "anymap" | 28 | name = "anymap" |
@@ -46,31 +55,22 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" | |||
46 | 55 | ||
47 | [[package]] | 56 | [[package]] |
48 | name = "backtrace" | 57 | name = "backtrace" |
49 | version = "0.3.46" | 58 | version = "0.3.48" |
50 | source = "registry+https://github.com/rust-lang/crates.io-index" | 59 | source = "registry+https://github.com/rust-lang/crates.io-index" |
51 | checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" | 60 | checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" |
52 | dependencies = [ | 61 | dependencies = [ |
53 | "backtrace-sys", | 62 | "addr2line", |
54 | "cfg-if", | 63 | "cfg-if", |
55 | "libc", | 64 | "libc", |
65 | "object", | ||
56 | "rustc-demangle", | 66 | "rustc-demangle", |
57 | ] | 67 | ] |
58 | 68 | ||
59 | [[package]] | 69 | [[package]] |
60 | name = "backtrace-sys" | ||
61 | version = "0.1.37" | ||
62 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
63 | checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" | ||
64 | dependencies = [ | ||
65 | "cc", | ||
66 | "libc", | ||
67 | ] | ||
68 | |||
69 | [[package]] | ||
70 | name = "base64" | 70 | name = "base64" |
71 | version = "0.12.0" | 71 | version = "0.12.1" |
72 | source = "registry+https://github.com/rust-lang/crates.io-index" | 72 | source = "registry+https://github.com/rust-lang/crates.io-index" |
73 | checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" | 73 | checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" |
74 | 74 | ||
75 | [[package]] | 75 | [[package]] |
76 | name = "bitflags" | 76 | name = "bitflags" |
@@ -80,18 +80,18 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | |||
80 | 80 | ||
81 | [[package]] | 81 | [[package]] |
82 | name = "bstr" | 82 | name = "bstr" |
83 | version = "0.2.12" | 83 | version = "0.2.13" |
84 | source = "registry+https://github.com/rust-lang/crates.io-index" | 84 | source = "registry+https://github.com/rust-lang/crates.io-index" |
85 | checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" | 85 | checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" |
86 | dependencies = [ | 86 | dependencies = [ |
87 | "memchr", | 87 | "memchr", |
88 | ] | 88 | ] |
89 | 89 | ||
90 | [[package]] | 90 | [[package]] |
91 | name = "cargo_metadata" | 91 | name = "cargo_metadata" |
92 | version = "0.9.1" | 92 | version = "0.10.0" |
93 | source = "registry+https://github.com/rust-lang/crates.io-index" | 93 | source = "registry+https://github.com/rust-lang/crates.io-index" |
94 | checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" | 94 | checksum = "b8de60b887edf6d74370fc8eb177040da4847d971d6234c7b13a6da324ef0caf" |
95 | dependencies = [ | 95 | dependencies = [ |
96 | "semver", | 96 | "semver", |
97 | "serde", | 97 | "serde", |
@@ -101,9 +101,9 @@ dependencies = [ | |||
101 | 101 | ||
102 | [[package]] | 102 | [[package]] |
103 | name = "cc" | 103 | name = "cc" |
104 | version = "1.0.52" | 104 | version = "1.0.53" |
105 | source = "registry+https://github.com/rust-lang/crates.io-index" | 105 | source = "registry+https://github.com/rust-lang/crates.io-index" |
106 | checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" | 106 | checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" |
107 | 107 | ||
108 | [[package]] | 108 | [[package]] |
109 | name = "cfg-if" | 109 | name = "cfg-if" |
@@ -113,8 +113,8 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |||
113 | 113 | ||
114 | [[package]] | 114 | [[package]] |
115 | name = "chalk-derive" | 115 | name = "chalk-derive" |
116 | version = "0.1.0" | 116 | version = "0.10.1-dev" |
117 | source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282" | 117 | source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" |
118 | dependencies = [ | 118 | dependencies = [ |
119 | "proc-macro2", | 119 | "proc-macro2", |
120 | "quote", | 120 | "quote", |
@@ -124,8 +124,8 @@ dependencies = [ | |||
124 | 124 | ||
125 | [[package]] | 125 | [[package]] |
126 | name = "chalk-engine" | 126 | name = "chalk-engine" |
127 | version = "0.9.0" | 127 | version = "0.10.1-dev" |
128 | source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282" | 128 | source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" |
129 | dependencies = [ | 129 | dependencies = [ |
130 | "chalk-macros", | 130 | "chalk-macros", |
131 | "rustc-hash", | 131 | "rustc-hash", |
@@ -133,8 +133,8 @@ dependencies = [ | |||
133 | 133 | ||
134 | [[package]] | 134 | [[package]] |
135 | name = "chalk-ir" | 135 | name = "chalk-ir" |
136 | version = "0.1.0" | 136 | version = "0.10.1-dev" |
137 | source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282" | 137 | source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" |
138 | dependencies = [ | 138 | dependencies = [ |
139 | "chalk-derive", | 139 | "chalk-derive", |
140 | "chalk-engine", | 140 | "chalk-engine", |
@@ -143,16 +143,16 @@ dependencies = [ | |||
143 | 143 | ||
144 | [[package]] | 144 | [[package]] |
145 | name = "chalk-macros" | 145 | name = "chalk-macros" |
146 | version = "0.1.1" | 146 | version = "0.10.1-dev" |
147 | source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282" | 147 | source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" |
148 | dependencies = [ | 148 | dependencies = [ |
149 | "lazy_static", | 149 | "lazy_static", |
150 | ] | 150 | ] |
151 | 151 | ||
152 | [[package]] | 152 | [[package]] |
153 | name = "chalk-rust-ir" | 153 | name = "chalk-rust-ir" |
154 | version = "0.1.0" | 154 | version = "0.10.1-dev" |
155 | source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282" | 155 | source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" |
156 | dependencies = [ | 156 | dependencies = [ |
157 | "chalk-derive", | 157 | "chalk-derive", |
158 | "chalk-engine", | 158 | "chalk-engine", |
@@ -162,8 +162,8 @@ dependencies = [ | |||
162 | 162 | ||
163 | [[package]] | 163 | [[package]] |
164 | name = "chalk-solve" | 164 | name = "chalk-solve" |
165 | version = "0.1.0" | 165 | version = "0.10.1-dev" |
166 | source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282" | 166 | source = "git+https://github.com/rust-lang/chalk.git?rev=3e9c2503ae9c5277c2acb74624dc267876dd89b3#3e9c2503ae9c5277c2acb74624dc267876dd89b3" |
167 | dependencies = [ | 167 | dependencies = [ |
168 | "chalk-derive", | 168 | "chalk-derive", |
169 | "chalk-engine", | 169 | "chalk-engine", |
@@ -342,9 +342,9 @@ dependencies = [ | |||
342 | 342 | ||
343 | [[package]] | 343 | [[package]] |
344 | name = "filetime" | 344 | name = "filetime" |
345 | version = "0.2.9" | 345 | version = "0.2.10" |
346 | source = "registry+https://github.com/rust-lang/crates.io-index" | 346 | source = "registry+https://github.com/rust-lang/crates.io-index" |
347 | checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e" | 347 | checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" |
348 | dependencies = [ | 348 | dependencies = [ |
349 | "cfg-if", | 349 | "cfg-if", |
350 | "libc", | 350 | "libc", |
@@ -360,9 +360,9 @@ checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" | |||
360 | 360 | ||
361 | [[package]] | 361 | [[package]] |
362 | name = "fnv" | 362 | name = "fnv" |
363 | version = "1.0.6" | 363 | version = "1.0.7" |
364 | source = "registry+https://github.com/rust-lang/crates.io-index" | 364 | source = "registry+https://github.com/rust-lang/crates.io-index" |
365 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" | 365 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" |
366 | 366 | ||
367 | [[package]] | 367 | [[package]] |
368 | name = "fs_extra" | 368 | name = "fs_extra" |
@@ -423,6 +423,12 @@ dependencies = [ | |||
423 | ] | 423 | ] |
424 | 424 | ||
425 | [[package]] | 425 | [[package]] |
426 | name = "gimli" | ||
427 | version = "0.21.0" | ||
428 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
429 | checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" | ||
430 | |||
431 | [[package]] | ||
426 | name = "globset" | 432 | name = "globset" |
427 | version = "0.4.5" | 433 | version = "0.4.5" |
428 | source = "registry+https://github.com/rust-lang/crates.io-index" | 434 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -437,9 +443,9 @@ dependencies = [ | |||
437 | 443 | ||
438 | [[package]] | 444 | [[package]] |
439 | name = "goblin" | 445 | name = "goblin" |
440 | version = "0.2.1" | 446 | version = "0.2.3" |
441 | source = "registry+https://github.com/rust-lang/crates.io-index" | 447 | source = "registry+https://github.com/rust-lang/crates.io-index" |
442 | checksum = "ddd5e3132801a1ac34ac53b97acde50c4685414dd2f291b9ea52afa6f07468c8" | 448 | checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" |
443 | dependencies = [ | 449 | dependencies = [ |
444 | "log", | 450 | "log", |
445 | "plain", | 451 | "plain", |
@@ -457,14 +463,23 @@ dependencies = [ | |||
457 | 463 | ||
458 | [[package]] | 464 | [[package]] |
459 | name = "hermit-abi" | 465 | name = "hermit-abi" |
460 | version = "0.1.12" | 466 | version = "0.1.13" |
461 | source = "registry+https://github.com/rust-lang/crates.io-index" | 467 | source = "registry+https://github.com/rust-lang/crates.io-index" |
462 | checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" | 468 | checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" |
463 | dependencies = [ | 469 | dependencies = [ |
464 | "libc", | 470 | "libc", |
465 | ] | 471 | ] |
466 | 472 | ||
467 | [[package]] | 473 | [[package]] |
474 | name = "home" | ||
475 | version = "0.5.3" | ||
476 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
477 | checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" | ||
478 | dependencies = [ | ||
479 | "winapi 0.3.8", | ||
480 | ] | ||
481 | |||
482 | [[package]] | ||
468 | name = "idna" | 483 | name = "idna" |
469 | version = "0.2.0" | 484 | version = "0.2.0" |
470 | source = "registry+https://github.com/rust-lang/crates.io-index" | 485 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -604,24 +619,24 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" | |||
604 | 619 | ||
605 | [[package]] | 620 | [[package]] |
606 | name = "libc" | 621 | name = "libc" |
607 | version = "0.2.69" | 622 | version = "0.2.70" |
608 | source = "registry+https://github.com/rust-lang/crates.io-index" | 623 | source = "registry+https://github.com/rust-lang/crates.io-index" |
609 | checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" | 624 | checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" |
610 | 625 | ||
611 | [[package]] | 626 | [[package]] |
612 | name = "libloading" | 627 | name = "libloading" |
613 | version = "0.6.1" | 628 | version = "0.6.2" |
614 | source = "registry+https://github.com/rust-lang/crates.io-index" | 629 | source = "registry+https://github.com/rust-lang/crates.io-index" |
615 | checksum = "3c4f51b790f5bdb65acb4cc94bb81d7b2ee60348a5431ac1467d390b017600b0" | 630 | checksum = "2cadb8e769f070c45df05c78c7520eb4cd17061d4ab262e43cfc68b4d00ac71c" |
616 | dependencies = [ | 631 | dependencies = [ |
617 | "winapi 0.3.8", | 632 | "winapi 0.3.8", |
618 | ] | 633 | ] |
619 | 634 | ||
620 | [[package]] | 635 | [[package]] |
621 | name = "linked-hash-map" | 636 | name = "linked-hash-map" |
622 | version = "0.5.2" | 637 | version = "0.5.3" |
623 | source = "registry+https://github.com/rust-lang/crates.io-index" | 638 | source = "registry+https://github.com/rust-lang/crates.io-index" |
624 | checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" | 639 | checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" |
625 | 640 | ||
626 | [[package]] | 641 | [[package]] |
627 | name = "lock_api" | 642 | name = "lock_api" |
@@ -655,9 +670,9 @@ dependencies = [ | |||
655 | 670 | ||
656 | [[package]] | 671 | [[package]] |
657 | name = "lsp-types" | 672 | name = "lsp-types" |
658 | version = "0.74.0" | 673 | version = "0.74.1" |
659 | source = "registry+https://github.com/rust-lang/crates.io-index" | 674 | source = "registry+https://github.com/rust-lang/crates.io-index" |
660 | checksum = "820f746e5716ab9a2d664794636188bd003023b72e55404ee27105dc22869922" | 675 | checksum = "57c0e6a2b8837d27b29deb3f3e6dc1c6d2f57947677f9be1024e482ec5b59525" |
661 | dependencies = [ | 676 | dependencies = [ |
662 | "base64", | 677 | "base64", |
663 | "bitflags", | 678 | "bitflags", |
@@ -706,9 +721,9 @@ dependencies = [ | |||
706 | 721 | ||
707 | [[package]] | 722 | [[package]] |
708 | name = "mio" | 723 | name = "mio" |
709 | version = "0.6.21" | 724 | version = "0.6.22" |
710 | source = "registry+https://github.com/rust-lang/crates.io-index" | 725 | source = "registry+https://github.com/rust-lang/crates.io-index" |
711 | checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" | 726 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" |
712 | dependencies = [ | 727 | dependencies = [ |
713 | "cfg-if", | 728 | "cfg-if", |
714 | "fuchsia-zircon", | 729 | "fuchsia-zircon", |
@@ -749,9 +764,9 @@ dependencies = [ | |||
749 | 764 | ||
750 | [[package]] | 765 | [[package]] |
751 | name = "net2" | 766 | name = "net2" |
752 | version = "0.2.33" | 767 | version = "0.2.34" |
753 | source = "registry+https://github.com/rust-lang/crates.io-index" | 768 | source = "registry+https://github.com/rust-lang/crates.io-index" |
754 | checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" | 769 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" |
755 | dependencies = [ | 770 | dependencies = [ |
756 | "cfg-if", | 771 | "cfg-if", |
757 | "libc", | 772 | "libc", |
@@ -787,10 +802,16 @@ dependencies = [ | |||
787 | ] | 802 | ] |
788 | 803 | ||
789 | [[package]] | 804 | [[package]] |
805 | name = "object" | ||
806 | version = "0.19.0" | ||
807 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
808 | checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" | ||
809 | |||
810 | [[package]] | ||
790 | name = "once_cell" | 811 | name = "once_cell" |
791 | version = "1.3.1" | 812 | version = "1.4.0" |
792 | source = "registry+https://github.com/rust-lang/crates.io-index" | 813 | source = "registry+https://github.com/rust-lang/crates.io-index" |
793 | checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" | 814 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" |
794 | 815 | ||
795 | [[package]] | 816 | [[package]] |
796 | name = "ordermap" | 817 | name = "ordermap" |
@@ -824,9 +845,9 @@ dependencies = [ | |||
824 | 845 | ||
825 | [[package]] | 846 | [[package]] |
826 | name = "paste" | 847 | name = "paste" |
827 | version = "0.1.11" | 848 | version = "0.1.12" |
828 | source = "registry+https://github.com/rust-lang/crates.io-index" | 849 | source = "registry+https://github.com/rust-lang/crates.io-index" |
829 | checksum = "a3c897744f63f34f7ae3a024d9162bb5001f4ad661dd24bea0dc9f075d2de1c6" | 850 | checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476" |
830 | dependencies = [ | 851 | dependencies = [ |
831 | "paste-impl", | 852 | "paste-impl", |
832 | "proc-macro-hack", | 853 | "proc-macro-hack", |
@@ -834,9 +855,9 @@ dependencies = [ | |||
834 | 855 | ||
835 | [[package]] | 856 | [[package]] |
836 | name = "paste-impl" | 857 | name = "paste-impl" |
837 | version = "0.1.11" | 858 | version = "0.1.12" |
838 | source = "registry+https://github.com/rust-lang/crates.io-index" | 859 | source = "registry+https://github.com/rust-lang/crates.io-index" |
839 | checksum = "66fd6f92e3594f2dd7b3fc23e42d82e292f7bcda6d8e5dcd167072327234ab89" | 860 | checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c" |
840 | dependencies = [ | 861 | dependencies = [ |
841 | "proc-macro-hack", | 862 | "proc-macro-hack", |
842 | "proc-macro2", | 863 | "proc-macro2", |
@@ -874,9 +895,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" | |||
874 | 895 | ||
875 | [[package]] | 896 | [[package]] |
876 | name = "ppv-lite86" | 897 | name = "ppv-lite86" |
877 | version = "0.2.6" | 898 | version = "0.2.8" |
878 | source = "registry+https://github.com/rust-lang/crates.io-index" | 899 | source = "registry+https://github.com/rust-lang/crates.io-index" |
879 | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" | 900 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" |
880 | 901 | ||
881 | [[package]] | 902 | [[package]] |
882 | name = "proc-macro-hack" | 903 | name = "proc-macro-hack" |
@@ -886,18 +907,18 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" | |||
886 | 907 | ||
887 | [[package]] | 908 | [[package]] |
888 | name = "proc-macro2" | 909 | name = "proc-macro2" |
889 | version = "1.0.10" | 910 | version = "1.0.13" |
890 | source = "registry+https://github.com/rust-lang/crates.io-index" | 911 | source = "registry+https://github.com/rust-lang/crates.io-index" |
891 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" | 912 | checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" |
892 | dependencies = [ | 913 | dependencies = [ |
893 | "unicode-xid", | 914 | "unicode-xid", |
894 | ] | 915 | ] |
895 | 916 | ||
896 | [[package]] | 917 | [[package]] |
897 | name = "quote" | 918 | name = "quote" |
898 | version = "1.0.4" | 919 | version = "1.0.6" |
899 | source = "registry+https://github.com/rust-lang/crates.io-index" | 920 | source = "registry+https://github.com/rust-lang/crates.io-index" |
900 | checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" | 921 | checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" |
901 | dependencies = [ | 922 | dependencies = [ |
902 | "proc-macro2", | 923 | "proc-macro2", |
903 | ] | 924 | ] |
@@ -954,10 +975,9 @@ version = "0.1.0" | |||
954 | dependencies = [ | 975 | dependencies = [ |
955 | "cargo_metadata", | 976 | "cargo_metadata", |
956 | "crossbeam-channel", | 977 | "crossbeam-channel", |
957 | "insta", | ||
958 | "jod-thread", | 978 | "jod-thread", |
959 | "log", | 979 | "log", |
960 | "lsp-types", | 980 | "ra_toolchain", |
961 | "serde_json", | 981 | "serde_json", |
962 | ] | 982 | ] |
963 | 983 | ||
@@ -1163,6 +1183,7 @@ dependencies = [ | |||
1163 | "ra_cfg", | 1183 | "ra_cfg", |
1164 | "ra_db", | 1184 | "ra_db", |
1165 | "ra_proc_macro", | 1185 | "ra_proc_macro", |
1186 | "ra_toolchain", | ||
1166 | "rustc-hash", | 1187 | "rustc-hash", |
1167 | "serde", | 1188 | "serde", |
1168 | "serde_json", | 1189 | "serde_json", |
@@ -1195,6 +1216,13 @@ dependencies = [ | |||
1195 | ] | 1216 | ] |
1196 | 1217 | ||
1197 | [[package]] | 1218 | [[package]] |
1219 | name = "ra_toolchain" | ||
1220 | version = "0.1.0" | ||
1221 | dependencies = [ | ||
1222 | "home", | ||
1223 | ] | ||
1224 | |||
1225 | [[package]] | ||
1198 | name = "ra_tt" | 1226 | name = "ra_tt" |
1199 | version = "0.1.0" | 1227 | version = "0.1.0" |
1200 | dependencies = [ | 1228 | dependencies = [ |
@@ -1203,9 +1231,9 @@ dependencies = [ | |||
1203 | 1231 | ||
1204 | [[package]] | 1232 | [[package]] |
1205 | name = "ra_vfs" | 1233 | name = "ra_vfs" |
1206 | version = "0.6.0" | 1234 | version = "0.6.1" |
1207 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1208 | checksum = "fcaa5615f420134aea7667253db101d03a5c5f300eac607872dc2a36407b2ac9" | 1236 | checksum = "cbf31a173fc77ec59c27cf39af6baa137b40f4dbd45a8b3eccb1b2e4cfc922c1" |
1209 | dependencies = [ | 1237 | dependencies = [ |
1210 | "crossbeam-channel", | 1238 | "crossbeam-channel", |
1211 | "jod-thread", | 1239 | "jod-thread", |
@@ -1351,6 +1379,7 @@ dependencies = [ | |||
1351 | "crossbeam-channel", | 1379 | "crossbeam-channel", |
1352 | "env_logger", | 1380 | "env_logger", |
1353 | "globset", | 1381 | "globset", |
1382 | "insta", | ||
1354 | "itertools", | 1383 | "itertools", |
1355 | "jod-thread", | 1384 | "jod-thread", |
1356 | "log", | 1385 | "log", |
@@ -1469,9 +1498,9 @@ dependencies = [ | |||
1469 | 1498 | ||
1470 | [[package]] | 1499 | [[package]] |
1471 | name = "scroll_derive" | 1500 | name = "scroll_derive" |
1472 | version = "0.10.1" | 1501 | version = "0.10.2" |
1473 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1474 | checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" | 1503 | checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9" |
1475 | dependencies = [ | 1504 | dependencies = [ |
1476 | "proc-macro2", | 1505 | "proc-macro2", |
1477 | "quote", | 1506 | "quote", |
@@ -1496,18 +1525,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | |||
1496 | 1525 | ||
1497 | [[package]] | 1526 | [[package]] |
1498 | name = "serde" | 1527 | name = "serde" |
1499 | version = "1.0.106" | 1528 | version = "1.0.110" |
1500 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1529 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1501 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" | 1530 | checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" |
1502 | dependencies = [ | 1531 | dependencies = [ |
1503 | "serde_derive", | 1532 | "serde_derive", |
1504 | ] | 1533 | ] |
1505 | 1534 | ||
1506 | [[package]] | 1535 | [[package]] |
1507 | name = "serde_derive" | 1536 | name = "serde_derive" |
1508 | version = "1.0.106" | 1537 | version = "1.0.110" |
1509 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1510 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" | 1539 | checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" |
1511 | dependencies = [ | 1540 | dependencies = [ |
1512 | "proc-macro2", | 1541 | "proc-macro2", |
1513 | "quote", | 1542 | "quote", |
@@ -1516,9 +1545,9 @@ dependencies = [ | |||
1516 | 1545 | ||
1517 | [[package]] | 1546 | [[package]] |
1518 | name = "serde_json" | 1547 | name = "serde_json" |
1519 | version = "1.0.52" | 1548 | version = "1.0.53" |
1520 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1549 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1521 | checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" | 1550 | checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" |
1522 | dependencies = [ | 1551 | dependencies = [ |
1523 | "itoa", | 1552 | "itoa", |
1524 | "ryu", | 1553 | "ryu", |
@@ -1538,9 +1567,9 @@ dependencies = [ | |||
1538 | 1567 | ||
1539 | [[package]] | 1568 | [[package]] |
1540 | name = "serde_yaml" | 1569 | name = "serde_yaml" |
1541 | version = "0.8.11" | 1570 | version = "0.8.12" |
1542 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1543 | checksum = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" | 1572 | checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" |
1544 | dependencies = [ | 1573 | dependencies = [ |
1545 | "dtoa", | 1574 | "dtoa", |
1546 | "linked-hash-map", | 1575 | "linked-hash-map", |
@@ -1581,9 +1610,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" | |||
1581 | 1610 | ||
1582 | [[package]] | 1611 | [[package]] |
1583 | name = "syn" | 1612 | name = "syn" |
1584 | version = "1.0.18" | 1613 | version = "1.0.22" |
1585 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1614 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1586 | checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" | 1615 | checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" |
1587 | dependencies = [ | 1616 | dependencies = [ |
1588 | "proc-macro2", | 1617 | "proc-macro2", |
1589 | "quote", | 1618 | "quote", |
@@ -1667,9 +1696,9 @@ dependencies = [ | |||
1667 | 1696 | ||
1668 | [[package]] | 1697 | [[package]] |
1669 | name = "threadpool" | 1698 | name = "threadpool" |
1670 | version = "1.8.0" | 1699 | version = "1.8.1" |
1671 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1672 | checksum = "e8dae184447c15d5a6916d973c642aec485105a13cd238192a6927ae3e077d66" | 1701 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" |
1673 | dependencies = [ | 1702 | dependencies = [ |
1674 | "num_cpus", | 1703 | "num_cpus", |
1675 | ] | 1704 | ] |
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs new file mode 100644 index 000000000..c0a0226fb --- /dev/null +++ b/crates/ra_assists/src/assist_config.rs | |||
@@ -0,0 +1,27 @@ | |||
1 | //! Settings for tweaking assists. | ||
2 | //! | ||
3 | //! The fun thing here is `SnippetCap` -- this type can only be created in this | ||
4 | //! module, and we use to statically check that we only produce snippet | ||
5 | //! assists if we are allowed to. | ||
6 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
8 | pub struct AssistConfig { | ||
9 | pub snippet_cap: Option<SnippetCap>, | ||
10 | } | ||
11 | |||
12 | impl AssistConfig { | ||
13 | pub fn allow_snippets(&mut self, yes: bool) { | ||
14 | self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } | ||
15 | } | ||
16 | } | ||
17 | |||
18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
19 | pub struct SnippetCap { | ||
20 | _private: (), | ||
21 | } | ||
22 | |||
23 | impl Default for AssistConfig { | ||
24 | fn default() -> Self { | ||
25 | AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } | ||
26 | } | ||
27 | } | ||
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs new file mode 100644 index 000000000..f3af70a3e --- /dev/null +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -0,0 +1,257 @@ | |||
1 | //! See `AssistContext` | ||
2 | |||
3 | use algo::find_covering_element; | ||
4 | use hir::Semantics; | ||
5 | use ra_db::{FileId, FileRange}; | ||
6 | use ra_fmt::{leading_indent, reindent}; | ||
7 | use ra_ide_db::{ | ||
8 | source_change::{SingleFileChange, SourceChange}, | ||
9 | RootDatabase, | ||
10 | }; | ||
11 | use ra_syntax::{ | ||
12 | algo::{self, find_node_at_offset, SyntaxRewriter}, | ||
13 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, | ||
14 | TokenAtOffset, | ||
15 | }; | ||
16 | use ra_text_edit::TextEditBuilder; | ||
17 | |||
18 | use crate::{ | ||
19 | assist_config::{AssistConfig, SnippetCap}, | ||
20 | Assist, AssistId, GroupLabel, ResolvedAssist, | ||
21 | }; | ||
22 | |||
23 | /// `AssistContext` allows to apply an assist or check if it could be applied. | ||
24 | /// | ||
25 | /// Assists use a somewhat over-engineered approach, given the current needs. | ||
26 | /// The assists workflow consists of two phases. In the first phase, a user asks | ||
27 | /// for the list of available assists. In the second phase, the user picks a | ||
28 | /// particular assist and it gets applied. | ||
29 | /// | ||
30 | /// There are two peculiarities here: | ||
31 | /// | ||
32 | /// * first, we ideally avoid computing more things then necessary to answer "is | ||
33 | /// assist applicable" in the first phase. | ||
34 | /// * second, when we are applying assist, we don't have a guarantee that there | ||
35 | /// weren't any changes between the point when user asked for assists and when | ||
36 | /// they applied a particular assist. So, when applying assist, we need to do | ||
37 | /// all the checks from scratch. | ||
38 | /// | ||
39 | /// To avoid repeating the same code twice for both "check" and "apply" | ||
40 | /// functions, we use an approach reminiscent of that of Django's function based | ||
41 | /// views dealing with forms. Each assist receives a runtime parameter, | ||
42 | /// `resolve`. It first check if an edit is applicable (potentially computing | ||
43 | /// info required to compute the actual edit). If it is applicable, and | ||
44 | /// `resolve` is `true`, it then computes the actual edit. | ||
45 | /// | ||
46 | /// So, to implement the original assists workflow, we can first apply each edit | ||
47 | /// with `resolve = false`, and then applying the selected edit again, with | ||
48 | /// `resolve = true` this time. | ||
49 | /// | ||
50 | /// Note, however, that we don't actually use such two-phase logic at the | ||
51 | /// moment, because the LSP API is pretty awkward in this place, and it's much | ||
52 | /// easier to just compute the edit eagerly :-) | ||
53 | pub(crate) struct AssistContext<'a> { | ||
54 | pub(crate) config: &'a AssistConfig, | ||
55 | pub(crate) sema: Semantics<'a, RootDatabase>, | ||
56 | pub(crate) db: &'a RootDatabase, | ||
57 | pub(crate) frange: FileRange, | ||
58 | source_file: SourceFile, | ||
59 | } | ||
60 | |||
61 | impl<'a> AssistContext<'a> { | ||
62 | pub(crate) fn new( | ||
63 | sema: Semantics<'a, RootDatabase>, | ||
64 | config: &'a AssistConfig, | ||
65 | frange: FileRange, | ||
66 | ) -> AssistContext<'a> { | ||
67 | let source_file = sema.parse(frange.file_id); | ||
68 | let db = sema.db; | ||
69 | AssistContext { config, sema, db, frange, source_file } | ||
70 | } | ||
71 | |||
72 | // NB, this ignores active selection. | ||
73 | pub(crate) fn offset(&self) -> TextSize { | ||
74 | self.frange.range.start() | ||
75 | } | ||
76 | |||
77 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { | ||
78 | self.source_file.syntax().token_at_offset(self.offset()) | ||
79 | } | ||
80 | pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> { | ||
81 | self.token_at_offset().find(|it| it.kind() == kind) | ||
82 | } | ||
83 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { | ||
84 | find_node_at_offset(self.source_file.syntax(), self.offset()) | ||
85 | } | ||
86 | pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { | ||
87 | self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset()) | ||
88 | } | ||
89 | pub(crate) fn covering_element(&self) -> SyntaxElement { | ||
90 | find_covering_element(self.source_file.syntax(), self.frange.range) | ||
91 | } | ||
92 | // FIXME: remove | ||
93 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { | ||
94 | find_covering_element(self.source_file.syntax(), range) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | pub(crate) struct Assists { | ||
99 | resolve: bool, | ||
100 | file: FileId, | ||
101 | buf: Vec<(Assist, Option<SourceChange>)>, | ||
102 | } | ||
103 | |||
104 | impl Assists { | ||
105 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | ||
106 | Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } | ||
107 | } | ||
108 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | ||
109 | Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } | ||
110 | } | ||
111 | |||
112 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | ||
113 | assert!(!self.resolve); | ||
114 | self.finish() | ||
115 | .into_iter() | ||
116 | .map(|(label, edit)| { | ||
117 | assert!(edit.is_none()); | ||
118 | label | ||
119 | }) | ||
120 | .collect() | ||
121 | } | ||
122 | |||
123 | pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> { | ||
124 | assert!(self.resolve); | ||
125 | self.finish() | ||
126 | .into_iter() | ||
127 | .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() }) | ||
128 | .collect() | ||
129 | } | ||
130 | |||
131 | pub(crate) fn add( | ||
132 | &mut self, | ||
133 | id: AssistId, | ||
134 | label: impl Into<String>, | ||
135 | target: TextRange, | ||
136 | f: impl FnOnce(&mut AssistBuilder), | ||
137 | ) -> Option<()> { | ||
138 | let label = Assist::new(id, label.into(), None, target); | ||
139 | self.add_impl(label, f) | ||
140 | } | ||
141 | pub(crate) fn add_group( | ||
142 | &mut self, | ||
143 | group: &GroupLabel, | ||
144 | id: AssistId, | ||
145 | label: impl Into<String>, | ||
146 | target: TextRange, | ||
147 | f: impl FnOnce(&mut AssistBuilder), | ||
148 | ) -> Option<()> { | ||
149 | let label = Assist::new(id, label.into(), Some(group.clone()), target); | ||
150 | self.add_impl(label, f) | ||
151 | } | ||
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 { | ||
155 | let mut builder = AssistBuilder::new(self.file); | ||
156 | f(&mut builder); | ||
157 | Some(builder.finish(change_label)) | ||
158 | } else { | ||
159 | None | ||
160 | }; | ||
161 | |||
162 | self.buf.push((label, source_change)); | ||
163 | Some(()) | ||
164 | } | ||
165 | |||
166 | fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> { | ||
167 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | ||
168 | self.buf | ||
169 | } | ||
170 | } | ||
171 | |||
172 | pub(crate) struct AssistBuilder { | ||
173 | edit: TextEditBuilder, | ||
174 | file: FileId, | ||
175 | is_snippet: bool, | ||
176 | } | ||
177 | |||
178 | impl AssistBuilder { | ||
179 | pub(crate) fn new(file: FileId) -> AssistBuilder { | ||
180 | AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } | ||
181 | } | ||
182 | |||
183 | /// Remove specified `range` of text. | ||
184 | pub(crate) fn delete(&mut self, range: TextRange) { | ||
185 | self.edit.delete(range) | ||
186 | } | ||
187 | /// Append specified `text` at the given `offset` | ||
188 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { | ||
189 | self.edit.insert(offset, text.into()) | ||
190 | } | ||
191 | /// Append specified `snippet` at the given `offset` | ||
192 | pub(crate) fn insert_snippet( | ||
193 | &mut self, | ||
194 | _cap: SnippetCap, | ||
195 | offset: TextSize, | ||
196 | snippet: impl Into<String>, | ||
197 | ) { | ||
198 | self.is_snippet = true; | ||
199 | self.insert(offset, snippet); | ||
200 | } | ||
201 | /// Replaces specified `range` of text with a given string. | ||
202 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | ||
203 | self.edit.replace(range, replace_with.into()) | ||
204 | } | ||
205 | /// Replaces specified `range` of text with a given `snippet`. | ||
206 | pub(crate) fn replace_snippet( | ||
207 | &mut self, | ||
208 | _cap: SnippetCap, | ||
209 | range: TextRange, | ||
210 | snippet: impl Into<String>, | ||
211 | ) { | ||
212 | self.is_snippet = true; | ||
213 | self.replace(range, snippet); | ||
214 | } | ||
215 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | ||
216 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | ||
217 | } | ||
218 | /// Replaces specified `node` of text with a given string, reindenting the | ||
219 | /// string to maintain `node`'s existing indent. | ||
220 | // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent | ||
221 | pub(crate) fn replace_node_and_indent( | ||
222 | &mut self, | ||
223 | node: &SyntaxNode, | ||
224 | replace_with: impl Into<String>, | ||
225 | ) { | ||
226 | let mut replace_with = replace_with.into(); | ||
227 | if let Some(indent) = leading_indent(node) { | ||
228 | replace_with = reindent(&replace_with, &indent) | ||
229 | } | ||
230 | self.replace(node.text_range(), replace_with) | ||
231 | } | ||
232 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | ||
233 | let node = rewriter.rewrite_root().unwrap(); | ||
234 | let new = rewriter.rewrite(&node); | ||
235 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | ||
236 | } | ||
237 | |||
238 | // FIXME: better API | ||
239 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | ||
240 | self.file = assist_file; | ||
241 | } | ||
242 | |||
243 | // FIXME: kill this API | ||
244 | /// Get access to the raw `TextEditBuilder`. | ||
245 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | ||
246 | &mut self.edit | ||
247 | } | ||
248 | |||
249 | fn finish(self, change_label: String) -> SourceChange { | ||
250 | let edit = self.edit.finish(); | ||
251 | let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file); | ||
252 | if self.is_snippet { | ||
253 | res.is_snippet = true; | ||
254 | } | ||
255 | res | ||
256 | } | ||
257 | } | ||
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs deleted file mode 100644 index 2fe7c3de3..000000000 --- a/crates/ra_assists/src/assist_ctx.rs +++ /dev/null | |||
@@ -1,257 +0,0 @@ | |||
1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. | ||
2 | use hir::Semantics; | ||
3 | use ra_db::FileRange; | ||
4 | use ra_fmt::{leading_indent, reindent}; | ||
5 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | ||
7 | algo::{self, find_covering_element, find_node_at_offset}, | ||
8 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, | ||
9 | TokenAtOffset, | ||
10 | }; | ||
11 | use ra_text_edit::TextEditBuilder; | ||
12 | |||
13 | use crate::{AssistAction, AssistFile, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; | ||
14 | use algo::SyntaxRewriter; | ||
15 | |||
16 | #[derive(Clone, Debug)] | ||
17 | pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); | ||
18 | |||
19 | #[derive(Clone, Debug)] | ||
20 | pub(crate) struct AssistInfo { | ||
21 | pub(crate) label: AssistLabel, | ||
22 | pub(crate) group_label: Option<GroupLabel>, | ||
23 | pub(crate) action: Option<AssistAction>, | ||
24 | } | ||
25 | |||
26 | impl AssistInfo { | ||
27 | fn new(label: AssistLabel) -> AssistInfo { | ||
28 | AssistInfo { label, group_label: None, action: None } | ||
29 | } | ||
30 | |||
31 | fn resolved(self, action: AssistAction) -> AssistInfo { | ||
32 | AssistInfo { action: Some(action), ..self } | ||
33 | } | ||
34 | |||
35 | fn with_group(self, group_label: GroupLabel) -> AssistInfo { | ||
36 | AssistInfo { group_label: Some(group_label), ..self } | ||
37 | } | ||
38 | |||
39 | pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> { | ||
40 | let label = self.label; | ||
41 | let group_label = self.group_label; | ||
42 | self.action.map(|action| ResolvedAssist { label, group_label, action }) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>; | ||
47 | |||
48 | /// `AssistCtx` allows to apply an assist or check if it could be applied. | ||
49 | /// | ||
50 | /// Assists use a somewhat over-engineered approach, given the current needs. The | ||
51 | /// assists workflow consists of two phases. In the first phase, a user asks for | ||
52 | /// the list of available assists. In the second phase, the user picks a | ||
53 | /// particular assist and it gets applied. | ||
54 | /// | ||
55 | /// There are two peculiarities here: | ||
56 | /// | ||
57 | /// * first, we ideally avoid computing more things then necessary to answer | ||
58 | /// "is assist applicable" in the first phase. | ||
59 | /// * second, when we are applying assist, we don't have a guarantee that there | ||
60 | /// weren't any changes between the point when user asked for assists and when | ||
61 | /// they applied a particular assist. So, when applying assist, we need to do | ||
62 | /// all the checks from scratch. | ||
63 | /// | ||
64 | /// To avoid repeating the same code twice for both "check" and "apply" | ||
65 | /// functions, we use an approach reminiscent of that of Django's function based | ||
66 | /// views dealing with forms. Each assist receives a runtime parameter, | ||
67 | /// `should_compute_edit`. It first check if an edit is applicable (potentially | ||
68 | /// computing info required to compute the actual edit). If it is applicable, | ||
69 | /// and `should_compute_edit` is `true`, it then computes the actual edit. | ||
70 | /// | ||
71 | /// So, to implement the original assists workflow, we can first apply each edit | ||
72 | /// with `should_compute_edit = false`, and then applying the selected edit | ||
73 | /// again, with `should_compute_edit = true` this time. | ||
74 | /// | ||
75 | /// Note, however, that we don't actually use such two-phase logic at the | ||
76 | /// moment, because the LSP API is pretty awkward in this place, and it's much | ||
77 | /// easier to just compute the edit eagerly :-) | ||
78 | #[derive(Clone)] | ||
79 | pub(crate) struct AssistCtx<'a> { | ||
80 | pub(crate) sema: &'a Semantics<'a, RootDatabase>, | ||
81 | pub(crate) db: &'a RootDatabase, | ||
82 | pub(crate) frange: FileRange, | ||
83 | source_file: SourceFile, | ||
84 | should_compute_edit: bool, | ||
85 | } | ||
86 | |||
87 | impl<'a> AssistCtx<'a> { | ||
88 | pub fn new( | ||
89 | sema: &'a Semantics<'a, RootDatabase>, | ||
90 | frange: FileRange, | ||
91 | should_compute_edit: bool, | ||
92 | ) -> AssistCtx<'a> { | ||
93 | let source_file = sema.parse(frange.file_id); | ||
94 | AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit } | ||
95 | } | ||
96 | |||
97 | pub(crate) fn add_assist( | ||
98 | self, | ||
99 | id: AssistId, | ||
100 | label: impl Into<String>, | ||
101 | f: impl FnOnce(&mut ActionBuilder), | ||
102 | ) -> Option<Assist> { | ||
103 | let label = AssistLabel::new(label.into(), id); | ||
104 | |||
105 | let mut info = AssistInfo::new(label); | ||
106 | if self.should_compute_edit { | ||
107 | let action = { | ||
108 | let mut edit = ActionBuilder::default(); | ||
109 | f(&mut edit); | ||
110 | edit.build() | ||
111 | }; | ||
112 | info = info.resolved(action) | ||
113 | }; | ||
114 | |||
115 | Some(Assist(vec![info])) | ||
116 | } | ||
117 | |||
118 | pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> { | ||
119 | AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() } | ||
120 | } | ||
121 | |||
122 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { | ||
123 | self.source_file.syntax().token_at_offset(self.frange.range.start()) | ||
124 | } | ||
125 | |||
126 | pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> { | ||
127 | self.token_at_offset().find(|it| it.kind() == kind) | ||
128 | } | ||
129 | |||
130 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { | ||
131 | find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) | ||
132 | } | ||
133 | pub(crate) fn covering_element(&self) -> SyntaxElement { | ||
134 | find_covering_element(self.source_file.syntax(), self.frange.range) | ||
135 | } | ||
136 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { | ||
137 | find_covering_element(self.source_file.syntax(), range) | ||
138 | } | ||
139 | } | ||
140 | |||
141 | pub(crate) struct AssistGroup<'a> { | ||
142 | ctx: AssistCtx<'a>, | ||
143 | group_name: String, | ||
144 | assists: Vec<AssistInfo>, | ||
145 | } | ||
146 | |||
147 | impl<'a> AssistGroup<'a> { | ||
148 | pub(crate) fn add_assist( | ||
149 | &mut self, | ||
150 | id: AssistId, | ||
151 | label: impl Into<String>, | ||
152 | f: impl FnOnce(&mut ActionBuilder), | ||
153 | ) { | ||
154 | let label = AssistLabel::new(label.into(), id); | ||
155 | |||
156 | let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); | ||
157 | if self.ctx.should_compute_edit { | ||
158 | let action = { | ||
159 | let mut edit = ActionBuilder::default(); | ||
160 | f(&mut edit); | ||
161 | edit.build() | ||
162 | }; | ||
163 | info = info.resolved(action) | ||
164 | }; | ||
165 | |||
166 | self.assists.push(info) | ||
167 | } | ||
168 | |||
169 | pub(crate) fn finish(self) -> Option<Assist> { | ||
170 | if self.assists.is_empty() { | ||
171 | None | ||
172 | } else { | ||
173 | Some(Assist(self.assists)) | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | #[derive(Default)] | ||
179 | pub(crate) struct ActionBuilder { | ||
180 | edit: TextEditBuilder, | ||
181 | cursor_position: Option<TextSize>, | ||
182 | target: Option<TextRange>, | ||
183 | file: AssistFile, | ||
184 | } | ||
185 | |||
186 | impl ActionBuilder { | ||
187 | /// Replaces specified `range` of text with a given string. | ||
188 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | ||
189 | self.edit.replace(range, replace_with.into()) | ||
190 | } | ||
191 | |||
192 | /// Replaces specified `node` of text with a given string, reindenting the | ||
193 | /// string to maintain `node`'s existing indent. | ||
194 | // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent | ||
195 | pub(crate) fn replace_node_and_indent( | ||
196 | &mut self, | ||
197 | node: &SyntaxNode, | ||
198 | replace_with: impl Into<String>, | ||
199 | ) { | ||
200 | let mut replace_with = replace_with.into(); | ||
201 | if let Some(indent) = leading_indent(node) { | ||
202 | replace_with = reindent(&replace_with, &indent) | ||
203 | } | ||
204 | self.replace(node.text_range(), replace_with) | ||
205 | } | ||
206 | |||
207 | /// Remove specified `range` of text. | ||
208 | #[allow(unused)] | ||
209 | pub(crate) fn delete(&mut self, range: TextRange) { | ||
210 | self.edit.delete(range) | ||
211 | } | ||
212 | |||
213 | /// Append specified `text` at the given `offset` | ||
214 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { | ||
215 | self.edit.insert(offset, text.into()) | ||
216 | } | ||
217 | |||
218 | /// Specify desired position of the cursor after the assist is applied. | ||
219 | pub(crate) fn set_cursor(&mut self, offset: TextSize) { | ||
220 | self.cursor_position = Some(offset) | ||
221 | } | ||
222 | |||
223 | /// Specify that the assist should be active withing the `target` range. | ||
224 | /// | ||
225 | /// Target ranges are used to sort assists: the smaller the target range, | ||
226 | /// the more specific assist is, and so it should be sorted first. | ||
227 | pub(crate) fn target(&mut self, target: TextRange) { | ||
228 | self.target = Some(target) | ||
229 | } | ||
230 | |||
231 | /// Get access to the raw `TextEditBuilder`. | ||
232 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | ||
233 | &mut self.edit | ||
234 | } | ||
235 | |||
236 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | ||
237 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | ||
238 | } | ||
239 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | ||
240 | let node = rewriter.rewrite_root().unwrap(); | ||
241 | let new = rewriter.rewrite(&node); | ||
242 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | ||
243 | } | ||
244 | |||
245 | pub(crate) fn set_file(&mut self, assist_file: AssistFile) { | ||
246 | self.file = assist_file | ||
247 | } | ||
248 | |||
249 | fn build(self) -> AssistAction { | ||
250 | AssistAction { | ||
251 | edit: self.edit.finish(), | ||
252 | cursor_position: self.cursor_position, | ||
253 | target: self.target, | ||
254 | file: self.file, | ||
255 | } | ||
256 | } | ||
257 | } | ||
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 9ac65ab39..3079a02a2 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. | 1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{PathResolution, SemanticsScope}; | 4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::SyntaxRewriter, | 7 | algo::SyntaxRewriter, |
@@ -51,7 +51,27 @@ impl<'a> SubstituteTypeParams<'a> { | |||
51 | .into_iter() | 51 | .into_iter() |
52 | // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky | 52 | // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky |
53 | .skip(1) | 53 | .skip(1) |
54 | .zip(substs.into_iter()) | 54 | // The actual list of trait type parameters may be longer than the one |
55 | // used in the `impl` block due to trailing default type parametrs. | ||
56 | // For that case we extend the `substs` with an empty iterator so we | ||
57 | // can still hit those trailing values and check if they actually have | ||
58 | // a default type. If they do, go for that type from `hir` to `ast` so | ||
59 | // the resulting change can be applied correctly. | ||
60 | .zip(substs.into_iter().map(Some).chain(std::iter::repeat(None))) | ||
61 | .filter_map(|(k, v)| match v { | ||
62 | Some(v) => Some((k, v)), | ||
63 | None => { | ||
64 | let default = k.default(source_scope.db)?; | ||
65 | Some(( | ||
66 | k, | ||
67 | ast::make::type_ref( | ||
68 | &default | ||
69 | .display_source_code(source_scope.db, source_scope.module()?.into()) | ||
70 | .ok()?, | ||
71 | ), | ||
72 | )) | ||
73 | } | ||
74 | }) | ||
55 | .collect(); | 75 | .collect(); |
56 | return SubstituteTypeParams { | 76 | return SubstituteTypeParams { |
57 | source_scope, | 77 | source_scope, |
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs deleted file mode 100644 index c0f9bc1fb..000000000 --- a/crates/ra_assists/src/doc_tests.rs +++ /dev/null | |||
@@ -1,35 +0,0 @@ | |||
1 | //! Each assist definition has a special comment, which specifies docs and | ||
2 | //! example. | ||
3 | //! | ||
4 | //! We collect all the example and write the as tests in this module. | ||
5 | |||
6 | mod generated; | ||
7 | |||
8 | use ra_db::FileRange; | ||
9 | use test_utils::{assert_eq_text, extract_range_or_offset}; | ||
10 | |||
11 | use crate::resolved_assists; | ||
12 | |||
13 | fn check(assist_id: &str, before: &str, after: &str) { | ||
14 | let (selection, before) = extract_range_or_offset(before); | ||
15 | let (db, file_id) = crate::helpers::with_single_file(&before); | ||
16 | let frange = FileRange { file_id, range: selection.into() }; | ||
17 | |||
18 | let assist = resolved_assists(&db, frange) | ||
19 | .into_iter() | ||
20 | .find(|assist| assist.label.id.0 == assist_id) | ||
21 | .unwrap_or_else(|| { | ||
22 | panic!( | ||
23 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | ||
24 | assist_id, | ||
25 | resolved_assists(&db, frange) | ||
26 | .into_iter() | ||
27 | .map(|assist| assist.label.id.0) | ||
28 | .collect::<Vec<_>>() | ||
29 | .join(", ") | ||
30 | ) | ||
31 | }); | ||
32 | |||
33 | let actual = assist.action.edit.apply(&before); | ||
34 | assert_eq_text!(after, &actual); | ||
35 | } | ||
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 4ea26a550..fa70c8496 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs | |||
@@ -6,7 +6,10 @@ use ra_syntax::{ | |||
6 | }; | 6 | }; |
7 | use stdx::SepBy; | 7 | use stdx::SepBy; |
8 | 8 | ||
9 | use crate::{Assist, AssistCtx, AssistId}; | 9 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | ||
11 | AssistId, | ||
12 | }; | ||
10 | 13 | ||
11 | // Assist: add_custom_impl | 14 | // Assist: add_custom_impl |
12 | // | 15 | // |
@@ -22,10 +25,10 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
22 | // struct S; | 25 | // struct S; |
23 | // | 26 | // |
24 | // impl Debug for S { | 27 | // impl Debug for S { |
25 | // | 28 | // $0 |
26 | // } | 29 | // } |
27 | // ``` | 30 | // ``` |
28 | pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { | 31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
29 | let input = ctx.find_node_at_offset::<ast::AttrInput>()?; | 32 | let input = ctx.find_node_at_offset::<ast::AttrInput>()?; |
30 | let attr = input.syntax().parent().and_then(ast::Attr::cast)?; | 33 | let attr = input.syntax().parent().and_then(ast::Attr::cast)?; |
31 | 34 | ||
@@ -46,11 +49,10 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { | |||
46 | let start_offset = annotated.syntax().parent()?.text_range().end(); | 49 | let start_offset = annotated.syntax().parent()?.text_range().end(); |
47 | 50 | ||
48 | let label = | 51 | let label = |
49 | format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); | 52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); |
50 | |||
51 | ctx.add_assist(AssistId("add_custom_impl"), label, |edit| { | ||
52 | edit.target(attr.syntax().text_range()); | ||
53 | 53 | ||
54 | let target = attr.syntax().text_range(); | ||
55 | acc.add(AssistId("add_custom_impl"), label, target, |builder| { | ||
54 | let new_attr_input = input | 56 | let new_attr_input = input |
55 | .syntax() | 57 | .syntax() |
56 | .descendants_with_tokens() | 58 | .descendants_with_tokens() |
@@ -61,20 +63,11 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { | |||
61 | let has_more_derives = !new_attr_input.is_empty(); | 63 | let has_more_derives = !new_attr_input.is_empty(); |
62 | let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); | 64 | let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); |
63 | 65 | ||
64 | let mut buf = String::new(); | 66 | if has_more_derives { |
65 | buf.push_str("\n\nimpl "); | 67 | builder.replace(input.syntax().text_range(), new_attr_input); |
66 | buf.push_str(trait_token.text().as_str()); | ||
67 | buf.push_str(" for "); | ||
68 | buf.push_str(annotated_name.as_str()); | ||
69 | buf.push_str(" {\n"); | ||
70 | |||
71 | let cursor_delta = if has_more_derives { | ||
72 | let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input); | ||
73 | edit.replace(input.syntax().text_range(), new_attr_input); | ||
74 | delta | ||
75 | } else { | 68 | } else { |
76 | let attr_range = attr.syntax().text_range(); | 69 | let attr_range = attr.syntax().text_range(); |
77 | edit.delete(attr_range); | 70 | builder.delete(attr_range); |
78 | 71 | ||
79 | let line_break_range = attr | 72 | let line_break_range = attr |
80 | .syntax() | 73 | .syntax() |
@@ -82,20 +75,30 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { | |||
82 | .filter(|t| t.kind() == WHITESPACE) | 75 | .filter(|t| t.kind() == WHITESPACE) |
83 | .map(|t| t.text_range()) | 76 | .map(|t| t.text_range()) |
84 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | 77 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); |
85 | edit.delete(line_break_range); | 78 | builder.delete(line_break_range); |
86 | 79 | } | |
87 | attr_range.len() + line_break_range.len() | 80 | |
88 | }; | 81 | match ctx.config.snippet_cap { |
89 | 82 | Some(cap) => { | |
90 | edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta); | 83 | builder.insert_snippet( |
91 | buf.push_str("\n}"); | 84 | cap, |
92 | edit.insert(start_offset, buf); | 85 | start_offset, |
86 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name), | ||
87 | ); | ||
88 | } | ||
89 | None => { | ||
90 | builder.insert( | ||
91 | start_offset, | ||
92 | format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name), | ||
93 | ); | ||
94 | } | ||
95 | } | ||
93 | }) | 96 | }) |
94 | } | 97 | } |
95 | 98 | ||
96 | #[cfg(test)] | 99 | #[cfg(test)] |
97 | mod tests { | 100 | mod tests { |
98 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 101 | use crate::tests::{check_assist, check_assist_not_applicable}; |
99 | 102 | ||
100 | use super::*; | 103 | use super::*; |
101 | 104 | ||
@@ -115,7 +118,7 @@ struct Foo { | |||
115 | } | 118 | } |
116 | 119 | ||
117 | impl Debug for Foo { | 120 | impl Debug for Foo { |
118 | <|> | 121 | $0 |
119 | } | 122 | } |
120 | ", | 123 | ", |
121 | ) | 124 | ) |
@@ -137,7 +140,7 @@ pub struct Foo { | |||
137 | } | 140 | } |
138 | 141 | ||
139 | impl Debug for Foo { | 142 | impl Debug for Foo { |
140 | <|> | 143 | $0 |
141 | } | 144 | } |
142 | ", | 145 | ", |
143 | ) | 146 | ) |
@@ -156,7 +159,7 @@ struct Foo {} | |||
156 | struct Foo {} | 159 | struct Foo {} |
157 | 160 | ||
158 | impl Debug for Foo { | 161 | impl Debug for Foo { |
159 | <|> | 162 | $0 |
160 | } | 163 | } |
161 | ", | 164 | ", |
162 | ) | 165 | ) |
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs index 6254eb7c4..b123b8498 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/add_derive.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextSize, | 4 | TextSize, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: add_derive | 9 | // Assist: add_derive |
10 | // | 10 | // |
@@ -18,31 +18,37 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
18 | // ``` | 18 | // ``` |
19 | // -> | 19 | // -> |
20 | // ``` | 20 | // ``` |
21 | // #[derive()] | 21 | // #[derive($0)] |
22 | // struct Point { | 22 | // struct Point { |
23 | // x: u32, | 23 | // x: u32, |
24 | // y: u32, | 24 | // y: u32, |
25 | // } | 25 | // } |
26 | // ``` | 26 | // ``` |
27 | pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> { | 27 | pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
28 | let cap = ctx.config.snippet_cap?; | ||
28 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | 29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; |
29 | let node_start = derive_insertion_offset(&nominal)?; | 30 | let node_start = derive_insertion_offset(&nominal)?; |
30 | ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", |edit| { | 31 | let target = nominal.syntax().text_range(); |
32 | acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { | ||
31 | let derive_attr = nominal | 33 | let derive_attr = nominal |
32 | .attrs() | 34 | .attrs() |
33 | .filter_map(|x| x.as_simple_call()) | 35 | .filter_map(|x| x.as_simple_call()) |
34 | .filter(|(name, _arg)| name == "derive") | 36 | .filter(|(name, _arg)| name == "derive") |
35 | .map(|(_name, arg)| arg) | 37 | .map(|(_name, arg)| arg) |
36 | .next(); | 38 | .next(); |
37 | let offset = match derive_attr { | 39 | match derive_attr { |
38 | None => { | 40 | None => { |
39 | edit.insert(node_start, "#[derive()]\n"); | 41 | builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); |
40 | node_start + TextSize::of("#[derive(") | 42 | } |
43 | Some(tt) => { | ||
44 | // Just move the cursor. | ||
45 | builder.insert_snippet( | ||
46 | cap, | ||
47 | tt.syntax().text_range().end() - TextSize::of(')'), | ||
48 | "$0", | ||
49 | ) | ||
41 | } | 50 | } |
42 | Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'), | ||
43 | }; | 51 | }; |
44 | edit.target(nominal.syntax().text_range()); | ||
45 | edit.set_cursor(offset) | ||
46 | }) | 52 | }) |
47 | } | 53 | } |
48 | 54 | ||
@@ -57,20 +63,21 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextSize> { | |||
57 | 63 | ||
58 | #[cfg(test)] | 64 | #[cfg(test)] |
59 | mod tests { | 65 | mod tests { |
66 | use crate::tests::{check_assist, check_assist_target}; | ||
67 | |||
60 | use super::*; | 68 | use super::*; |
61 | use crate::helpers::{check_assist, check_assist_target}; | ||
62 | 69 | ||
63 | #[test] | 70 | #[test] |
64 | fn add_derive_new() { | 71 | fn add_derive_new() { |
65 | check_assist( | 72 | check_assist( |
66 | add_derive, | 73 | add_derive, |
67 | "struct Foo { a: i32, <|>}", | 74 | "struct Foo { a: i32, <|>}", |
68 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 75 | "#[derive($0)]\nstruct Foo { a: i32, }", |
69 | ); | 76 | ); |
70 | check_assist( | 77 | check_assist( |
71 | add_derive, | 78 | add_derive, |
72 | "struct Foo { <|> a: i32, }", | 79 | "struct Foo { <|> a: i32, }", |
73 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 80 | "#[derive($0)]\nstruct Foo { a: i32, }", |
74 | ); | 81 | ); |
75 | } | 82 | } |
76 | 83 | ||
@@ -79,7 +86,7 @@ mod tests { | |||
79 | check_assist( | 86 | check_assist( |
80 | add_derive, | 87 | add_derive, |
81 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | 88 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", |
82 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | 89 | "#[derive(Clone$0)]\nstruct Foo { a: i32, }", |
83 | ); | 90 | ); |
84 | } | 91 | } |
85 | 92 | ||
@@ -95,7 +102,7 @@ struct Foo { a: i32<|>, } | |||
95 | " | 102 | " |
96 | /// `Foo` is a pretty important struct. | 103 | /// `Foo` is a pretty important struct. |
97 | /// It does stuff. | 104 | /// It does stuff. |
98 | #[derive(<|>)] | 105 | #[derive($0)] |
99 | struct Foo { a: i32, } | 106 | struct Foo { a: i32, } |
100 | ", | 107 | ", |
101 | ); | 108 | ); |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index bc313782b..ab20c6649 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextRange, | 4 | TextRange, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: add_explicit_type | 9 | // Assist: add_explicit_type |
10 | // | 10 | // |
@@ -21,12 +21,12 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
21 | // let x: i32 = 92; | 21 | // let x: i32 = 92; |
22 | // } | 22 | // } |
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | 24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | ||
26 | let expr = stmt.initializer()?; | 27 | let expr = stmt.initializer()?; |
27 | let pat = stmt.pat()?; | ||
28 | // Must be a binding | 28 | // Must be a binding |
29 | let pat = match pat { | 29 | let pat = match stmt.pat()? { |
30 | ast::Pat::BindPat(bind_pat) => bind_pat, | 30 | ast::Pat::BindPat(bind_pat) => bind_pat, |
31 | _ => return None, | 31 | _ => return None, |
32 | }; | 32 | }; |
@@ -45,7 +45,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
45 | // Assist not applicable if the type has already been specified | 45 | // Assist not applicable if the type has already been specified |
46 | // and it has no placeholders | 46 | // and it has no placeholders |
47 | let ascribed_ty = stmt.ascribed_type(); | 47 | let ascribed_ty = stmt.ascribed_type(); |
48 | if let Some(ref ty) = ascribed_ty { | 48 | if let Some(ty) = &ascribed_ty { |
49 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { | 49 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { |
50 | return None; | 50 | return None; |
51 | } | 51 | } |
@@ -57,17 +57,17 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
57 | return None; | 57 | return None; |
58 | } | 58 | } |
59 | 59 | ||
60 | let db = ctx.db; | 60 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; |
61 | let new_type_string = ty.display_truncated(db, None).to_string(); | 61 | acc.add( |
62 | ctx.add_assist( | ||
63 | AssistId("add_explicit_type"), | 62 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", new_type_string), | 63 | format!("Insert explicit type `{}`", inferred_type), |
65 | |edit| { | 64 | pat_range, |
66 | edit.target(pat_range); | 65 | |builder| match ascribed_ty { |
67 | if let Some(ascribed_ty) = ascribed_ty { | 66 | Some(ascribed_ty) => { |
68 | edit.replace(ascribed_ty.syntax().text_range(), new_type_string); | 67 | builder.replace(ascribed_ty.syntax().text_range(), inferred_type); |
69 | } else { | 68 | } |
70 | edit.insert(name_range.end(), format!(": {}", new_type_string)); | 69 | None => { |
70 | builder.insert(name_range.end(), format!(": {}", inferred_type)); | ||
71 | } | 71 | } |
72 | }, | 72 | }, |
73 | ) | 73 | ) |
@@ -77,7 +77,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
77 | mod tests { | 77 | mod tests { |
78 | use super::*; | 78 | use super::*; |
79 | 79 | ||
80 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | 80 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
81 | 81 | ||
82 | #[test] | 82 | #[test] |
83 | fn add_explicit_type_target() { | 83 | fn add_explicit_type_target() { |
@@ -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 | ||
210 | fn main() { | 206 | fn 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 49deb6701..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 | |||
@@ -1,12 +1,8 @@ | |||
1 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | ast::{self, AstNode, NameOwner}, | 3 | use test_utils::mark; |
4 | TextSize, | ||
5 | }; | ||
6 | use stdx::format_to; | ||
7 | 4 | ||
8 | use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId}; | 5 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; |
9 | use test_utils::tested_by; | ||
10 | 6 | ||
11 | // Assist add_from_impl_for_enum | 7 | // Assist add_from_impl_for_enum |
12 | // | 8 | // |
@@ -25,7 +21,7 @@ use test_utils::tested_by; | |||
25 | // } | 21 | // } |
26 | // } | 22 | // } |
27 | // ``` | 23 | // ``` |
28 | pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { | 24 | pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
29 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | 25 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; |
30 | let variant_name = variant.name()?; | 26 | let variant_name = variant.name()?; |
31 | let enum_name = variant.parent_enum().name()?; | 27 | let enum_name = variant.parent_enum().name()?; |
@@ -38,23 +34,23 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { | |||
38 | } | 34 | } |
39 | let field_type = field_list.fields().next()?.type_ref()?; | 35 | let field_type = field_list.fields().next()?.type_ref()?; |
40 | let path = match field_type { | 36 | let path = match field_type { |
41 | ast::TypeRef::PathType(p) => p, | 37 | ast::TypeRef::PathType(it) => it, |
42 | _ => return None, | 38 | _ => return None, |
43 | }; | 39 | }; |
44 | 40 | ||
45 | if existing_from_impl(ctx.sema, &variant).is_some() { | 41 | if existing_from_impl(&ctx.sema, &variant).is_some() { |
46 | tested_by!(test_add_from_impl_already_exists); | 42 | mark::hit!(test_add_from_impl_already_exists); |
47 | return None; | 43 | return None; |
48 | } | 44 | } |
49 | 45 | ||
50 | ctx.add_assist( | 46 | let target = variant.syntax().text_range(); |
47 | acc.add( | ||
51 | AssistId("add_from_impl_for_enum"), | 48 | AssistId("add_from_impl_for_enum"), |
52 | "Add From impl for this enum variant", | 49 | "Add From impl for this enum variant", |
50 | target, | ||
53 | |edit| { | 51 | |edit| { |
54 | let start_offset = variant.parent_enum().syntax().text_range().end(); | 52 | let start_offset = variant.parent_enum().syntax().text_range().end(); |
55 | let mut buf = String::new(); | 53 | let buf = format!( |
56 | format_to!( | ||
57 | buf, | ||
58 | r#" | 54 | r#" |
59 | 55 | ||
60 | impl From<{0}> for {1} {{ | 56 | impl From<{0}> for {1} {{ |
@@ -67,7 +63,6 @@ impl From<{0}> for {1} {{ | |||
67 | variant_name | 63 | variant_name |
68 | ); | 64 | ); |
69 | edit.insert(start_offset, buf); | 65 | edit.insert(start_offset, buf); |
70 | edit.set_cursor(start_offset + TextSize::of("\n\n")); | ||
71 | }, | 66 | }, |
72 | ) | 67 | ) |
73 | } | 68 | } |
@@ -95,10 +90,11 @@ fn existing_from_impl( | |||
95 | 90 | ||
96 | #[cfg(test)] | 91 | #[cfg(test)] |
97 | mod tests { | 92 | mod tests { |
98 | use super::*; | 93 | use test_utils::mark; |
94 | |||
95 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
99 | 96 | ||
100 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 97 | use super::*; |
101 | use test_utils::covers; | ||
102 | 98 | ||
103 | #[test] | 99 | #[test] |
104 | fn test_add_from_impl_for_enum() { | 100 | fn test_add_from_impl_for_enum() { |
@@ -107,7 +103,7 @@ mod tests { | |||
107 | "enum A { <|>One(u32) }", | 103 | "enum A { <|>One(u32) }", |
108 | r#"enum A { One(u32) } | 104 | r#"enum A { One(u32) } |
109 | 105 | ||
110 | <|>impl From<u32> for A { | 106 | impl From<u32> for A { |
111 | fn from(v: u32) -> Self { | 107 | fn from(v: u32) -> Self { |
112 | A::One(v) | 108 | A::One(v) |
113 | } | 109 | } |
@@ -119,10 +115,10 @@ mod tests { | |||
119 | fn test_add_from_impl_for_enum_complicated_path() { | 115 | fn test_add_from_impl_for_enum_complicated_path() { |
120 | check_assist( | 116 | check_assist( |
121 | add_from_impl_for_enum, | 117 | add_from_impl_for_enum, |
122 | "enum A { <|>One(foo::bar::baz::Boo) }", | 118 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, |
123 | r#"enum A { One(foo::bar::baz::Boo) } | 119 | r#"enum A { One(foo::bar::baz::Boo) } |
124 | 120 | ||
125 | <|>impl From<foo::bar::baz::Boo> for A { | 121 | impl From<foo::bar::baz::Boo> for A { |
126 | fn from(v: foo::bar::baz::Boo) -> Self { | 122 | fn from(v: foo::bar::baz::Boo) -> Self { |
127 | A::One(v) | 123 | A::One(v) |
128 | } | 124 | } |
@@ -153,7 +149,7 @@ mod tests { | |||
153 | 149 | ||
154 | #[test] | 150 | #[test] |
155 | fn test_add_from_impl_already_exists() { | 151 | fn test_add_from_impl_already_exists() { |
156 | covers!(test_add_from_impl_already_exists); | 152 | mark::check!(test_add_from_impl_already_exists); |
157 | check_not_applicable( | 153 | check_not_applicable( |
158 | r#" | 154 | r#" |
159 | enum A { <|>One(u32), } | 155 | enum A { <|>One(u32), } |
@@ -184,7 +180,7 @@ pub trait From<T> { | |||
184 | }"#, | 180 | }"#, |
185 | r#"enum A { One(u32), Two(String), } | 181 | r#"enum A { One(u32), Two(String), } |
186 | 182 | ||
187 | <|>impl From<u32> for A { | 183 | impl From<u32> for A { |
188 | fn from(v: u32) -> Self { | 184 | fn from(v: u32) -> Self { |
189 | A::One(v) | 185 | A::One(v) |
190 | } | 186 | } |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 6c7456579..24f931a85 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -1,13 +1,21 @@ | |||
1 | use hir::HirDisplay; | ||
2 | use ra_db::FileId; | ||
1 | use ra_syntax::{ | 3 | use ra_syntax::{ |
2 | ast::{self, AstNode}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | make, ArgListOwner, AstNode, ModuleItemOwner, | ||
8 | }, | ||
3 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
4 | }; | 10 | }; |
5 | |||
6 | use crate::{Assist, AssistCtx, AssistFile, AssistId}; | ||
7 | use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; | ||
8 | use hir::HirDisplay; | ||
9 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
10 | 12 | ||
13 | use crate::{ | ||
14 | assist_config::SnippetCap, | ||
15 | utils::{render_snippet, Cursor}, | ||
16 | AssistContext, AssistId, Assists, | ||
17 | }; | ||
18 | |||
11 | // Assist: add_function | 19 | // Assist: add_function |
12 | // | 20 | // |
13 | // Adds a stub function with a signature matching the function under the cursor. | 21 | // Adds a stub function with a signature matching the function under the cursor. |
@@ -29,11 +37,11 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
29 | // } | 37 | // } |
30 | // | 38 | // |
31 | // fn bar(arg: &str, baz: Baz) { | 39 | // fn bar(arg: &str, baz: Baz) { |
32 | // todo!() | 40 | // ${0:todo!()} |
33 | // } | 41 | // } |
34 | // | 42 | // |
35 | // ``` | 43 | // ``` |
36 | pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | 44 | pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
37 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; | 45 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; |
38 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 46 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; |
39 | let path = path_expr.path()?; | 47 | let path = path_expr.path()?; |
@@ -43,36 +51,49 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | |||
43 | return None; | 51 | return None; |
44 | } | 52 | } |
45 | 53 | ||
46 | let target_module = if let Some(qualifier) = path.qualifier() { | 54 | let target_module = match path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = | 55 | Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { |
48 | ctx.sema.resolve_path(&qualifier) | 56 | Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module), |
49 | { | 57 | _ => return None, |
50 | Some(module.definition_source(ctx.sema.db)) | 58 | }, |
51 | } else { | 59 | None => None, |
52 | return None; | ||
53 | } | ||
54 | } else { | ||
55 | None | ||
56 | }; | 60 | }; |
57 | 61 | ||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 62 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
59 | 63 | ||
60 | ctx.add_assist(AssistId("add_function"), "Add function", |edit| { | 64 | let target = call.syntax().text_range(); |
61 | edit.target(call.syntax().text_range()); | 65 | acc.add(AssistId("add_function"), "Add function", target, |builder| { |
62 | 66 | let function_template = function_builder.render(); | |
63 | if let Some(function_template) = function_builder.render() { | 67 | builder.set_file(function_template.file); |
64 | edit.set_file(function_template.file); | 68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
65 | edit.set_cursor(function_template.cursor_offset); | 69 | match ctx.config.snippet_cap { |
66 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); | 70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), |
71 | None => builder.insert(function_template.insert_offset, new_fn), | ||
67 | } | 72 | } |
68 | }) | 73 | }) |
69 | } | 74 | } |
70 | 75 | ||
71 | struct FunctionTemplate { | 76 | struct FunctionTemplate { |
72 | insert_offset: TextSize, | 77 | insert_offset: TextSize, |
73 | cursor_offset: TextSize, | 78 | placeholder_expr: ast::MacroCall, |
74 | fn_def: ast::SourceFile, | 79 | leading_ws: String, |
75 | file: AssistFile, | 80 | fn_def: ast::FnDef, |
81 | trailing_ws: String, | ||
82 | file: FileId, | ||
83 | } | ||
84 | |||
85 | impl FunctionTemplate { | ||
86 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | ||
87 | let f = match cap { | ||
88 | Some(cap) => render_snippet( | ||
89 | cap, | ||
90 | self.fn_def.syntax(), | ||
91 | Cursor::Replace(self.placeholder_expr.syntax()), | ||
92 | ), | ||
93 | None => self.fn_def.to_string(), | ||
94 | }; | ||
95 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | ||
96 | } | ||
76 | } | 97 | } |
77 | 98 | ||
78 | struct FunctionBuilder { | 99 | struct FunctionBuilder { |
@@ -80,68 +101,73 @@ struct FunctionBuilder { | |||
80 | fn_name: ast::Name, | 101 | fn_name: ast::Name, |
81 | type_params: Option<ast::TypeParamList>, | 102 | type_params: Option<ast::TypeParamList>, |
82 | params: ast::ParamList, | 103 | params: ast::ParamList, |
83 | file: AssistFile, | 104 | file: FileId, |
84 | needs_pub: bool, | 105 | needs_pub: bool, |
85 | } | 106 | } |
86 | 107 | ||
87 | impl FunctionBuilder { | 108 | impl FunctionBuilder { |
88 | /// Prepares a generated function that matches `call` in `generate_in` | 109 | /// Prepares a generated function that matches `call`. |
89 | /// (or as close to `call` as possible, if `generate_in` is `None`) | 110 | /// The function is generated in `target_module` or next to `call` |
90 | fn from_call( | 111 | fn from_call( |
91 | ctx: &AssistCtx, | 112 | ctx: &AssistContext, |
92 | call: &ast::CallExpr, | 113 | call: &ast::CallExpr, |
93 | path: &ast::Path, | 114 | path: &ast::Path, |
94 | target_module: Option<hir::InFile<hir::ModuleSource>>, | 115 | target_module: Option<hir::Module>, |
95 | ) -> Option<Self> { | 116 | ) -> Option<Self> { |
96 | let needs_pub = target_module.is_some(); | 117 | let mut file = ctx.frange.file_id; |
97 | let mut file = AssistFile::default(); | 118 | let target = match &target_module { |
98 | let target = if let Some(target_module) = target_module { | 119 | Some(target_module) => { |
99 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; | 120 | let module_source = target_module.definition_source(ctx.db); |
100 | file = in_file; | 121 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
101 | target | 122 | file = in_file; |
102 | } else { | 123 | target |
103 | next_space_for_fn_after_call_site(&call)? | 124 | } |
125 | None => next_space_for_fn_after_call_site(&call)?, | ||
104 | }; | 126 | }; |
127 | let needs_pub = target_module.is_some(); | ||
128 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; | ||
105 | let fn_name = fn_name(&path)?; | 129 | let fn_name = fn_name(&path)?; |
106 | let (type_params, params) = fn_args(ctx, &call)?; | 130 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
131 | |||
107 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 132 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) |
108 | } | 133 | } |
109 | 134 | ||
110 | fn render(self) -> Option<FunctionTemplate> { | 135 | fn render(self) -> FunctionTemplate { |
111 | let placeholder_expr = ast::make::expr_todo(); | 136 | let placeholder_expr = make::expr_todo(); |
112 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); | 137 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
113 | let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); | 138 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
114 | if self.needs_pub { | 139 | let mut fn_def = |
115 | fn_def = ast::make::add_pub_crate_modifier(fn_def); | 140 | make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); |
116 | } | 141 | let leading_ws; |
142 | let trailing_ws; | ||
117 | 143 | ||
118 | let (fn_def, insert_offset) = match self.target { | 144 | let insert_offset = match self.target { |
119 | GeneratedFunctionTarget::BehindItem(it) => { | 145 | GeneratedFunctionTarget::BehindItem(it) => { |
120 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 146 | let indent = IndentLevel::from_node(&it); |
121 | let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); | 147 | leading_ws = format!("\n\n{}", indent); |
122 | (indented, it.text_range().end()) | 148 | fn_def = fn_def.indent(indent); |
149 | trailing_ws = String::new(); | ||
150 | it.text_range().end() | ||
123 | } | 151 | } |
124 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 152 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
125 | let indent_once = IndentLevel(1); | ||
126 | let indent = IndentLevel::from_node(it.syntax()); | 153 | let indent = IndentLevel::from_node(it.syntax()); |
127 | 154 | leading_ws = format!("\n{}", indent + 1); | |
128 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 155 | fn_def = fn_def.indent(indent + 1); |
129 | let fn_def = indent_once.increase_indent(fn_def); | 156 | trailing_ws = format!("\n{}", indent); |
130 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 157 | it.syntax().text_range().start() + TextSize::of('{') |
131 | let fn_def = indent.increase_indent(fn_def); | ||
132 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | ||
133 | } | 158 | } |
134 | }; | 159 | }; |
135 | 160 | ||
136 | let cursor_offset_from_fn_start = fn_def | 161 | let placeholder_expr = |
137 | .syntax() | 162 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
138 | .descendants() | 163 | FunctionTemplate { |
139 | .find_map(ast::MacroCall::cast)? | 164 | insert_offset, |
140 | .syntax() | 165 | placeholder_expr, |
141 | .text_range() | 166 | leading_ws, |
142 | .start(); | 167 | fn_def, |
143 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; | 168 | trailing_ws, |
144 | Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }) | 169 | file: self.file, |
170 | } | ||
145 | } | 171 | } |
146 | } | 172 | } |
147 | 173 | ||
@@ -150,32 +176,41 @@ enum GeneratedFunctionTarget { | |||
150 | InEmptyItemList(ast::ItemList), | 176 | InEmptyItemList(ast::ItemList), |
151 | } | 177 | } |
152 | 178 | ||
179 | impl GeneratedFunctionTarget { | ||
180 | fn syntax(&self) -> &SyntaxNode { | ||
181 | match self { | ||
182 | GeneratedFunctionTarget::BehindItem(it) => it, | ||
183 | GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
153 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 188 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
154 | let name = call.segment()?.syntax().to_string(); | 189 | let name = call.segment()?.syntax().to_string(); |
155 | Some(ast::make::name(&name)) | 190 | Some(make::name(&name)) |
156 | } | 191 | } |
157 | 192 | ||
158 | /// Computes the type variables and arguments required for the generated function | 193 | /// Computes the type variables and arguments required for the generated function |
159 | fn fn_args( | 194 | fn fn_args( |
160 | ctx: &AssistCtx, | 195 | ctx: &AssistContext, |
196 | target_module: hir::Module, | ||
161 | call: &ast::CallExpr, | 197 | call: &ast::CallExpr, |
162 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 198 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
163 | let mut arg_names = Vec::new(); | 199 | let mut arg_names = Vec::new(); |
164 | let mut arg_types = Vec::new(); | 200 | let mut arg_types = Vec::new(); |
165 | for arg in call.arg_list()?.args() { | 201 | for arg in call.arg_list()?.args() { |
166 | let arg_name = match fn_arg_name(&arg) { | 202 | arg_names.push(match fn_arg_name(&arg) { |
167 | Some(name) => name, | 203 | Some(name) => name, |
168 | None => String::from("arg"), | 204 | None => String::from("arg"), |
169 | }; | 205 | }); |
170 | arg_names.push(arg_name); | 206 | arg_types.push(match fn_arg_type(ctx, target_module, &arg) { |
171 | arg_types.push(match fn_arg_type(ctx, &arg) { | ||
172 | Some(ty) => ty, | 207 | Some(ty) => ty, |
173 | None => String::from("()"), | 208 | None => String::from("()"), |
174 | }); | 209 | }); |
175 | } | 210 | } |
176 | deduplicate_arg_names(&mut arg_names); | 211 | deduplicate_arg_names(&mut arg_names); |
177 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); | 212 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); |
178 | Some((None, ast::make::param_list(params))) | 213 | Some((None, make::param_list(params))) |
179 | } | 214 | } |
180 | 215 | ||
181 | /// Makes duplicate argument names unique by appending incrementing numbers. | 216 | /// Makes duplicate argument names unique by appending incrementing numbers. |
@@ -224,12 +259,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | |||
224 | } | 259 | } |
225 | } | 260 | } |
226 | 261 | ||
227 | fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> { | 262 | fn fn_arg_type( |
263 | ctx: &AssistContext, | ||
264 | target_module: hir::Module, | ||
265 | fn_arg: &ast::Expr, | ||
266 | ) -> Option<String> { | ||
228 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 267 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
229 | if ty.is_unknown() { | 268 | if ty.is_unknown() { |
230 | return None; | 269 | return None; |
231 | } | 270 | } |
232 | Some(ty.display(ctx.sema.db).to_string()) | 271 | |
272 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | ||
273 | Some(rendered) | ||
274 | } else { | ||
275 | None | ||
276 | } | ||
233 | } | 277 | } |
234 | 278 | ||
235 | /// Returns the position inside the current mod or file | 279 | /// Returns the position inside the current mod or file |
@@ -258,11 +302,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu | |||
258 | 302 | ||
259 | fn next_space_for_fn_in_module( | 303 | fn next_space_for_fn_in_module( |
260 | db: &dyn hir::db::AstDatabase, | 304 | db: &dyn hir::db::AstDatabase, |
261 | module: hir::InFile<hir::ModuleSource>, | 305 | module_source: &hir::InFile<hir::ModuleSource>, |
262 | ) -> Option<(AssistFile, GeneratedFunctionTarget)> { | 306 | ) -> Option<(FileId, GeneratedFunctionTarget)> { |
263 | let file = module.file_id.original_file(db); | 307 | let file = module_source.file_id.original_file(db); |
264 | let assist_file = AssistFile::TargetFile(file); | 308 | let assist_item = match &module_source.value { |
265 | let assist_item = match module.value { | ||
266 | hir::ModuleSource::SourceFile(it) => { | 309 | hir::ModuleSource::SourceFile(it) => { |
267 | if let Some(last_item) = it.items().last() { | 310 | if let Some(last_item) = it.items().last() { |
268 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | 311 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
@@ -278,12 +321,12 @@ fn next_space_for_fn_in_module( | |||
278 | } | 321 | } |
279 | } | 322 | } |
280 | }; | 323 | }; |
281 | Some((assist_file, assist_item)) | 324 | Some((file, assist_item)) |
282 | } | 325 | } |
283 | 326 | ||
284 | #[cfg(test)] | 327 | #[cfg(test)] |
285 | mod tests { | 328 | mod tests { |
286 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 329 | use crate::tests::{check_assist, check_assist_not_applicable}; |
287 | 330 | ||
288 | use super::*; | 331 | use super::*; |
289 | 332 | ||
@@ -302,7 +345,7 @@ fn foo() { | |||
302 | } | 345 | } |
303 | 346 | ||
304 | fn bar() { | 347 | fn bar() { |
305 | <|>todo!() | 348 | ${0:todo!()} |
306 | } | 349 | } |
307 | ", | 350 | ", |
308 | ) | 351 | ) |
@@ -329,7 +372,7 @@ impl Foo { | |||
329 | } | 372 | } |
330 | 373 | ||
331 | fn bar() { | 374 | fn bar() { |
332 | <|>todo!() | 375 | ${0:todo!()} |
333 | } | 376 | } |
334 | ", | 377 | ", |
335 | ) | 378 | ) |
@@ -353,7 +396,7 @@ fn foo1() { | |||
353 | } | 396 | } |
354 | 397 | ||
355 | fn bar() { | 398 | fn bar() { |
356 | <|>todo!() | 399 | ${0:todo!()} |
357 | } | 400 | } |
358 | 401 | ||
359 | fn foo2() {} | 402 | fn foo2() {} |
@@ -379,7 +422,7 @@ mod baz { | |||
379 | } | 422 | } |
380 | 423 | ||
381 | fn bar() { | 424 | fn bar() { |
382 | <|>todo!() | 425 | ${0:todo!()} |
383 | } | 426 | } |
384 | } | 427 | } |
385 | ", | 428 | ", |
@@ -405,7 +448,7 @@ fn foo() { | |||
405 | } | 448 | } |
406 | 449 | ||
407 | fn bar(baz: Baz) { | 450 | fn bar(baz: Baz) { |
408 | <|>todo!() | 451 | ${0:todo!()} |
409 | } | 452 | } |
410 | ", | 453 | ", |
411 | ); | 454 | ); |
@@ -438,7 +481,7 @@ impl Baz { | |||
438 | } | 481 | } |
439 | 482 | ||
440 | fn bar(baz: Baz) { | 483 | fn bar(baz: Baz) { |
441 | <|>todo!() | 484 | ${0:todo!()} |
442 | } | 485 | } |
443 | ", | 486 | ", |
444 | ) | 487 | ) |
@@ -459,7 +502,7 @@ fn foo() { | |||
459 | } | 502 | } |
460 | 503 | ||
461 | fn bar(arg: &str) { | 504 | fn bar(arg: &str) { |
462 | <|>todo!() | 505 | ${0:todo!()} |
463 | } | 506 | } |
464 | "#, | 507 | "#, |
465 | ) | 508 | ) |
@@ -480,7 +523,7 @@ fn foo() { | |||
480 | } | 523 | } |
481 | 524 | ||
482 | fn bar(arg: char) { | 525 | fn bar(arg: char) { |
483 | <|>todo!() | 526 | ${0:todo!()} |
484 | } | 527 | } |
485 | "#, | 528 | "#, |
486 | ) | 529 | ) |
@@ -501,7 +544,7 @@ fn foo() { | |||
501 | } | 544 | } |
502 | 545 | ||
503 | fn bar(arg: i32) { | 546 | fn bar(arg: i32) { |
504 | <|>todo!() | 547 | ${0:todo!()} |
505 | } | 548 | } |
506 | ", | 549 | ", |
507 | ) | 550 | ) |
@@ -522,7 +565,7 @@ fn foo() { | |||
522 | } | 565 | } |
523 | 566 | ||
524 | fn bar(arg: u8) { | 567 | fn bar(arg: u8) { |
525 | <|>todo!() | 568 | ${0:todo!()} |
526 | } | 569 | } |
527 | ", | 570 | ", |
528 | ) | 571 | ) |
@@ -547,7 +590,7 @@ fn foo() { | |||
547 | } | 590 | } |
548 | 591 | ||
549 | fn bar(x: u8) { | 592 | fn bar(x: u8) { |
550 | <|>todo!() | 593 | ${0:todo!()} |
551 | } | 594 | } |
552 | ", | 595 | ", |
553 | ) | 596 | ) |
@@ -570,7 +613,7 @@ fn foo() { | |||
570 | } | 613 | } |
571 | 614 | ||
572 | fn bar(worble: ()) { | 615 | fn bar(worble: ()) { |
573 | <|>todo!() | 616 | ${0:todo!()} |
574 | } | 617 | } |
575 | ", | 618 | ", |
576 | ) | 619 | ) |
@@ -599,15 +642,40 @@ fn baz() { | |||
599 | } | 642 | } |
600 | 643 | ||
601 | fn bar(foo: impl Foo) { | 644 | fn bar(foo: impl Foo) { |
602 | <|>todo!() | 645 | ${0:todo!()} |
646 | } | ||
647 | ", | ||
648 | ) | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn borrowed_arg() { | ||
653 | check_assist( | ||
654 | add_function, | ||
655 | r" | ||
656 | struct Baz; | ||
657 | fn baz() -> Baz { todo!() } | ||
658 | |||
659 | fn foo() { | ||
660 | bar<|>(&baz()) | ||
661 | } | ||
662 | ", | ||
663 | r" | ||
664 | struct Baz; | ||
665 | fn baz() -> Baz { todo!() } | ||
666 | |||
667 | fn foo() { | ||
668 | bar(&baz()) | ||
669 | } | ||
670 | |||
671 | fn bar(baz: &Baz) { | ||
672 | ${0:todo!()} | ||
603 | } | 673 | } |
604 | ", | 674 | ", |
605 | ) | 675 | ) |
606 | } | 676 | } |
607 | 677 | ||
608 | #[test] | 678 | #[test] |
609 | #[ignore] | ||
610 | // FIXME print paths properly to make this test pass | ||
611 | fn add_function_with_qualified_path_arg() { | 679 | fn add_function_with_qualified_path_arg() { |
612 | check_assist( | 680 | check_assist( |
613 | add_function, | 681 | add_function, |
@@ -616,10 +684,8 @@ mod Baz { | |||
616 | pub struct Bof; | 684 | pub struct Bof; |
617 | pub fn baz() -> Bof { Bof } | 685 | pub fn baz() -> Bof { Bof } |
618 | } | 686 | } |
619 | mod Foo { | 687 | fn foo() { |
620 | fn foo() { | 688 | <|>bar(Baz::baz()) |
621 | <|>bar(super::Baz::baz()) | ||
622 | } | ||
623 | } | 689 | } |
624 | ", | 690 | ", |
625 | r" | 691 | r" |
@@ -627,14 +693,12 @@ mod Baz { | |||
627 | pub struct Bof; | 693 | pub struct Bof; |
628 | pub fn baz() -> Bof { Bof } | 694 | pub fn baz() -> Bof { Bof } |
629 | } | 695 | } |
630 | mod Foo { | 696 | fn foo() { |
631 | fn foo() { | 697 | bar(Baz::baz()) |
632 | bar(super::Baz::baz()) | 698 | } |
633 | } | ||
634 | 699 | ||
635 | fn bar(baz: super::Baz::Bof) { | 700 | fn bar(baz: Baz::Bof) { |
636 | <|>todo!() | 701 | ${0:todo!()} |
637 | } | ||
638 | } | 702 | } |
639 | ", | 703 | ", |
640 | ) | 704 | ) |
@@ -657,7 +721,7 @@ fn foo<T>(t: T) { | |||
657 | } | 721 | } |
658 | 722 | ||
659 | fn bar<T>(t: T) { | 723 | fn bar<T>(t: T) { |
660 | <|>todo!() | 724 | ${0:todo!()} |
661 | } | 725 | } |
662 | ", | 726 | ", |
663 | ) | 727 | ) |
@@ -688,7 +752,7 @@ fn foo() { | |||
688 | } | 752 | } |
689 | 753 | ||
690 | fn bar(arg: fn() -> Baz) { | 754 | fn bar(arg: fn() -> Baz) { |
691 | <|>todo!() | 755 | ${0:todo!()} |
692 | } | 756 | } |
693 | ", | 757 | ", |
694 | ) | 758 | ) |
@@ -713,7 +777,7 @@ fn foo() { | |||
713 | } | 777 | } |
714 | 778 | ||
715 | fn bar(closure: impl Fn(i64) -> i64) { | 779 | fn bar(closure: impl Fn(i64) -> i64) { |
716 | <|>todo!() | 780 | ${0:todo!()} |
717 | } | 781 | } |
718 | ", | 782 | ", |
719 | ) | 783 | ) |
@@ -734,7 +798,7 @@ fn foo() { | |||
734 | } | 798 | } |
735 | 799 | ||
736 | fn bar(baz: ()) { | 800 | fn bar(baz: ()) { |
737 | <|>todo!() | 801 | ${0:todo!()} |
738 | } | 802 | } |
739 | ", | 803 | ", |
740 | ) | 804 | ) |
@@ -759,7 +823,7 @@ fn foo() { | |||
759 | } | 823 | } |
760 | 824 | ||
761 | fn bar(baz_1: Baz, baz_2: Baz) { | 825 | fn bar(baz_1: Baz, baz_2: Baz) { |
762 | <|>todo!() | 826 | ${0:todo!()} |
763 | } | 827 | } |
764 | ", | 828 | ", |
765 | ) | 829 | ) |
@@ -784,7 +848,7 @@ fn foo() { | |||
784 | } | 848 | } |
785 | 849 | ||
786 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | 850 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { |
787 | <|>todo!() | 851 | ${0:todo!()} |
788 | } | 852 | } |
789 | "#, | 853 | "#, |
790 | ) | 854 | ) |
@@ -804,7 +868,7 @@ fn foo() { | |||
804 | r" | 868 | r" |
805 | mod bar { | 869 | mod bar { |
806 | pub(crate) fn my_fn() { | 870 | pub(crate) fn my_fn() { |
807 | <|>todo!() | 871 | ${0:todo!()} |
808 | } | 872 | } |
809 | } | 873 | } |
810 | 874 | ||
@@ -816,6 +880,40 @@ fn foo() { | |||
816 | } | 880 | } |
817 | 881 | ||
818 | #[test] | 882 | #[test] |
883 | #[ignore] | ||
884 | // Ignored until local imports are supported. | ||
885 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 | ||
886 | fn qualified_path_uses_correct_scope() { | ||
887 | check_assist( | ||
888 | add_function, | ||
889 | " | ||
890 | mod foo { | ||
891 | pub struct Foo; | ||
892 | } | ||
893 | fn bar() { | ||
894 | use foo::Foo; | ||
895 | let foo = Foo; | ||
896 | baz<|>(foo) | ||
897 | } | ||
898 | ", | ||
899 | " | ||
900 | mod foo { | ||
901 | pub struct Foo; | ||
902 | } | ||
903 | fn bar() { | ||
904 | use foo::Foo; | ||
905 |