diff options
103 files changed, 2595 insertions, 1931 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8ab47106d..974d18584 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml | |||
@@ -8,6 +8,16 @@ on: | |||
8 | - trying | 8 | - trying |
9 | 9 | ||
10 | jobs: | 10 | jobs: |
11 | rust-audit: | ||
12 | name: Audit Rust vulnerabilities | ||
13 | runs-on: ubuntu-latest | ||
14 | steps: | ||
15 | - name: Checkout repository | ||
16 | uses: actions/checkout@v1 | ||
17 | |||
18 | - run: cargo install cargo-audit | ||
19 | - run: cargo audit | ||
20 | |||
11 | rust: | 21 | rust: |
12 | name: Rust | 22 | name: Rust |
13 | runs-on: ${{ matrix.os }} | 23 | runs-on: ${{ matrix.os }} |
@@ -79,7 +89,7 @@ jobs: | |||
79 | if: matrix.os == 'windows-latest' | 89 | if: matrix.os == 'windows-latest' |
80 | run: Remove-Item ./target/debug/xtask.exe | 90 | run: Remove-Item ./target/debug/xtask.exe |
81 | 91 | ||
82 | type-script: | 92 | typescript: |
83 | name: TypeScript | 93 | name: TypeScript |
84 | runs-on: ubuntu-latest | 94 | runs-on: ubuntu-latest |
85 | env: | 95 | env: |
@@ -96,7 +106,12 @@ jobs: | |||
96 | 106 | ||
97 | - run: npm ci | 107 | - run: npm ci |
98 | working-directory: ./editors/code | 108 | working-directory: ./editors/code |
109 | |||
110 | - run: npm audit | ||
111 | working-directory: ./editors/code | ||
112 | |||
99 | - run: npm run fmt | 113 | - run: npm run fmt |
100 | working-directory: ./editors/code | 114 | working-directory: ./editors/code |
115 | |||
101 | - run: npm run package --scripts-prepend-node-path | 116 | - run: npm run package --scripts-prepend-node-path |
102 | working-directory: ./editors/code | 117 | working-directory: ./editors/code |
diff --git a/Cargo.lock b/Cargo.lock index 0b4d5ebe6..49fddef4b 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -129,7 +129,7 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |||
129 | [[package]] | 129 | [[package]] |
130 | name = "chalk-derive" | 130 | name = "chalk-derive" |
131 | version = "0.1.0" | 131 | version = "0.1.0" |
132 | source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" | 132 | source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702" |
133 | dependencies = [ | 133 | dependencies = [ |
134 | "proc-macro2", | 134 | "proc-macro2", |
135 | "quote", | 135 | "quote", |
@@ -139,7 +139,7 @@ dependencies = [ | |||
139 | [[package]] | 139 | [[package]] |
140 | name = "chalk-engine" | 140 | name = "chalk-engine" |
141 | version = "0.9.0" | 141 | version = "0.9.0" |
142 | source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" | 142 | source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702" |
143 | dependencies = [ | 143 | dependencies = [ |
144 | "chalk-macros", | 144 | "chalk-macros", |
145 | "rustc-hash", | 145 | "rustc-hash", |
@@ -148,7 +148,7 @@ dependencies = [ | |||
148 | [[package]] | 148 | [[package]] |
149 | name = "chalk-ir" | 149 | name = "chalk-ir" |
150 | version = "0.1.0" | 150 | version = "0.1.0" |
151 | source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" | 151 | source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702" |
152 | dependencies = [ | 152 | dependencies = [ |
153 | "chalk-derive", | 153 | "chalk-derive", |
154 | "chalk-engine", | 154 | "chalk-engine", |
@@ -159,7 +159,7 @@ dependencies = [ | |||
159 | [[package]] | 159 | [[package]] |
160 | name = "chalk-macros" | 160 | name = "chalk-macros" |
161 | version = "0.1.1" | 161 | version = "0.1.1" |
162 | source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" | 162 | source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702" |
163 | dependencies = [ | 163 | dependencies = [ |
164 | "lazy_static", | 164 | "lazy_static", |
165 | ] | 165 | ] |
@@ -167,7 +167,7 @@ dependencies = [ | |||
167 | [[package]] | 167 | [[package]] |
168 | name = "chalk-rust-ir" | 168 | name = "chalk-rust-ir" |
169 | version = "0.1.0" | 169 | version = "0.1.0" |
170 | source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" | 170 | source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702" |
171 | dependencies = [ | 171 | dependencies = [ |
172 | "chalk-derive", | 172 | "chalk-derive", |
173 | "chalk-engine", | 173 | "chalk-engine", |
@@ -178,7 +178,7 @@ dependencies = [ | |||
178 | [[package]] | 178 | [[package]] |
179 | name = "chalk-solve" | 179 | name = "chalk-solve" |
180 | version = "0.1.0" | 180 | version = "0.1.0" |
181 | source = "git+https://github.com/rust-lang/chalk.git?rev=af48f302a1f571b3ca418f7c5aa639a144a34f75#af48f302a1f571b3ca418f7c5aa639a144a34f75" | 181 | source = "git+https://github.com/rust-lang/chalk.git?rev=2939913fb7bb94ac2a6721087dc086be11410702#2939913fb7bb94ac2a6721087dc086be11410702" |
182 | dependencies = [ | 182 | dependencies = [ |
183 | "chalk-derive", | 183 | "chalk-derive", |
184 | "chalk-engine", | 184 | "chalk-engine", |
@@ -461,9 +461,9 @@ dependencies = [ | |||
461 | 461 | ||
462 | [[package]] | 462 | [[package]] |
463 | name = "hermit-abi" | 463 | name = "hermit-abi" |
464 | version = "0.1.7" | 464 | version = "0.1.8" |
465 | source = "registry+https://github.com/rust-lang/crates.io-index" | 465 | source = "registry+https://github.com/rust-lang/crates.io-index" |
466 | checksum = "e2c55f143919fbc0bc77e427fe2d74cf23786d7c1875666f2fde3ac3c659bb67" | 466 | checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" |
467 | dependencies = [ | 467 | dependencies = [ |
468 | "libc", | 468 | "libc", |
469 | ] | 469 | ] |
@@ -662,9 +662,9 @@ dependencies = [ | |||
662 | 662 | ||
663 | [[package]] | 663 | [[package]] |
664 | name = "lsp-types" | 664 | name = "lsp-types" |
665 | version = "0.70.2" | 665 | version = "0.71.0" |
666 | source = "registry+https://github.com/rust-lang/crates.io-index" | 666 | source = "registry+https://github.com/rust-lang/crates.io-index" |
667 | checksum = "6743fb3902ab3dfa6ce030daeac6ff492e20bb0fee840739d16f6bfb0efaf91c" | 667 | checksum = "efa6b75633b0c3412ee36fc416e6d9c1e4ff576b536217f4ac3f34ac83d9e564" |
668 | dependencies = [ | 668 | dependencies = [ |
669 | "base64", | 669 | "base64", |
670 | "bitflags", | 670 | "bitflags", |
@@ -821,9 +821,9 @@ dependencies = [ | |||
821 | 821 | ||
822 | [[package]] | 822 | [[package]] |
823 | name = "paste" | 823 | name = "paste" |
824 | version = "0.1.6" | 824 | version = "0.1.7" |
825 | source = "registry+https://github.com/rust-lang/crates.io-index" | 825 | source = "registry+https://github.com/rust-lang/crates.io-index" |
826 | checksum = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" | 826 | checksum = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" |
827 | dependencies = [ | 827 | dependencies = [ |
828 | "paste-impl", | 828 | "paste-impl", |
829 | "proc-macro-hack", | 829 | "proc-macro-hack", |
@@ -831,9 +831,9 @@ dependencies = [ | |||
831 | 831 | ||
832 | [[package]] | 832 | [[package]] |
833 | name = "paste-impl" | 833 | name = "paste-impl" |
834 | version = "0.1.6" | 834 | version = "0.1.7" |
835 | source = "registry+https://github.com/rust-lang/crates.io-index" | 835 | source = "registry+https://github.com/rust-lang/crates.io-index" |
836 | checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" | 836 | checksum = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" |
837 | dependencies = [ | 837 | dependencies = [ |
838 | "proc-macro-hack", | 838 | "proc-macro-hack", |
839 | "proc-macro2", | 839 | "proc-macro2", |
@@ -882,9 +882,9 @@ dependencies = [ | |||
882 | 882 | ||
883 | [[package]] | 883 | [[package]] |
884 | name = "proc-macro2" | 884 | name = "proc-macro2" |
885 | version = "1.0.8" | 885 | version = "1.0.9" |
886 | source = "registry+https://github.com/rust-lang/crates.io-index" | 886 | source = "registry+https://github.com/rust-lang/crates.io-index" |
887 | checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" | 887 | checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" |
888 | dependencies = [ | 888 | dependencies = [ |
889 | "unicode-xid", | 889 | "unicode-xid", |
890 | ] | 890 | ] |
@@ -906,7 +906,6 @@ version = "0.1.0" | |||
906 | name = "ra_assists" | 906 | name = "ra_assists" |
907 | version = "0.1.0" | 907 | version = "0.1.0" |
908 | dependencies = [ | 908 | dependencies = [ |
909 | "either", | ||
910 | "format-buf", | 909 | "format-buf", |
911 | "join_to_string", | 910 | "join_to_string", |
912 | "ra_db", | 911 | "ra_db", |
@@ -927,10 +926,8 @@ dependencies = [ | |||
927 | "cargo_metadata", | 926 | "cargo_metadata", |
928 | "crossbeam-channel", | 927 | "crossbeam-channel", |
929 | "insta", | 928 | "insta", |
930 | "jod-thread", | ||
931 | "log", | 929 | "log", |
932 | "lsp-types", | 930 | "lsp-types", |
933 | "parking_lot", | ||
934 | "serde_json", | 931 | "serde_json", |
935 | ] | 932 | ] |
936 | 933 | ||
@@ -988,7 +985,6 @@ dependencies = [ | |||
988 | "drop_bomb", | 985 | "drop_bomb", |
989 | "either", | 986 | "either", |
990 | "insta", | 987 | "insta", |
991 | "itertools", | ||
992 | "log", | 988 | "log", |
993 | "once_cell", | 989 | "once_cell", |
994 | "ra_arena", | 990 | "ra_arena", |
@@ -1046,7 +1042,6 @@ version = "0.1.0" | |||
1046 | dependencies = [ | 1042 | dependencies = [ |
1047 | "either", | 1043 | "either", |
1048 | "format-buf", | 1044 | "format-buf", |
1049 | "fst", | ||
1050 | "indexmap", | 1045 | "indexmap", |
1051 | "insta", | 1046 | "insta", |
1052 | "itertools", | 1047 | "itertools", |
@@ -1063,29 +1058,17 @@ dependencies = [ | |||
1063 | "ra_syntax", | 1058 | "ra_syntax", |
1064 | "ra_text_edit", | 1059 | "ra_text_edit", |
1065 | "rand", | 1060 | "rand", |
1066 | "rayon", | ||
1067 | "rustc-hash", | 1061 | "rustc-hash", |
1068 | "superslice", | ||
1069 | "test_utils", | 1062 | "test_utils", |
1070 | "unicase", | ||
1071 | ] | 1063 | ] |
1072 | 1064 | ||
1073 | [[package]] | 1065 | [[package]] |
1074 | name = "ra_ide_db" | 1066 | name = "ra_ide_db" |
1075 | version = "0.1.0" | 1067 | version = "0.1.0" |
1076 | dependencies = [ | 1068 | dependencies = [ |
1077 | "either", | ||
1078 | "format-buf", | ||
1079 | "fst", | 1069 | "fst", |
1080 | "indexmap", | ||
1081 | "insta", | ||
1082 | "itertools", | ||
1083 | "join_to_string", | ||
1084 | "log", | 1070 | "log", |
1085 | "once_cell", | ||
1086 | "ra_cfg", | ||
1087 | "ra_db", | 1071 | "ra_db", |
1088 | "ra_fmt", | ||
1089 | "ra_hir", | 1072 | "ra_hir", |
1090 | "ra_prof", | 1073 | "ra_prof", |
1091 | "ra_syntax", | 1074 | "ra_syntax", |
@@ -1093,8 +1076,6 @@ dependencies = [ | |||
1093 | "rayon", | 1076 | "rayon", |
1094 | "rustc-hash", | 1077 | "rustc-hash", |
1095 | "superslice", | 1078 | "superslice", |
1096 | "test_utils", | ||
1097 | "unicase", | ||
1098 | ] | 1079 | ] |
1099 | 1080 | ||
1100 | [[package]] | 1081 | [[package]] |
@@ -1122,7 +1103,6 @@ name = "ra_prof" | |||
1122 | version = "0.1.0" | 1103 | version = "0.1.0" |
1123 | dependencies = [ | 1104 | dependencies = [ |
1124 | "backtrace", | 1105 | "backtrace", |
1125 | "itertools", | ||
1126 | "jemalloc-ctl", | 1106 | "jemalloc-ctl", |
1127 | "jemallocator", | 1107 | "jemallocator", |
1128 | "once_cell", | 1108 | "once_cell", |
@@ -1165,7 +1145,6 @@ dependencies = [ | |||
1165 | name = "ra_text_edit" | 1145 | name = "ra_text_edit" |
1166 | version = "0.1.0" | 1146 | version = "0.1.0" |
1167 | dependencies = [ | 1147 | dependencies = [ |
1168 | "test_utils", | ||
1169 | "text_unit", | 1148 | "text_unit", |
1170 | ] | 1149 | ] |
1171 | 1150 | ||
@@ -1308,9 +1287,9 @@ dependencies = [ | |||
1308 | 1287 | ||
1309 | [[package]] | 1288 | [[package]] |
1310 | name = "rowan" | 1289 | name = "rowan" |
1311 | version = "0.9.0" | 1290 | version = "0.9.1" |
1312 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1313 | checksum = "6d03d4eff7a4e8dcc362e4c06bb2b1b33af4bcd64336c7f40a31a05850336b6c" | 1292 | checksum = "1ea7cadf87a9d8432e85cb4eb86bd2e765ace60c24ef86e79084dcae5d1c5a19" |
1314 | dependencies = [ | 1293 | dependencies = [ |
1315 | "rustc-hash", | 1294 | "rustc-hash", |
1316 | "smol_str", | 1295 | "smol_str", |
@@ -1324,7 +1303,6 @@ version = "0.1.0" | |||
1324 | dependencies = [ | 1303 | dependencies = [ |
1325 | "anyhow", | 1304 | "anyhow", |
1326 | "crossbeam-channel", | 1305 | "crossbeam-channel", |
1327 | "either", | ||
1328 | "env_logger", | 1306 | "env_logger", |
1329 | "globset", | 1307 | "globset", |
1330 | "itertools", | 1308 | "itertools", |
@@ -1534,9 +1512,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" | |||
1534 | 1512 | ||
1535 | [[package]] | 1513 | [[package]] |
1536 | name = "syn" | 1514 | name = "syn" |
1537 | version = "1.0.15" | 1515 | version = "1.0.16" |
1538 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1539 | checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5" | 1517 | checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" |
1540 | dependencies = [ | 1518 | dependencies = [ |
1541 | "proc-macro2", | 1519 | "proc-macro2", |
1542 | "quote", | 1520 | "quote", |
@@ -1577,9 +1555,9 @@ dependencies = [ | |||
1577 | 1555 | ||
1578 | [[package]] | 1556 | [[package]] |
1579 | name = "text_unit" | 1557 | name = "text_unit" |
1580 | version = "0.1.9" | 1558 | version = "0.1.10" |
1581 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1582 | checksum = "e08bbcb7a3adbda0eb23431206b653bdad3d8dea311e72d36bf2215e27a42579" | 1560 | checksum = "20431e104bfecc1a40872578dbc390e10290a0e9c35fffe3ce6f73c15a9dbfc2" |
1583 | 1561 | ||
1584 | [[package]] | 1562 | [[package]] |
1585 | name = "thin-dst" | 1563 | name = "thin-dst" |
@@ -1606,15 +1584,6 @@ dependencies = [ | |||
1606 | ] | 1584 | ] |
1607 | 1585 | ||
1608 | [[package]] | 1586 | [[package]] |
1609 | name = "unicase" | ||
1610 | version = "2.6.0" | ||
1611 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1612 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" | ||
1613 | dependencies = [ | ||
1614 | "version_check", | ||
1615 | ] | ||
1616 | |||
1617 | [[package]] | ||
1618 | name = "unicode-bidi" | 1587 | name = "unicode-bidi" |
1619 | version = "0.3.4" | 1588 | version = "0.3.4" |
1620 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1657,12 +1626,6 @@ dependencies = [ | |||
1657 | ] | 1626 | ] |
1658 | 1627 | ||
1659 | [[package]] | 1628 | [[package]] |
1660 | name = "version_check" | ||
1661 | version = "0.9.1" | ||
1662 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1663 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" | ||
1664 | |||
1665 | [[package]] | ||
1666 | name = "walkdir" | 1629 | name = "walkdir" |
1667 | version = "2.3.1" | 1630 | version = "2.3.1" |
1668 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" |
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 12a933645..d314dc8e6 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml | |||
@@ -11,7 +11,6 @@ doctest = false | |||
11 | format-buf = "1.0.0" | 11 | format-buf = "1.0.0" |
12 | join_to_string = "0.1.3" | 12 | join_to_string = "0.1.3" |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | either = "1.5.3" | ||
15 | 14 | ||
16 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
17 | ra_text_edit = { path = "../ra_text_edit" } | 16 | ra_text_edit = { path = "../ra_text_edit" } |
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 5aab5fb8b..c25d2e323 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. | 1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. |
2 | use hir::{InFile, SourceAnalyzer, SourceBinder}; | 2 | use hir::Semantics; |
3 | use ra_db::{FileRange, SourceDatabase}; | 3 | use ra_db::FileRange; |
4 | use ra_fmt::{leading_indent, reindent}; | 4 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -74,29 +74,23 @@ pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>; | |||
74 | /// Note, however, that we don't actually use such two-phase logic at the | 74 | /// Note, however, that we don't actually use such two-phase logic at the |
75 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 75 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
76 | /// easier to just compute the edit eagerly :-) | 76 | /// easier to just compute the edit eagerly :-) |
77 | #[derive(Debug)] | 77 | #[derive(Clone)] |
78 | pub(crate) struct AssistCtx<'a> { | 78 | pub(crate) struct AssistCtx<'a> { |
79 | pub(crate) sema: &'a Semantics<'a, RootDatabase>, | ||
79 | pub(crate) db: &'a RootDatabase, | 80 | pub(crate) db: &'a RootDatabase, |
80 | pub(crate) frange: FileRange, | 81 | pub(crate) frange: FileRange, |
81 | source_file: SourceFile, | 82 | source_file: SourceFile, |
82 | should_compute_edit: bool, | 83 | should_compute_edit: bool, |
83 | } | 84 | } |
84 | 85 | ||
85 | impl Clone for AssistCtx<'_> { | ||
86 | fn clone(&self) -> Self { | ||
87 | AssistCtx { | ||
88 | db: self.db, | ||
89 | frange: self.frange, | ||
90 | source_file: self.source_file.clone(), | ||
91 | should_compute_edit: self.should_compute_edit, | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | impl<'a> AssistCtx<'a> { | 86 | impl<'a> AssistCtx<'a> { |
97 | pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx { | 87 | pub fn new( |
98 | let parse = db.parse(frange.file_id); | 88 | sema: &'a Semantics<'a, RootDatabase>, |
99 | AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit } | 89 | frange: FileRange, |
90 | should_compute_edit: bool, | ||
91 | ) -> AssistCtx<'a> { | ||
92 | let source_file = sema.parse(frange.file_id); | ||
93 | AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit } | ||
100 | } | 94 | } |
101 | 95 | ||
102 | pub(crate) fn add_assist( | 96 | pub(crate) fn add_assist( |
@@ -138,18 +132,6 @@ impl<'a> AssistCtx<'a> { | |||
138 | pub(crate) fn covering_element(&self) -> SyntaxElement { | 132 | pub(crate) fn covering_element(&self) -> SyntaxElement { |
139 | find_covering_element(self.source_file.syntax(), self.frange.range) | 133 | find_covering_element(self.source_file.syntax(), self.frange.range) |
140 | } | 134 | } |
141 | pub(crate) fn source_binder(&self) -> SourceBinder<'a, RootDatabase> { | ||
142 | SourceBinder::new(self.db) | ||
143 | } | ||
144 | pub(crate) fn source_analyzer( | ||
145 | &self, | ||
146 | node: &SyntaxNode, | ||
147 | offset: Option<TextUnit>, | ||
148 | ) -> SourceAnalyzer { | ||
149 | let src = InFile::new(self.frange.file_id.into(), node); | ||
150 | self.source_binder().analyze(src, offset) | ||
151 | } | ||
152 | |||
153 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { | 135 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { |
154 | find_covering_element(self.source_file.syntax(), range) | 136 | find_covering_element(self.source_file.syntax(), range) |
155 | } | 137 | } |
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 56b7588ef..7846e9798 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -1,14 +1,12 @@ | |||
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::{db::HirDatabase, InFile, PathResolution}; | 4 | use hir::{PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | ||
5 | use ra_syntax::ast::{self, AstNode}; | 6 | use ra_syntax::ast::{self, AstNode}; |
6 | 7 | ||
7 | pub trait AstTransform<'a> { | 8 | pub trait AstTransform<'a> { |
8 | fn get_substitution( | 9 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>; |
9 | &self, | ||
10 | node: InFile<&ra_syntax::SyntaxNode>, | ||
11 | ) -> Option<ra_syntax::SyntaxNode>; | ||
12 | 10 | ||
13 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; | 11 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; |
14 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> | 12 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> |
@@ -22,10 +20,7 @@ pub trait AstTransform<'a> { | |||
22 | struct NullTransformer; | 20 | struct NullTransformer; |
23 | 21 | ||
24 | impl<'a> AstTransform<'a> for NullTransformer { | 22 | impl<'a> AstTransform<'a> for NullTransformer { |
25 | fn get_substitution( | 23 | fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
26 | &self, | ||
27 | _node: InFile<&ra_syntax::SyntaxNode>, | ||
28 | ) -> Option<ra_syntax::SyntaxNode> { | ||
29 | None | 24 | None |
30 | } | 25 | } |
31 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 26 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
@@ -33,18 +28,20 @@ impl<'a> AstTransform<'a> for NullTransformer { | |||
33 | } | 28 | } |
34 | } | 29 | } |
35 | 30 | ||
36 | pub struct SubstituteTypeParams<'a, DB: HirDatabase> { | 31 | pub struct SubstituteTypeParams<'a> { |
37 | db: &'a DB, | 32 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
38 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, | 33 | substs: FxHashMap<hir::TypeParam, ast::TypeRef>, |
39 | previous: Box<dyn AstTransform<'a> + 'a>, | 34 | previous: Box<dyn AstTransform<'a> + 'a>, |
40 | } | 35 | } |
41 | 36 | ||
42 | impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | 37 | impl<'a> SubstituteTypeParams<'a> { |
43 | pub fn for_trait_impl( | 38 | pub fn for_trait_impl( |
44 | db: &'a DB, | 39 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
40 | db: &'a RootDatabase, | ||
41 | // FIXME: there's implicit invariant that `trait_` and `source_scope` match... | ||
45 | trait_: hir::Trait, | 42 | trait_: hir::Trait, |
46 | impl_block: ast::ImplBlock, | 43 | impl_block: ast::ImplBlock, |
47 | ) -> SubstituteTypeParams<'a, DB> { | 44 | ) -> SubstituteTypeParams<'a> { |
48 | let substs = get_syntactic_substs(impl_block).unwrap_or_default(); | 45 | let substs = get_syntactic_substs(impl_block).unwrap_or_default(); |
49 | let generic_def: hir::GenericDef = trait_.into(); | 46 | let generic_def: hir::GenericDef = trait_.into(); |
50 | let substs_by_param: FxHashMap<_, _> = generic_def | 47 | let substs_by_param: FxHashMap<_, _> = generic_def |
@@ -55,7 +52,7 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | |||
55 | .zip(substs.into_iter()) | 52 | .zip(substs.into_iter()) |
56 | .collect(); | 53 | .collect(); |
57 | return SubstituteTypeParams { | 54 | return SubstituteTypeParams { |
58 | db, | 55 | source_scope, |
59 | substs: substs_by_param, | 56 | substs: substs_by_param, |
60 | previous: Box::new(NullTransformer), | 57 | previous: Box::new(NullTransformer), |
61 | }; | 58 | }; |
@@ -79,15 +76,15 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | |||
79 | } | 76 | } |
80 | fn get_substitution_inner( | 77 | fn get_substitution_inner( |
81 | &self, | 78 | &self, |
82 | node: InFile<&ra_syntax::SyntaxNode>, | 79 | node: &ra_syntax::SyntaxNode, |
83 | ) -> Option<ra_syntax::SyntaxNode> { | 80 | ) -> Option<ra_syntax::SyntaxNode> { |
84 | let type_ref = ast::TypeRef::cast(node.value.clone())?; | 81 | let type_ref = ast::TypeRef::cast(node.clone())?; |
85 | let path = match &type_ref { | 82 | let path = match &type_ref { |
86 | ast::TypeRef::PathType(path_type) => path_type.path()?, | 83 | ast::TypeRef::PathType(path_type) => path_type.path()?, |
87 | _ => return None, | 84 | _ => return None, |
88 | }; | 85 | }; |
89 | let analyzer = hir::SourceAnalyzer::new(self.db, node, None); | 86 | let path = hir::Path::from_ast(path)?; |
90 | let resolution = analyzer.resolve_path(self.db, &path)?; | 87 | let resolution = self.source_scope.resolve_hir_path(&path)?; |
91 | match resolution { | 88 | match resolution { |
92 | hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), | 89 | hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), |
93 | _ => None, | 90 | _ => None, |
@@ -95,11 +92,8 @@ impl<'a, DB: HirDatabase> SubstituteTypeParams<'a, DB> { | |||
95 | } | 92 | } |
96 | } | 93 | } |
97 | 94 | ||
98 | impl<'a, DB: HirDatabase> AstTransform<'a> for SubstituteTypeParams<'a, DB> { | 95 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { |
99 | fn get_substitution( | 96 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
100 | &self, | ||
101 | node: InFile<&ra_syntax::SyntaxNode>, | ||
102 | ) -> Option<ra_syntax::SyntaxNode> { | ||
103 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | 97 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) |
104 | } | 98 | } |
105 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 99 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
@@ -107,30 +101,35 @@ impl<'a, DB: HirDatabase> AstTransform<'a> for SubstituteTypeParams<'a, DB> { | |||
107 | } | 101 | } |
108 | } | 102 | } |
109 | 103 | ||
110 | pub struct QualifyPaths<'a, DB: HirDatabase> { | 104 | pub struct QualifyPaths<'a> { |
111 | db: &'a DB, | 105 | target_scope: &'a SemanticsScope<'a, RootDatabase>, |
112 | from: Option<hir::Module>, | 106 | source_scope: &'a SemanticsScope<'a, RootDatabase>, |
107 | db: &'a RootDatabase, | ||
113 | previous: Box<dyn AstTransform<'a> + 'a>, | 108 | previous: Box<dyn AstTransform<'a> + 'a>, |
114 | } | 109 | } |
115 | 110 | ||
116 | impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | 111 | impl<'a> QualifyPaths<'a> { |
117 | pub fn new(db: &'a DB, from: Option<hir::Module>) -> Self { | 112 | pub fn new( |
118 | Self { db, from, previous: Box::new(NullTransformer) } | 113 | target_scope: &'a SemanticsScope<'a, RootDatabase>, |
114 | source_scope: &'a SemanticsScope<'a, RootDatabase>, | ||
115 | db: &'a RootDatabase, | ||
116 | ) -> Self { | ||
117 | Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) } | ||
119 | } | 118 | } |
120 | 119 | ||
121 | fn get_substitution_inner( | 120 | fn get_substitution_inner( |
122 | &self, | 121 | &self, |
123 | node: InFile<&ra_syntax::SyntaxNode>, | 122 | node: &ra_syntax::SyntaxNode, |
124 | ) -> Option<ra_syntax::SyntaxNode> { | 123 | ) -> Option<ra_syntax::SyntaxNode> { |
125 | // FIXME handle value ns? | 124 | // FIXME handle value ns? |
126 | let from = self.from?; | 125 | let from = self.target_scope.module()?; |
127 | let p = ast::Path::cast(node.value.clone())?; | 126 | let p = ast::Path::cast(node.clone())?; |
128 | if p.segment().and_then(|s| s.param_list()).is_some() { | 127 | if p.segment().and_then(|s| s.param_list()).is_some() { |
129 | // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway | 128 | // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway |
130 | return None; | 129 | return None; |
131 | } | 130 | } |
132 | let analyzer = hir::SourceAnalyzer::new(self.db, node, None); | 131 | let hir_path = hir::Path::from_ast(p.clone()); |
133 | let resolution = analyzer.resolve_path(self.db, &p)?; | 132 | let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; |
134 | match resolution { | 133 | match resolution { |
135 | PathResolution::Def(def) => { | 134 | PathResolution::Def(def) => { |
136 | let found_path = from.find_use_path(self.db, def)?; | 135 | let found_path = from.find_use_path(self.db, def)?; |
@@ -139,7 +138,7 @@ impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | |||
139 | let type_args = p | 138 | let type_args = p |
140 | .segment() | 139 | .segment() |
141 | .and_then(|s| s.type_arg_list()) | 140 | .and_then(|s| s.type_arg_list()) |
142 | .map(|arg_list| apply(self, node.with_value(arg_list))); | 141 | .map(|arg_list| apply(self, arg_list)); |
143 | if let Some(type_args) = type_args { | 142 | if let Some(type_args) = type_args { |
144 | let last_segment = path.segment().unwrap(); | 143 | let last_segment = path.segment().unwrap(); |
145 | path = path.with_segment(last_segment.with_type_args(type_args)) | 144 | path = path.with_segment(last_segment.with_type_args(type_args)) |
@@ -156,11 +155,11 @@ impl<'a, DB: HirDatabase> QualifyPaths<'a, DB> { | |||
156 | } | 155 | } |
157 | } | 156 | } |
158 | 157 | ||
159 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N { | 158 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
160 | let syntax = node.value.syntax(); | 159 | let syntax = node.syntax(); |
161 | let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { | 160 | let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { |
162 | ra_syntax::SyntaxElement::Node(n) => { | 161 | ra_syntax::SyntaxElement::Node(n) => { |
163 | let replacement = transformer.get_substitution(node.with_value(&n))?; | 162 | let replacement = transformer.get_substitution(&n)?; |
164 | Some(replacement.into()) | 163 | Some(replacement.into()) |
165 | } | 164 | } |
166 | _ => None, | 165 | _ => None, |
@@ -168,11 +167,8 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N> | |||
168 | N::cast(result).unwrap() | 167 | N::cast(result).unwrap() |
169 | } | 168 | } |
170 | 169 | ||
171 | impl<'a, DB: HirDatabase> AstTransform<'a> for QualifyPaths<'a, DB> { | 170 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { |
172 | fn get_substitution( | 171 | fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> { |
173 | &self, | ||
174 | node: InFile<&ra_syntax::SyntaxNode>, | ||
175 | ) -> Option<ra_syntax::SyntaxNode> { | ||
176 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | 172 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) |
177 | } | 173 | } |
178 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 174 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 2cb9d2f48..a63ef48b1 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -51,14 +51,13 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
51 | } | 51 | } |
52 | } | 52 | } |
53 | // Infer type | 53 | // Infer type |
54 | let db = ctx.db; | 54 | let ty = ctx.sema.type_of_expr(&expr)?; |
55 | let analyzer = ctx.source_analyzer(stmt.syntax(), None); | ||
56 | let ty = analyzer.type_of(db, &expr)?; | ||
57 | // Assist not applicable if the type is unknown | 55 | // Assist not applicable if the type is unknown |
58 | if ty.contains_unknown() { | 56 | if ty.contains_unknown() { |
59 | return None; | 57 | return None; |
60 | } | 58 | } |
61 | 59 | ||
60 | let db = ctx.db; | ||
62 | ctx.add_assist( | 61 | ctx.add_assist( |
63 | AssistId("add_explicit_type"), | 62 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", ty.display(db)), | 63 | format!("Insert explicit type '{}'", ty.display(db)), |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index ab21388c8..4005014bd 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{HasSource, InFile}; | 1 | use hir::HasSource; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, edit, make, AstNode, NameOwner}, | 3 | ast::{self, edit, make, AstNode, NameOwner}, |
4 | SmolStr, | 4 | SmolStr, |
@@ -104,9 +104,7 @@ fn add_missing_impl_members_inner( | |||
104 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; | 104 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; |
105 | let impl_item_list = impl_node.item_list()?; | 105 | let impl_item_list = impl_node.item_list()?; |
106 | 106 | ||
107 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); | 107 | let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; |
108 | |||
109 | let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?; | ||
110 | 108 | ||
111 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { | 109 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { |
112 | match item { | 110 | match item { |
@@ -117,7 +115,7 @@ fn add_missing_impl_members_inner( | |||
117 | .map(|it| it.text().clone()) | 115 | .map(|it| it.text().clone()) |
118 | }; | 116 | }; |
119 | 117 | ||
120 | let missing_items = get_missing_impl_items(ctx.db, &analyzer, &impl_node) | 118 | let missing_items = get_missing_impl_items(&ctx.sema, &impl_node) |
121 | .iter() | 119 | .iter() |
122 | .map(|i| match i { | 120 | .map(|i| match i { |
123 | hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), | 121 | hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), |
@@ -138,23 +136,17 @@ fn add_missing_impl_members_inner( | |||
138 | return None; | 136 | return None; |
139 | } | 137 | } |
140 | 138 | ||
141 | let db = ctx.db; | 139 | let sema = ctx.sema; |
142 | let file_id = ctx.frange.file_id; | ||
143 | let trait_file_id = trait_.source(db).file_id; | ||
144 | 140 | ||
145 | ctx.add_assist(AssistId(assist_id), label, |edit| { | 141 | ctx.add_assist(AssistId(assist_id), label, |edit| { |
146 | let n_existing_items = impl_item_list.impl_items().count(); | 142 | let n_existing_items = impl_item_list.impl_items().count(); |
147 | let module = hir::SourceAnalyzer::new( | 143 | let source_scope = sema.scope_for_def(trait_); |
148 | db, | 144 | let target_scope = sema.scope(impl_item_list.syntax()); |
149 | hir::InFile::new(file_id.into(), impl_node.syntax()), | 145 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope, sema.db) |
150 | None, | 146 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, sema.db, trait_, impl_node)); |
151 | ) | ||
152 | .module(); | ||
153 | let ast_transform = QualifyPaths::new(db, module) | ||
154 | .or(SubstituteTypeParams::for_trait_impl(db, trait_, impl_node)); | ||
155 | let items = missing_items | 147 | let items = missing_items |
156 | .into_iter() | 148 | .into_iter() |
157 | .map(|it| ast_transform::apply(&*ast_transform, InFile::new(trait_file_id, it))) | 149 | .map(|it| ast_transform::apply(&*ast_transform, it)) |
158 | .map(|it| match it { | 150 | .map(|it| match it { |
159 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), | 151 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), |
160 | _ => it, | 152 | _ => it, |
@@ -181,9 +173,10 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
181 | 173 | ||
182 | #[cfg(test)] | 174 | #[cfg(test)] |
183 | mod tests { | 175 | mod tests { |
184 | use super::*; | ||
185 | use crate::helpers::{check_assist, check_assist_not_applicable}; | 176 | use crate::helpers::{check_assist, check_assist_not_applicable}; |
186 | 177 | ||
178 | use super::*; | ||
179 | |||
187 | #[test] | 180 | #[test] |
188 | fn test_add_missing_impl_members() { | 181 | fn test_add_missing_impl_members() { |
189 | check_assist( | 182 | check_assist( |
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index dd070e8ec..166e907fb 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use format_buf::format; | 1 | use format_buf::format; |
2 | use hir::{Adt, InFile}; | 2 | use hir::Adt; |
3 | use join_to_string::join; | 3 | use join_to_string::join; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{ | 5 | ast::{ |
@@ -133,16 +133,11 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a | |||
133 | let module = strukt.syntax().ancestors().find(|node| { | 133 | let module = strukt.syntax().ancestors().find(|node| { |
134 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | 134 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) |
135 | })?; | 135 | })?; |
136 | let mut sb = ctx.source_binder(); | ||
137 | 136 | ||
138 | let struct_def = { | 137 | let struct_def = ctx.sema.to_def(strukt)?; |
139 | let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; | ||
140 | sb.to_def(src)? | ||
141 | }; | ||
142 | 138 | ||
143 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { | 139 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { |
144 | let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; | 140 | let blk = ctx.sema.to_def(&impl_blk)?; |
145 | let blk = sb.to_def(src)?; | ||
146 | 141 | ||
147 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` | 142 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` |
148 | // (we currently use the wrong type parameter) | 143 | // (we currently use the wrong type parameter) |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index c4aea2a06..edf0cf6d0 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -3,8 +3,8 @@ use crate::{ | |||
3 | insert_use_statement, AssistId, | 3 | insert_use_statement, AssistId, |
4 | }; | 4 | }; |
5 | use hir::{ | 5 | use hir::{ |
6 | db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, | 6 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, |
7 | SourceAnalyzer, Trait, Type, | 7 | Type, |
8 | }; | 8 | }; |
9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | 9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; |
10 | use ra_prof::profile; | 10 | use ra_prof::profile; |
@@ -78,14 +78,9 @@ impl AutoImportAssets { | |||
78 | 78 | ||
79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { | 79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { |
80 | let syntax_under_caret = method_call.syntax().to_owned(); | 80 | let syntax_under_caret = method_call.syntax().to_owned(); |
81 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | 81 | let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; |
82 | let module_with_name_to_import = source_analyzer.module()?; | ||
83 | Some(Self { | 82 | Some(Self { |
84 | import_candidate: ImportCandidate::for_method_call( | 83 | import_candidate: ImportCandidate::for_method_call(&ctx.sema, &method_call)?, |
85 | &method_call, | ||
86 | &source_analyzer, | ||
87 | ctx.db, | ||
88 | )?, | ||
89 | module_with_name_to_import, | 84 | module_with_name_to_import, |
90 | syntax_under_caret, | 85 | syntax_under_caret, |
91 | }) | 86 | }) |
@@ -97,14 +92,9 @@ impl AutoImportAssets { | |||
97 | return None; | 92 | return None; |
98 | } | 93 | } |
99 | 94 | ||
100 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | 95 | let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; |
101 | let module_with_name_to_import = source_analyzer.module()?; | ||
102 | Some(Self { | 96 | Some(Self { |
103 | import_candidate: ImportCandidate::for_regular_path( | 97 | import_candidate: ImportCandidate::for_regular_path(&ctx.sema, &path_under_caret)?, |
104 | &path_under_caret, | ||
105 | &source_analyzer, | ||
106 | ctx.db, | ||
107 | )?, | ||
108 | module_with_name_to_import, | 98 | module_with_name_to_import, |
109 | syntax_under_caret, | 99 | syntax_under_caret, |
110 | }) | 100 | }) |
@@ -229,25 +219,23 @@ enum ImportCandidate { | |||
229 | 219 | ||
230 | impl ImportCandidate { | 220 | impl ImportCandidate { |
231 | fn for_method_call( | 221 | fn for_method_call( |
222 | sema: &Semantics<RootDatabase>, | ||
232 | method_call: &ast::MethodCallExpr, | 223 | method_call: &ast::MethodCallExpr, |
233 | source_analyzer: &SourceAnalyzer, | ||
234 | db: &impl HirDatabase, | ||
235 | ) -> Option<Self> { | 224 | ) -> Option<Self> { |
236 | if source_analyzer.resolve_method_call(method_call).is_some() { | 225 | if sema.resolve_method_call(method_call).is_some() { |
237 | return None; | 226 | return None; |
238 | } | 227 | } |
239 | Some(Self::TraitMethod( | 228 | Some(Self::TraitMethod( |
240 | source_analyzer.type_of(db, &method_call.expr()?)?, | 229 | sema.type_of_expr(&method_call.expr()?)?, |
241 | method_call.name_ref()?.syntax().to_string(), | 230 | method_call.name_ref()?.syntax().to_string(), |
242 | )) | 231 | )) |
243 | } | 232 | } |
244 | 233 | ||
245 | fn for_regular_path( | 234 | fn for_regular_path( |
235 | sema: &Semantics<RootDatabase>, | ||
246 | path_under_caret: &ast::Path, | 236 | path_under_caret: &ast::Path, |
247 | source_analyzer: &SourceAnalyzer, | ||
248 | db: &impl HirDatabase, | ||
249 | ) -> Option<Self> { | 237 | ) -> Option<Self> { |
250 | if source_analyzer.resolve_path(db, path_under_caret).is_some() { | 238 | if sema.resolve_path(path_under_caret).is_some() { |
251 | return None; | 239 | return None; |
252 | } | 240 | } |
253 | 241 | ||
@@ -256,17 +244,15 @@ impl ImportCandidate { | |||
256 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 244 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; |
257 | let qualifier_start_path = | 245 | let qualifier_start_path = |
258 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 246 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; |
259 | if let Some(qualifier_start_resolution) = | 247 | if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { |
260 | source_analyzer.resolve_path(db, &qualifier_start_path) | ||
261 | { | ||
262 | let qualifier_resolution = if qualifier_start_path == qualifier { | 248 | let qualifier_resolution = if qualifier_start_path == qualifier { |
263 | qualifier_start_resolution | 249 | qualifier_start_resolution |
264 | } else { | 250 | } else { |
265 | source_analyzer.resolve_path(db, &qualifier)? | 251 | sema.resolve_path(&qualifier)? |
266 | }; | 252 | }; |
267 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { | 253 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { |
268 | Some(ImportCandidate::TraitAssocItem( | 254 | Some(ImportCandidate::TraitAssocItem( |
269 | assoc_item_path.ty(db), | 255 | assoc_item_path.ty(sema.db), |
270 | segment.syntax().to_string(), | 256 | segment.syntax().to_string(), |
271 | )) | 257 | )) |
272 | } else { | 258 | } else { |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index ae2437ed3..e5d8c639d 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -2,10 +2,11 @@ | |||
2 | 2 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use hir::{db::HirDatabase, Adt, HasSource}; | 5 | use hir::{db::HirDatabase, Adt, HasSource, Semantics}; |
6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; | 6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
9 | use ra_ide_db::RootDatabase; | ||
9 | 10 | ||
10 | // Assist: fill_match_arms | 11 | // Assist: fill_match_arms |
11 | // | 12 | // |
@@ -46,10 +47,9 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
46 | }; | 47 | }; |
47 | 48 | ||
48 | let expr = match_expr.expr()?; | 49 | let expr = match_expr.expr()?; |
49 | let (enum_def, module) = { | 50 | let enum_def = resolve_enum_def(&ctx.sema, &expr)?; |
50 | let analyzer = ctx.source_analyzer(expr.syntax(), None); | 51 | let module = ctx.sema.scope(expr.syntax()).module()?; |
51 | (resolve_enum_def(ctx.db, &analyzer, &expr)?, analyzer.module()?) | 52 | |
52 | }; | ||
53 | let variants = enum_def.variants(ctx.db); | 53 | let variants = enum_def.variants(ctx.db); |
54 | if variants.is_empty() { | 54 | if variants.is_empty() { |
55 | return None; | 55 | return None; |
@@ -81,18 +81,11 @@ fn is_trivial(arm: &ast::MatchArm) -> bool { | |||
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | fn resolve_enum_def( | 84 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { |
85 | db: &impl HirDatabase, | 85 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
86 | analyzer: &hir::SourceAnalyzer, | ||
87 | expr: &ast::Expr, | ||
88 | ) -> Option<hir::Enum> { | ||
89 | let expr_ty = analyzer.type_of(db, &expr)?; | ||
90 | |||
91 | let result = expr_ty.autoderef(db).find_map(|ty| match ty.as_adt() { | ||
92 | Some(Adt::Enum(e)) => Some(e), | 86 | Some(Adt::Enum(e)) => Some(e), |
93 | _ => None, | 87 | _ => None, |
94 | }); | 88 | }) |
95 | result | ||
96 | } | 89 | } |
97 | 90 | ||
98 | fn build_pat( | 91 | fn build_pat( |
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index 91b588243..53a72309b 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs | |||
@@ -44,8 +44,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> { | |||
44 | } else { | 44 | } else { |
45 | let_stmt.syntax().text_range() | 45 | let_stmt.syntax().text_range() |
46 | }; | 46 | }; |
47 | let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); | 47 | let refs = ctx.sema.find_all_refs(&bind_pat); |
48 | let refs = analyzer.find_all_refs(&bind_pat); | ||
49 | if refs.is_empty() { | 48 | if refs.is_empty() { |
50 | return None; | 49 | return None; |
51 | }; | 50 | }; |
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 7312ce687..b453c51fb 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs | |||
@@ -136,15 +136,13 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | |||
136 | mod tests { | 136 | mod tests { |
137 | use test_utils::covers; | 137 | use test_utils::covers; |
138 | 138 | ||
139 | use crate::helpers::{ | 139 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; |
140 | check_assist_range, check_assist_range_not_applicable, check_assist_range_target, | ||
141 | }; | ||
142 | 140 | ||
143 | use super::*; | 141 | use super::*; |
144 | 142 | ||
145 | #[test] | 143 | #[test] |
146 | fn test_introduce_var_simple() { | 144 | fn test_introduce_var_simple() { |
147 | check_assist_range( | 145 | check_assist( |
148 | introduce_variable, | 146 | introduce_variable, |
149 | " | 147 | " |
150 | fn foo() { | 148 | fn foo() { |
@@ -161,16 +159,13 @@ fn foo() { | |||
161 | #[test] | 159 | #[test] |
162 | fn introduce_var_in_comment_is_not_applicable() { | 160 | fn introduce_var_in_comment_is_not_applicable() { |
163 | covers!(introduce_var_in_comment_is_not_applicable); | 161 | covers!(introduce_var_in_comment_is_not_applicable); |
164 | check_assist_range_not_applicable( | 162 | check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); |
165 | introduce_variable, | ||
166 | "fn main() { 1 + /* <|>comment<|> */ 1; }", | ||
167 | ); | ||
168 | } | 163 | } |
169 | 164 | ||
170 | #[test] | 165 | #[test] |
171 | fn test_introduce_var_expr_stmt() { | 166 | fn test_introduce_var_expr_stmt() { |
172 | covers!(test_introduce_var_expr_stmt); | 167 | covers!(test_introduce_var_expr_stmt); |
173 | check_assist_range( | 168 | check_assist( |
174 | introduce_variable, | 169 | introduce_variable, |
175 | " | 170 | " |
176 | fn foo() { | 171 | fn foo() { |
@@ -181,7 +176,7 @@ fn foo() { | |||
181 | let <|>var_name = 1 + 1; | 176 | let <|>var_name = 1 + 1; |
182 | }", | 177 | }", |
183 | ); | 178 | ); |
184 | check_assist_range( | 179 | check_assist( |
185 | introduce_variable, | 180 | introduce_variable, |
186 | " | 181 | " |
187 | fn foo() { | 182 | fn foo() { |
@@ -198,7 +193,7 @@ fn foo() { | |||
198 | 193 | ||
199 | #[test] | 194 | #[test] |
200 | fn test_introduce_var_part_of_expr_stmt() { | 195 | fn test_introduce_var_part_of_expr_stmt() { |
201 | check_assist_range( | 196 | check_assist( |
202 | introduce_variable, | 197 | introduce_variable, |
203 | " | 198 | " |
204 | fn foo() { | 199 | fn foo() { |
@@ -215,7 +210,7 @@ fn foo() { | |||
215 | #[test] | 210 | #[test] |
216 | fn test_introduce_var_last_expr() { | 211 | fn test_introduce_var_last_expr() { |
217 | covers!(test_introduce_var_last_expr); | 212 | covers!(test_introduce_var_last_expr); |
218 | check_assist_range( | 213 | check_assist( |
219 | introduce_variable, | 214 | introduce_variable, |
220 | " | 215 | " |
221 | fn foo() { | 216 | fn foo() { |
@@ -227,7 +222,7 @@ fn foo() { | |||
227 | bar(var_name) | 222 | bar(var_name) |
228 | }", | 223 | }", |
229 | ); | 224 | ); |
230 | check_assist_range( | 225 | check_assist( |
231 | introduce_variable, | 226 | introduce_variable, |
232 | " | 227 | " |
233 | fn foo() { | 228 | fn foo() { |
@@ -243,7 +238,7 @@ fn foo() { | |||
243 | 238 | ||
244 | #[test] | 239 | #[test] |
245 | fn test_introduce_var_in_match_arm_no_block() { | 240 | fn test_introduce_var_in_match_arm_no_block() { |
246 | check_assist_range( | 241 | check_assist( |
247 | introduce_variable, | 242 | introduce_variable, |
248 | " | 243 | " |
249 | fn main() { | 244 | fn main() { |
@@ -268,7 +263,7 @@ fn main() { | |||
268 | 263 | ||
269 | #[test] | 264 | #[test] |
270 | fn test_introduce_var_in_match_arm_with_block() { | 265 | fn test_introduce_var_in_match_arm_with_block() { |
271 | check_assist_range( | 266 | check_assist( |
272 | introduce_variable, | 267 | introduce_variable, |
273 | " | 268 | " |
274 | fn main() { | 269 | fn main() { |
@@ -300,7 +295,7 @@ fn main() { | |||
300 | 295 | ||
301 | #[test] | 296 | #[test] |
302 | fn test_introduce_var_in_closure_no_block() { | 297 | fn test_introduce_var_in_closure_no_block() { |
303 | check_assist_range( | 298 | check_assist( |
304 | introduce_variable, | 299 | introduce_variable, |
305 | " | 300 | " |
306 | fn main() { | 301 | fn main() { |
@@ -317,7 +312,7 @@ fn main() { | |||
317 | 312 | ||
318 | #[test] | 313 | #[test] |
319 | fn test_introduce_var_in_closure_with_block() { | 314 | fn test_introduce_var_in_closure_with_block() { |
320 | check_assist_range( | 315 | check_assist( |
321 | introduce_variable, | 316 | introduce_variable, |
322 | " | 317 | " |
323 | fn main() { | 318 | fn main() { |
@@ -334,7 +329,7 @@ fn main() { | |||
334 | 329 | ||
335 | #[test] | 330 | #[test] |
336 | fn test_introduce_var_path_simple() { | 331 | fn test_introduce_var_path_simple() { |
337 | check_assist_range( | 332 | check_assist( |
338 | introduce_variable, | 333 | introduce_variable, |
339 | " | 334 | " |
340 | fn main() { | 335 | fn main() { |
@@ -352,7 +347,7 @@ fn main() { | |||
352 | 347 | ||
353 | #[test] | 348 | #[test] |
354 | fn test_introduce_var_path_method() { | 349 | fn test_introduce_var_path_method() { |
355 | check_assist_range( | 350 | check_assist( |
356 | introduce_variable, | 351 | introduce_variable, |
357 | " | 352 | " |
358 | fn main() { | 353 | fn main() { |
@@ -370,7 +365,7 @@ fn main() { | |||
370 | 365 | ||
371 | #[test] | 366 | #[test] |
372 | fn test_introduce_var_return() { | 367 | fn test_introduce_var_return() { |
373 | check_assist_range( | 368 | check_assist( |
374 | introduce_variable, | 369 | introduce_variable, |
375 | " | 370 | " |
376 | fn foo() -> u32 { | 371 | fn foo() -> u32 { |
@@ -388,7 +383,7 @@ fn foo() -> u32 { | |||
388 | 383 | ||
389 | #[test] | 384 | #[test] |
390 | fn test_introduce_var_does_not_add_extra_whitespace() { | 385 | fn test_introduce_var_does_not_add_extra_whitespace() { |
391 | check_assist_range( | 386 | check_assist( |
392 | introduce_variable, | 387 | introduce_variable, |
393 | " | 388 | " |
394 | fn foo() -> u32 { | 389 | fn foo() -> u32 { |
@@ -407,7 +402,7 @@ fn foo() -> u32 { | |||
407 | ", | 402 | ", |
408 | ); | 403 | ); |
409 | 404 | ||
410 | check_assist_range( | 405 | check_assist( |
411 | introduce_variable, | 406 | introduce_variable, |
412 | " | 407 | " |
413 | fn foo() -> u32 { | 408 | fn foo() -> u32 { |
@@ -424,7 +419,7 @@ fn foo() -> u32 { | |||
424 | ", | 419 | ", |
425 | ); | 420 | ); |
426 | 421 | ||
427 | check_assist_range( | 422 | check_assist( |
428 | introduce_variable, | 423 | introduce_variable, |
429 | " | 424 | " |
430 | fn foo() -> u32 { | 425 | fn foo() -> u32 { |
@@ -452,7 +447,7 @@ fn foo() -> u32 { | |||
452 | 447 | ||
453 | #[test] | 448 | #[test] |
454 | fn test_introduce_var_break() { | 449 | fn test_introduce_var_break() { |
455 | check_assist_range( | 450 | check_assist( |
456 | introduce_variable, | 451 | introduce_variable, |
457 | " | 452 | " |
458 | fn main() { | 453 | fn main() { |
@@ -474,7 +469,7 @@ fn main() { | |||
474 | 469 | ||
475 | #[test] | 470 | #[test] |
476 | fn test_introduce_var_for_cast() { | 471 | fn test_introduce_var_for_cast() { |
477 | check_assist_range( | 472 | check_assist( |
478 | introduce_variable, | 473 | introduce_variable, |
479 | " | 474 | " |
480 | fn main() { | 475 | fn main() { |
@@ -492,27 +487,20 @@ fn main() { | |||
492 | 487 | ||
493 | #[test] | 488 | #[test] |
494 | fn test_introduce_var_for_return_not_applicable() { | 489 | fn test_introduce_var_for_return_not_applicable() { |
495 | check_assist_range_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); | 490 | check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); |
496 | } | 491 | } |
497 | 492 | ||
498 | #[test] | 493 | #[test] |
499 | fn test_introduce_var_for_break_not_applicable() { | 494 | fn test_introduce_var_for_break_not_applicable() { |
500 | check_assist_range_not_applicable( | 495 | check_assist_not_applicable(introduce_variable, "fn main() { loop { <|>break<|>; }; }"); |
501 | introduce_variable, | ||
502 | "fn main() { loop { <|>break<|>; }; }", | ||
503 | ); | ||
504 | } | 496 | } |
505 | 497 | ||
506 | // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic | 498 | // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic |
507 | #[test] | 499 | #[test] |
508 | fn introduce_var_target() { | 500 | fn introduce_var_target() { |
509 | check_assist_range_target( | 501 | check_assist_target(introduce_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2"); |
510 | introduce_variable, | ||
511 | "fn foo() -> u32 { <|>return 2 + 2<|>; }", | ||
512 | "2 + 2", | ||
513 | ); | ||
514 | 502 | ||
515 | check_assist_range_target( | 503 | check_assist_target( |
516 | introduce_variable, | 504 | introduce_variable, |
517 | " | 505 | " |
518 | fn main() { | 506 | fn main() { |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index d7998b0d1..c28a9b92b 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -19,6 +19,7 @@ use ra_text_edit::TextEdit; | |||
19 | 19 | ||
20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; | 20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; |
21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; | 21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; |
22 | use hir::Semantics; | ||
22 | 23 | ||
23 | /// Unique identifier of the assist, should not be shown to the user | 24 | /// Unique identifier of the assist, should not be shown to the user |
24 | /// directly. | 25 | /// directly. |
@@ -63,7 +64,8 @@ pub struct ResolvedAssist { | |||
63 | /// Assists are returned in the "unresolved" state, that is only labels are | 64 | /// Assists are returned in the "unresolved" state, that is only labels are |
64 | /// returned, without actual edits. | 65 | /// returned, without actual edits. |
65 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { | 66 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { |
66 | let ctx = AssistCtx::new(db, range, false); | 67 | let sema = Semantics::new(db); |
68 | let ctx = AssistCtx::new(&sema, range, false); | ||
67 | handlers::all() | 69 | handlers::all() |
68 | .iter() | 70 | .iter() |
69 | .filter_map(|f| f(ctx.clone())) | 71 | .filter_map(|f| f(ctx.clone())) |
@@ -77,7 +79,8 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe | |||
77 | /// Assists are returned in the "resolved" state, that is with edit fully | 79 | /// Assists are returned in the "resolved" state, that is with edit fully |
78 | /// computed. | 80 | /// computed. |
79 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 81 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { |
80 | let ctx = AssistCtx::new(db, range, true); | 82 | let sema = Semantics::new(db); |
83 | let ctx = AssistCtx::new(&sema, range, true); | ||
81 | let mut a = handlers::all() | 84 | let mut a = handlers::all() |
82 | .iter() | 85 | .iter() |
83 | .filter_map(|f| f(ctx.clone())) | 86 | .filter_map(|f| f(ctx.clone())) |
@@ -162,9 +165,10 @@ mod helpers { | |||
162 | use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | 165 | use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; |
163 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; | 166 | use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; |
164 | use ra_syntax::TextRange; | 167 | use ra_syntax::TextRange; |
165 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; | 168 | use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; |
166 | 169 | ||
167 | use crate::{AssistCtx, AssistHandler}; | 170 | use crate::{AssistCtx, AssistHandler}; |
171 | use hir::Semantics; | ||
168 | 172 | ||
169 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 173 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
170 | let (mut db, file_id) = RootDatabase::with_single_file(text); | 174 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
@@ -176,81 +180,66 @@ mod helpers { | |||
176 | } | 180 | } |
177 | 181 | ||
178 | pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) { | 182 | pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) { |
179 | let (before_cursor_pos, before) = extract_offset(before); | 183 | check(assist, before, ExpectedResult::After(after)); |
180 | let (db, file_id) = with_single_file(&before); | ||
181 | let frange = | ||
182 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
183 | let assist = | ||
184 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); | ||
185 | let action = assist.0[0].action.clone().unwrap(); | ||
186 | |||
187 | let actual = action.edit.apply(&before); | ||
188 | let actual_cursor_pos = match action.cursor_position { | ||
189 | None => action | ||
190 | .edit | ||
191 | .apply_to_offset(before_cursor_pos) | ||
192 | .expect("cursor position is affected by the edit"), | ||
193 | Some(off) => off, | ||
194 | }; | ||
195 | let actual = add_cursor(&actual, actual_cursor_pos); | ||
196 | assert_eq_text!(after, &actual); | ||
197 | } | ||
198 | |||
199 | pub(crate) fn check_assist_range(assist: AssistHandler, before: &str, after: &str) { | ||
200 | let (range, before) = extract_range(before); | ||
201 | let (db, file_id) = with_single_file(&before); | ||
202 | let frange = FileRange { file_id, range }; | ||
203 | let assist = | ||
204 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); | ||
205 | let action = assist.0[0].action.clone().unwrap(); | ||
206 | |||
207 | let mut actual = action.edit.apply(&before); | ||
208 | if let Some(pos) = action.cursor_position { | ||
209 | actual = add_cursor(&actual, pos); | ||
210 | } | ||
211 | assert_eq_text!(after, &actual); | ||
212 | } | 184 | } |
213 | 185 | ||
186 | // FIXME: instead of having a separate function here, maybe use | ||
187 | // `extract_ranges` and mark the target as `<target> </target>` in the | ||
188 | // fixuture? | ||
214 | pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) { | 189 | pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) { |
215 | let (before_cursor_pos, before) = extract_offset(before); | 190 | check(assist, before, ExpectedResult::Target(target)); |
216 | let (db, file_id) = with_single_file(&before); | ||
217 | let frange = | ||
218 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
219 | let assist = | ||
220 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); | ||
221 | let action = assist.0[0].action.clone().unwrap(); | ||
222 | |||
223 | let range = action.target.expect("expected target on action"); | ||
224 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | ||
225 | } | 191 | } |
226 | 192 | ||
227 | pub(crate) fn check_assist_range_target(assist: AssistHandler, before: &str, target: &str) { | 193 | pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) { |
228 | let (range, before) = extract_range(before); | 194 | check(assist, before, ExpectedResult::NotApplicable); |
229 | let (db, file_id) = with_single_file(&before); | ||
230 | let frange = FileRange { file_id, range }; | ||
231 | let assist = | ||
232 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); | ||
233 | let action = assist.0[0].action.clone().unwrap(); | ||
234 | |||
235 | let range = action.target.expect("expected target on action"); | ||
236 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | ||
237 | } | 195 | } |
238 | 196 | ||
239 | pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) { | 197 | enum ExpectedResult<'a> { |
240 | let (before_cursor_pos, before) = extract_offset(before); | 198 | NotApplicable, |
241 | let (db, file_id) = with_single_file(&before); | 199 | After(&'a str), |
242 | let frange = | 200 | Target(&'a str), |
243 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
244 | let assist = assist(AssistCtx::new(&db, frange, true)); | ||
245 | assert!(assist.is_none()); | ||
246 | } | 201 | } |
247 | 202 | ||
248 | pub(crate) fn check_assist_range_not_applicable(assist: AssistHandler, before: &str) { | 203 | fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { |
249 | let (range, before) = extract_range(before); | 204 | let (range_or_offset, before) = extract_range_or_offset(before); |
205 | let range: TextRange = range_or_offset.into(); | ||
206 | |||
250 | let (db, file_id) = with_single_file(&before); | 207 | let (db, file_id) = with_single_file(&before); |
251 | let frange = FileRange { file_id, range }; | 208 | let frange = FileRange { file_id, range }; |
252 | let assist = assist(AssistCtx::new(&db, frange, true)); | 209 | let sema = Semantics::new(&db); |
253 | assert!(assist.is_none()); | 210 | let assist_ctx = AssistCtx::new(&sema, frange, true); |
211 | |||
212 | match (assist(assist_ctx), expected) { | ||
213 | (Some(assist), ExpectedResult::After(after)) => { | ||
214 | let action = assist.0[0].action.clone().unwrap(); | ||
215 | |||
216 | let mut actual = action.edit.apply(&before); | ||
217 | match action.cursor_position { | ||
218 | None => { | ||
219 | if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { | ||
220 | let off = action | ||
221 | .edit | ||
222 | .apply_to_offset(before_cursor_pos) | ||
223 | .expect("cursor position is affected by the edit"); | ||
224 | actual = add_cursor(&actual, off) | ||
225 | } | ||
226 | } | ||
227 | Some(off) => actual = add_cursor(&actual, off), | ||
228 | }; | ||
229 | |||
230 | assert_eq_text!(after, &actual); | ||
231 | } | ||
232 | (Some(assist), ExpectedResult::Target(target)) => { | ||
233 | let action = assist.0[0].action.clone().unwrap(); | ||
234 | let range = action.target.expect("expected target on action"); | ||
235 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | ||
236 | } | ||
237 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), | ||
238 | (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => { | ||
239 | panic!("code action is not applicable") | ||
240 | } | ||
241 | (None, ExpectedResult::NotApplicable) => (), | ||
242 | }; | ||
254 | } | 243 | } |
255 | } | 244 | } |
256 | 245 | ||
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 6ff44c95c..92d3ed471 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,16 +1,15 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | 2 | ||
3 | use hir::Semantics; | ||
4 | use ra_ide_db::RootDatabase; | ||
3 | use ra_syntax::{ | 5 | use ra_syntax::{ |
4 | ast::{self, make, NameOwner}, | 6 | ast::{self, make, NameOwner}, |
5 | AstNode, T, | 7 | AstNode, T, |
6 | }; | 8 | }; |
7 | |||
8 | use hir::db::HirDatabase; | ||
9 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
10 | 10 | ||
11 | pub fn get_missing_impl_items( | 11 | pub fn get_missing_impl_items( |
12 | db: &impl HirDatabase, | 12 | sema: &Semantics<RootDatabase>, |
13 | analyzer: &hir::SourceAnalyzer, | ||
14 | impl_block: &ast::ImplBlock, | 13 | impl_block: &ast::ImplBlock, |
15 | ) -> Vec<hir::AssocItem> { | 14 | ) -> Vec<hir::AssocItem> { |
16 | // Names must be unique between constants and functions. However, type aliases | 15 | // Names must be unique between constants and functions. However, type aliases |
@@ -42,15 +41,17 @@ pub fn get_missing_impl_items( | |||
42 | } | 41 | } |
43 | } | 42 | } |
44 | 43 | ||
45 | resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| { | 44 | resolve_target_trait(sema, impl_block).map_or(vec![], |target_trait| { |
46 | target_trait | 45 | target_trait |
47 | .items(db) | 46 | .items(sema.db) |
48 | .iter() | 47 | .iter() |
49 | .filter(|i| match i { | 48 | .filter(|i| match i { |
50 | hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(db).to_string()), | 49 | hir::AssocItem::Function(f) => { |
51 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()), | 50 | !impl_fns_consts.contains(&f.name(sema.db).to_string()) |
51 | } | ||
52 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), | ||
52 | hir::AssocItem::Const(c) => c | 53 | hir::AssocItem::Const(c) => c |
53 | .name(db) | 54 | .name(sema.db) |
54 | .map(|n| !impl_fns_consts.contains(&n.to_string())) | 55 | .map(|n| !impl_fns_consts.contains(&n.to_string())) |
55 | .unwrap_or_default(), | 56 | .unwrap_or_default(), |
56 | }) | 57 | }) |
@@ -60,8 +61,7 @@ pub fn get_missing_impl_items( | |||
60 | } | 61 | } |
61 | 62 | ||
62 | pub(crate) fn resolve_target_trait( | 63 | pub(crate) fn resolve_target_trait( |
63 | db: &impl HirDatabase, | 64 | sema: &Semantics<RootDatabase>, |
64 | analyzer: &hir::SourceAnalyzer, | ||
65 | impl_block: &ast::ImplBlock, | 65 | impl_block: &ast::ImplBlock, |
66 | ) -> Option<hir::Trait> { | 66 | ) -> Option<hir::Trait> { |
67 | let ast_path = impl_block | 67 | let ast_path = impl_block |
@@ -70,7 +70,7 @@ pub(crate) fn resolve_target_trait( | |||
70 | .and_then(ast::PathType::cast)? | 70 | .and_then(ast::PathType::cast)? |
71 | .path()?; | 71 | .path()?; |
72 | 72 | ||
73 | match analyzer.resolve_path(db, &ast_path) { | 73 | match sema.resolve_path(&ast_path) { |
74 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), | 74 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), |
75 | _ => None, | 75 | _ => None, |
76 | } | 76 | } |
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml index b09650d98..1fdbffea1 100644 --- a/crates/ra_cargo_watch/Cargo.toml +++ b/crates/ra_cargo_watch/Cargo.toml | |||
@@ -6,13 +6,10 @@ authors = ["rust-analyzer developers"] | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | crossbeam-channel = "0.4.0" | 8 | crossbeam-channel = "0.4.0" |
9 | lsp-types = { version = "0.70.1", features = ["proposed"] } | 9 | lsp-types = { version = "0.71.0", features = ["proposed"] } |
10 | log = "0.4.8" | 10 | log = "0.4.8" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | jod-thread = "0.1.0" | ||
13 | parking_lot = "0.10.0" | ||
14 | serde_json = "1.0.48" | 12 | serde_json = "1.0.48" |
15 | 13 | ||
16 | [dev-dependencies] | 14 | [dev-dependencies] |
17 | insta = "0.13.1" | 15 | insta = "0.13.1" |
18 | serde_json = "1.0.48" | ||
diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs index 4bca27b5c..db27f9b83 100644 --- a/crates/ra_fmt/src/lib.rs +++ b/crates/ra_fmt/src/lib.rs | |||
@@ -43,15 +43,35 @@ pub fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | |||
43 | 43 | ||
44 | pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | 44 | pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { |
45 | let block = block.block()?; | 45 | let block = block.block()?; |
46 | let expr = block.expr()?; | 46 | let has_anything_else = |thing: &SyntaxNode| -> bool { |
47 | let non_trivial_children = block.syntax().children().filter(|it| match it.kind() { | 47 | let mut non_trivial_children = |
48 | WHITESPACE | T!['{'] | T!['}'] => false, | 48 | block.syntax().children_with_tokens().filter(|it| match it.kind() { |
49 | _ => it != expr.syntax(), | 49 | WHITESPACE | T!['{'] | T!['}'] => false, |
50 | }); | 50 | _ => it.as_node() != Some(thing), |
51 | if non_trivial_children.count() > 0 { | 51 | }); |
52 | return None; | 52 | non_trivial_children.next().is_some() |
53 | }; | ||
54 | |||
55 | if let Some(expr) = block.expr() { | ||
56 | if has_anything_else(expr.syntax()) { | ||
57 | return None; | ||
58 | } | ||
59 | return Some(expr); | ||
60 | } else { | ||
61 | // Unwrap `{ continue; }` | ||
62 | let (stmt,) = block.statements().next_tuple()?; | ||
63 | if has_anything_else(stmt.syntax()) { | ||
64 | return None; | ||
65 | } | ||
66 | if let ast::Stmt::ExprStmt(expr_stmt) = stmt { | ||
67 | let expr = expr_stmt.expr()?; | ||
68 | match expr.syntax().kind() { | ||
69 | CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr), | ||
70 | _ => (), | ||
71 | } | ||
72 | } | ||
53 | } | 73 | } |
54 | Some(expr) | 74 | None |
55 | } | 75 | } |
56 | 76 | ||
57 | pub fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str { | 77 | pub fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str { |
diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs index c16c17072..3aa7c4870 100644 --- a/crates/ra_hir/src/from_id.rs +++ b/crates/ra_hir/src/from_id.rs | |||
@@ -40,6 +40,7 @@ from_id![ | |||
40 | (hir_def::ConstId, crate::Const), | 40 | (hir_def::ConstId, crate::Const), |
41 | (hir_def::FunctionId, crate::Function), | 41 | (hir_def::FunctionId, crate::Function), |
42 | (hir_def::ImplId, crate::ImplBlock), | 42 | (hir_def::ImplId, crate::ImplBlock), |
43 | (hir_def::TypeParamId, crate::TypeParam), | ||
43 | (hir_expand::MacroDefId, crate::MacroDef) | 44 | (hir_expand::MacroDefId, crate::MacroDef) |
44 | ]; | 45 | ]; |
45 | 46 | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7a9745ebe..4a85e7e36 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -26,6 +26,7 @@ macro_rules! impl_froms { | |||
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | mod semantics; | ||
29 | pub mod db; | 30 | pub mod db; |
30 | pub mod source_analyzer; | 31 | pub mod source_analyzer; |
31 | pub mod source_binder; | 32 | pub mod source_binder; |
@@ -45,8 +46,8 @@ pub use crate::{ | |||
45 | StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, | 46 | StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, |
46 | }, | 47 | }, |
47 | has_source::HasSource, | 48 | has_source::HasSource, |
48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, | 49 | semantics::{original_range, Semantics, SemanticsScope}, |
49 | source_binder::SourceBinder, | 50 | source_analyzer::PathResolution, |
50 | }; | 51 | }; |
51 | 52 | ||
52 | pub use hir_def::{ | 53 | pub use hir_def::{ |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs new file mode 100644 index 000000000..c3d8ee1ae --- /dev/null +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -0,0 +1,412 @@ | |||
1 | //! See `Semantics`. | ||
2 | |||
3 | use std::{cell::RefCell, fmt, iter::successors}; | ||
4 | |||
5 | use hir_def::{ | ||
6 | resolver::{self, HasResolver, Resolver}, | ||
7 | DefWithBodyId, TraitId, | ||
8 | }; | ||
9 | use ra_db::{FileId, FileRange}; | ||
10 | use ra_syntax::{ | ||
11 | algo::{find_covering_element, skip_trivia_token}, | ||
12 | ast, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, | ||
13 | TextRange, TextUnit, | ||
14 | }; | ||
15 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
16 | |||
17 | use crate::{ | ||
18 | db::HirDatabase, | ||
19 | source_analyzer::{resolve_hir_path, ReferenceDescriptor, SourceAnalyzer}, | ||
20 | source_binder::{ChildContainer, SourceBinder}, | ||
21 | Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution, | ||
22 | ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, | ||
23 | }; | ||
24 | use ra_prof::profile; | ||
25 | |||
26 | /// Primary API to get semantic information, like types, from syntax trees. | ||
27 | pub struct Semantics<'db, DB> { | ||
28 | pub db: &'db DB, | ||
29 | pub(crate) sb: RefCell<SourceBinder>, | ||
30 | cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, | ||
31 | } | ||
32 | |||
33 | impl<DB> fmt::Debug for Semantics<'_, DB> { | ||
34 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
35 | write!(f, "Semantics {{ ... }}") | ||
36 | } | ||
37 | } | ||
38 | |||
39 | impl<'db, DB: HirDatabase> Semantics<'db, DB> { | ||
40 | pub fn new(db: &DB) -> Semantics<DB> { | ||
41 | let sb = RefCell::new(SourceBinder::new()); | ||
42 | Semantics { db, sb, cache: RefCell::default() } | ||
43 | } | ||
44 | |||
45 | pub fn parse(&self, file_id: FileId) -> ast::SourceFile { | ||
46 | let tree = self.db.parse(file_id).tree(); | ||
47 | self.cache(tree.syntax().clone(), file_id.into()); | ||
48 | tree | ||
49 | } | ||
50 | |||
51 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | ||
52 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | ||
53 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
54 | let file_id = sa.expand(self.db, macro_call)?; | ||
55 | let node = self.db.parse_or_expand(file_id)?; | ||
56 | self.cache(node.clone(), file_id); | ||
57 | Some(node) | ||
58 | } | ||
59 | |||
60 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | ||
61 | let parent = token.parent(); | ||
62 | let parent = self.find_file(parent); | ||
63 | let sa = self.analyze2(parent.as_ref(), None); | ||
64 | |||
65 | let token = successors(Some(parent.with_value(token)), |token| { | ||
66 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | ||
67 | let tt = macro_call.token_tree()?; | ||
68 | if !token.value.text_range().is_subrange(&tt.syntax().text_range()) { | ||
69 | return None; | ||
70 | } | ||
71 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; | ||
72 | let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; | ||
73 | |||
74 | self.cache(find_root(&token.value.parent()), token.file_id); | ||
75 | |||
76 | Some(token) | ||
77 | }) | ||
78 | .last() | ||
79 | .unwrap(); | ||
80 | |||
81 | token.value | ||
82 | } | ||
83 | |||
84 | pub fn original_range(&self, node: &SyntaxNode) -> FileRange { | ||
85 | let node = self.find_file(node.clone()); | ||
86 | original_range(self.db, node.as_ref()) | ||
87 | } | ||
88 | |||
89 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
90 | let node = self.find_file(node); | ||
91 | node.ancestors_with_macros(self.db).map(|it| it.value) | ||
92 | } | ||
93 | |||
94 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | ||
95 | self.analyze(expr.syntax()).type_of(self.db, &expr) | ||
96 | } | ||
97 | |||
98 | pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { | ||
99 | self.analyze(pat.syntax()).type_of_pat(self.db, &pat) | ||
100 | } | ||
101 | |||
102 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
103 | self.analyze(call.syntax()).resolve_method_call(call) | ||
104 | } | ||
105 | |||
106 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> { | ||
107 | self.analyze(field.syntax()).resolve_field(field) | ||
108 | } | ||
109 | |||
110 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> { | ||
111 | self.analyze(field.syntax()).resolve_record_field(field) | ||
112 | } | ||
113 | |||
114 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> { | ||
115 | self.analyze(record_lit.syntax()).resolve_record_literal(record_lit) | ||
116 | } | ||
117 | |||
118 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<VariantDef> { | ||
119 | self.analyze(record_pat.syntax()).resolve_record_pattern(record_pat) | ||
120 | } | ||
121 | |||
122 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | ||
123 | let sa = self.analyze(macro_call.syntax()); | ||
124 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | ||
125 | sa.resolve_macro_call(self.db, macro_call) | ||
126 | } | ||
127 | |||
128 | pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { | ||
129 | self.analyze(path.syntax()).resolve_path(self.db, path) | ||
130 | } | ||
131 | |||
132 | // FIXME: use this instead? | ||
133 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; | ||
134 | |||
135 | pub fn to_def<T: ToDef + Clone>(&self, src: &T) -> Option<T::Def> { | ||
136 | T::to_def(self, src) | ||
137 | } | ||
138 | |||
139 | pub fn to_module_def(&self, file: FileId) -> Option<Module> { | ||
140 | let mut sb = self.sb.borrow_mut(); | ||
141 | sb.to_module_def(self.db, file) | ||
142 | } | ||
143 | |||
144 | pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> { | ||
145 | let node = self.find_file(node.clone()); | ||
146 | let resolver = self.analyze2(node.as_ref(), None).resolver; | ||
147 | SemanticsScope { db: self.db, resolver } | ||
148 | } | ||
149 | |||
150 | pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextUnit) -> SemanticsScope<'db, DB> { | ||
151 | let node = self.find_file(node.clone()); | ||
152 | let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; | ||
153 | SemanticsScope { db: self.db, resolver } | ||
154 | } | ||
155 | |||
156 | pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> { | ||
157 | let resolver = def.id.resolver(self.db); | ||
158 | SemanticsScope { db: self.db, resolver } | ||
159 | } | ||
160 | |||
161 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | ||
162 | // should switch to general reference search infra there. | ||
163 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
164 | self.analyze(pat.syntax()).find_all_refs(pat) | ||
165 | } | ||
166 | |||
167 | fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer { | ||
168 | let src = self.find_file(node.clone()); | ||
169 | self.analyze2(src.as_ref(), None) | ||
170 | } | ||
171 | |||
172 | fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option<TextUnit>) -> SourceAnalyzer { | ||
173 | let _p = profile("Semantics::analyze2"); | ||
174 | |||
175 | let container = match self.sb.borrow_mut().find_container(self.db, src) { | ||
176 | Some(it) => it, | ||
177 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
178 | }; | ||
179 | |||
180 | let resolver = match container { | ||
181 | ChildContainer::DefWithBodyId(def) => { | ||
182 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | ||
183 | } | ||
184 | ChildContainer::TraitId(it) => it.resolver(self.db), | ||
185 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
186 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
187 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
188 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
189 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | ||
190 | }; | ||
191 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
192 | } | ||
193 | |||
194 | fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { | ||
195 | assert!(root_node.parent().is_none()); | ||
196 | let mut cache = self.cache.borrow_mut(); | ||
197 | let prev = cache.insert(root_node, file_id); | ||
198 | assert!(prev == None || prev == Some(file_id)) | ||
199 | } | ||
200 | |||
201 | pub fn assert_contains_node(&self, node: &SyntaxNode) { | ||
202 | self.find_file(node.clone()); | ||
203 | } | ||
204 | |||
205 | fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> { | ||
206 | let cache = self.cache.borrow(); | ||
207 | cache.get(root_node).copied() | ||
208 | } | ||
209 | |||
210 | fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { | ||
211 | let root_node = find_root(&node); | ||
212 | let file_id = self.lookup(&root_node).unwrap_or_else(|| { | ||
213 | panic!( | ||
214 | "\n\nFailed to lookup {:?} in this Semantics.\n\ | ||
215 | Make sure to use only query nodes, derived from this instance of Semantics.\n\ | ||
216 | root node: {:?}\n\ | ||
217 | known nodes: {}\n\n", | ||
218 | node, | ||
219 | root_node, | ||
220 | self.cache | ||
221 | .borrow() | ||
222 | .keys() | ||
223 | .map(|it| format!("{:?}", it)) | ||
224 | .collect::<Vec<_>>() | ||
225 | .join(", ") | ||
226 | ) | ||
227 | }); | ||
228 | InFile::new(file_id, node) | ||
229 | } | ||
230 | } | ||
231 | |||
232 | pub trait ToDef: Sized + AstNode + 'static { | ||
233 | type Def; | ||
234 | fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: &Self) -> Option<Self::Def>; | ||
235 | } | ||
236 | |||
237 | macro_rules! to_def_impls { | ||
238 | ($(($def:path, $ast:path)),* ,) => {$( | ||
239 | impl ToDef for $ast { | ||
240 | type Def = $def; | ||
241 | fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: &Self) | ||
242 | -> Option<Self::Def> | ||
243 | { | ||
244 | let src = sema.find_file(src.syntax().clone()).with_value(src); | ||
245 | sema.sb.borrow_mut().to_id(sema.db, src.cloned()).map(Into::into) | ||
246 | } | ||
247 | } | ||
248 | )*} | ||
249 | } | ||
250 | |||
251 | to_def_impls![ | ||
252 | (crate::Module, ast::Module), | ||
253 | (crate::Struct, ast::StructDef), | ||
254 | (crate::Enum, ast::EnumDef), | ||
255 | (crate::Union, ast::UnionDef), | ||
256 | (crate::Trait, ast::TraitDef), | ||
257 | (crate::ImplBlock, ast::ImplBlock), | ||
258 | (crate::TypeAlias, ast::TypeAliasDef), | ||
259 | (crate::Const, ast::ConstDef), | ||
260 | (crate::Static, ast::StaticDef), | ||
261 | (crate::Function, ast::FnDef), | ||
262 | (crate::StructField, ast::RecordFieldDef), | ||
263 | (crate::EnumVariant, ast::EnumVariant), | ||
264 | (crate::TypeParam, ast::TypeParam), | ||
265 | (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros | ||
266 | ]; | ||
267 | |||
268 | impl ToDef for ast::BindPat { | ||
269 | type Def = Local; | ||
270 | |||
271 | fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: &Self) -> Option<Local> { | ||
272 | let src = sema.find_file(src.syntax().clone()).with_value(src); | ||
273 | let file_id = src.file_id; | ||
274 | let mut sb = sema.sb.borrow_mut(); | ||
275 | let db = sema.db; | ||
276 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { | ||
277 | let res = match_ast! { | ||
278 | match it { | ||
279 | ast::ConstDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, | ||
280 | ast::StaticDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, | ||
281 | ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, | ||
282 | _ => return None, | ||
283 | } | ||
284 | }; | ||
285 | Some(res) | ||
286 | })?; | ||
287 | let (_body, source_map) = db.body_with_source_map(parent); | ||
288 | let src = src.cloned().map(ast::Pat::from); | ||
289 | let pat_id = source_map.node_pat(src.as_ref())?; | ||
290 | Some(Local { parent: parent.into(), pat_id }) | ||
291 | } | ||
292 | } | ||
293 | |||
294 | fn find_root(node: &SyntaxNode) -> SyntaxNode { | ||
295 | node.ancestors().last().unwrap() | ||
296 | } | ||
297 | |||
298 | pub struct SemanticsScope<'a, DB> { | ||
299 | pub db: &'a DB, | ||
300 | resolver: Resolver, | ||
301 | } | ||
302 | |||
303 | impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | ||
304 | pub fn module(&self) -> Option<Module> { | ||
305 | Some(Module { id: self.resolver.module()? }) | ||
306 | } | ||
307 | |||
308 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | ||
309 | // FIXME: rename to visible_traits to not repeat scope? | ||
310 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { | ||
311 | let resolver = &self.resolver; | ||
312 | resolver.traits_in_scope(self.db) | ||
313 | } | ||
314 | |||
315 | pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
316 | let resolver = &self.resolver; | ||
317 | |||
318 | resolver.process_all_names(self.db, &mut |name, def| { | ||
319 | let def = match def { | ||
320 | resolver::ScopeDef::PerNs(it) => it.into(), | ||
321 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
322 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
323 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
324 | resolver::ScopeDef::Local(pat_id) => { | ||
325 | let parent = resolver.body_owner().unwrap().into(); | ||
326 | ScopeDef::Local(Local { parent, pat_id }) | ||
327 | } | ||
328 | }; | ||
329 | f(name, def) | ||
330 | }) | ||
331 | } | ||
332 | |||
333 | pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> { | ||
334 | resolve_hir_path(self.db, &self.resolver, path) | ||
335 | } | ||
336 | } | ||
337 | |||
338 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? | ||
339 | pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { | ||
340 | let mut elem: InFile<SyntaxElement> = node.map(|n| n.clone().into()); | ||
341 | |||
342 | while let Some((range, Origin::Call)) = original_range_and_origin(db, elem.as_ref()) { | ||
343 | let original_file = range.file_id.original_file(db); | ||
344 | |||
345 | if range.file_id == original_file.into() { | ||
346 | return FileRange { file_id: original_file, range: range.value }; | ||
347 | } | ||
348 | |||
349 | if range.file_id != elem.file_id { | ||
350 | if let Some(root) = db.parse_or_expand(range.file_id) { | ||
351 | elem = range.with_value(find_covering_element(&root, range.value)); | ||
352 | continue; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | log::error!("Fail to mapping up more for {:?}", range); | ||
357 | return FileRange { file_id: range.file_id.original_file(db), range: range.value }; | ||
358 | } | ||
359 | |||
360 | // Fall back to whole macro call | ||
361 | if let Some(expansion) = node.file_id.expansion_info(db) { | ||
362 | if let Some(call_node) = expansion.call_node() { | ||
363 | return FileRange { | ||
364 | file_id: call_node.file_id.original_file(db), | ||
365 | range: call_node.value.text_range(), | ||
366 | }; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | ||
371 | } | ||
372 | |||
373 | fn original_range_and_origin( | ||
374 | db: &impl HirDatabase, | ||
375 | elem: InFile<&SyntaxElement>, | ||
376 | ) -> Option<(InFile<TextRange>, Origin)> { | ||
377 | let expansion = elem.file_id.expansion_info(db)?; | ||
378 | |||
379 | let node = match elem.as_ref().value { | ||
380 | NodeOrToken::Node(it) => elem.with_value(it), | ||
381 | NodeOrToken::Token(it) => { | ||
382 | let (tt, origin) = expansion.map_token_up(elem.with_value(it))?; | ||
383 | return Some((tt.map(|it| it.text_range()), origin)); | ||
384 | } | ||
385 | }; | ||
386 | |||
387 | // the input node has only one token ? | ||
388 | let single = skip_trivia_token(node.value.first_token()?, Direction::Next)? | ||
389 | == skip_trivia_token(node.value.last_token()?, Direction::Prev)?; | ||
390 | |||
391 | Some(node.value.descendants().find_map(|it| { | ||
392 | let first = skip_trivia_token(it.first_token()?, Direction::Next)?; | ||
393 | let last = skip_trivia_token(it.last_token()?, Direction::Prev)?; | ||
394 | |||
395 | if !single && first == last { | ||
396 | return None; | ||
397 | } | ||
398 | |||
399 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | ||
400 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | ||
401 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
402 | |||
403 | if first.file_id != last.file_id || first_origin != last_origin { | ||
404 | return None; | ||
405 | } | ||
406 | |||
407 | Some(( | ||
408 | first.with_value(first.value.text_range().extend_to(&last.value.text_range())), | ||
409 | first_origin, | ||
410 | )) | ||
411 | })?) | ||
412 | } | ||
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index efa3f8a79..b655e2c32 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -14,29 +14,27 @@ use hir_def::{ | |||
14 | BodySourceMap, | 14 | BodySourceMap, |
15 | }, | 15 | }, |
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, | 17 | resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, |
18 | AsMacroCall, DefWithBodyId, TraitId, | 18 | AsMacroCall, DefWithBodyId, |
19 | }; | 19 | }; |
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; | 20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; |
21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; | 21 | use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; |
22 | use ra_syntax::{ | 22 | use ra_syntax::{ |
23 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
24 | AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | 24 | AstPtr, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, |
25 | }; | 25 | }; |
26 | use rustc_hash::FxHashSet; | ||
27 | 26 | ||
28 | use crate::{ | 27 | use crate::{ |
29 | db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, | 28 | db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, Path, Static, Struct, |
30 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | 29 | Trait, Type, TypeAlias, TypeParam, |
31 | }; | 30 | }; |
32 | 31 | ||
33 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | 32 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of |
34 | /// original source files. It should not be used inside the HIR itself. | 33 | /// original source files. It should not be used inside the HIR itself. |
35 | #[derive(Debug)] | 34 | #[derive(Debug)] |
36 | pub struct SourceAnalyzer { | 35 | pub(crate) struct SourceAnalyzer { |
37 | file_id: HirFileId, | 36 | file_id: HirFileId, |
38 | resolver: Resolver, | 37 | pub(crate) resolver: Resolver, |
39 | body_owner: Option<DefWithBody>, | ||
40 | body_source_map: Option<Arc<BodySourceMap>>, | 38 | body_source_map: Option<Arc<BodySourceMap>>, |
41 | infer: Option<Arc<InferenceResult>>, | 39 | infer: Option<Arc<InferenceResult>>, |
42 | scopes: Option<Arc<ExprScopes>>, | 40 | scopes: Option<Arc<ExprScopes>>, |
@@ -55,57 +53,13 @@ pub enum PathResolution { | |||
55 | AssocItem(crate::AssocItem), | 53 | AssocItem(crate::AssocItem), |
56 | } | 54 | } |
57 | 55 | ||
58 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
59 | pub struct ScopeEntryWithSyntax { | ||
60 | pub(crate) name: Name, | ||
61 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
62 | } | ||
63 | |||
64 | impl ScopeEntryWithSyntax { | ||
65 | pub fn name(&self) -> &Name { | ||
66 | &self.name | ||
67 | } | ||
68 | |||
69 | pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> { | ||
70 | self.ptr | ||
71 | } | ||
72 | } | ||
73 | |||
74 | #[derive(Debug)] | 56 | #[derive(Debug)] |
75 | pub struct ReferenceDescriptor { | 57 | pub struct ReferenceDescriptor { |
76 | pub range: TextRange, | 58 | pub range: TextRange, |
77 | pub name: String, | 59 | pub name: String, |
78 | } | 60 | } |
79 | 61 | ||
80 | #[derive(Debug)] | ||
81 | pub struct Expansion { | ||
82 | macro_call_id: MacroCallId, | ||
83 | } | ||
84 | |||
85 | impl Expansion { | ||
86 | pub fn map_token_down( | ||
87 | &self, | ||
88 | db: &impl HirDatabase, | ||
89 | token: InFile<&SyntaxToken>, | ||
90 | ) -> Option<InFile<SyntaxToken>> { | ||
91 | let exp_info = self.file_id().expansion_info(db)?; | ||
92 | exp_info.map_token_down(token) | ||
93 | } | ||
94 | |||
95 | pub fn file_id(&self) -> HirFileId { | ||
96 | self.macro_call_id.as_file() | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl SourceAnalyzer { | 62 | impl SourceAnalyzer { |
101 | pub fn new( | ||
102 | db: &impl HirDatabase, | ||
103 | node: InFile<&SyntaxNode>, | ||
104 | offset: Option<TextUnit>, | ||
105 | ) -> SourceAnalyzer { | ||
106 | crate::source_binder::SourceBinder::new(db).analyze(node, offset) | ||
107 | } | ||
108 | |||
109 | pub(crate) fn new_for_body( | 63 | pub(crate) fn new_for_body( |
110 | db: &impl HirDatabase, | 64 | db: &impl HirDatabase, |
111 | def: DefWithBodyId, | 65 | def: DefWithBodyId, |
@@ -121,7 +75,6 @@ impl SourceAnalyzer { | |||
121 | let resolver = resolver_for_scope(db, def, scope); | 75 | let resolver = resolver_for_scope(db, def, scope); |
122 | SourceAnalyzer { | 76 | SourceAnalyzer { |
123 | resolver, | 77 | resolver, |
124 | body_owner: Some(def.into()), | ||
125 | body_source_map: Some(source_map), | 78 | body_source_map: Some(source_map), |
126 | infer: Some(db.infer(def)), | 79 | infer: Some(db.infer(def)), |
127 | scopes: Some(scopes), | 80 | scopes: Some(scopes), |
@@ -135,7 +88,6 @@ impl SourceAnalyzer { | |||
135 | ) -> SourceAnalyzer { | 88 | ) -> SourceAnalyzer { |
136 | SourceAnalyzer { | 89 | SourceAnalyzer { |
137 | resolver, | 90 | resolver, |
138 | body_owner: None, | ||
139 | body_source_map: None, | 91 | body_source_map: None, |
140 | infer: None, | 92 | infer: None, |
141 | scopes: None, | 93 | scopes: None, |
@@ -143,10 +95,6 @@ impl SourceAnalyzer { | |||
143 | } | 95 | } |
144 | } | 96 | } |
145 | 97 | ||
146 | pub fn module(&self) -> Option<crate::code_model::Module> { | ||
147 | Some(crate::code_model::Module { id: self.resolver.module()? }) | ||
148 | } | ||
149 | |||
150 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | 98 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { |
151 | let src = InFile { file_id: self.file_id, value: expr }; | 99 | let src = InFile { file_id: self.file_id, value: expr }; |
152 | self.body_source_map.as_ref()?.node_expr(src) | 100 | self.body_source_map.as_ref()?.node_expr(src) |
@@ -180,7 +128,7 @@ impl SourceAnalyzer { | |||
180 | TraitEnvironment::lower(db, &self.resolver) | 128 | TraitEnvironment::lower(db, &self.resolver) |
181 | } | 129 | } |
182 | 130 | ||
183 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | 131 | pub(crate) fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { |
184 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | 132 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { |
185 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | 133 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? |
186 | } else { | 134 | } else { |
@@ -192,24 +140,27 @@ impl SourceAnalyzer { | |||
192 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | 140 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) |
193 | } | 141 | } |
194 | 142 | ||
195 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | 143 | pub(crate) fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { |
196 | let pat_id = self.pat_id(pat)?; | 144 | let pat_id = self.pat_id(pat)?; |
197 | let ty = self.infer.as_ref()?[pat_id].clone(); | 145 | let ty = self.infer.as_ref()?[pat_id].clone(); |
198 | let environment = self.trait_env(db); | 146 | let environment = self.trait_env(db); |
199 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | 147 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) |
200 | } | 148 | } |
201 | 149 | ||
202 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | 150 | pub(crate) fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { |
203 | let expr_id = self.expr_id(&call.clone().into())?; | 151 | let expr_id = self.expr_id(&call.clone().into())?; |
204 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | 152 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) |
205 | } | 153 | } |
206 | 154 | ||
207 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | 155 | pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { |
208 | let expr_id = self.expr_id(&field.clone().into())?; | 156 | let expr_id = self.expr_id(&field.clone().into())?; |
209 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | 157 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) |
210 | } | 158 | } |
211 | 159 | ||
212 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | 160 | pub(crate) fn resolve_record_field( |
161 | &self, | ||
162 | field: &ast::RecordField, | ||
163 | ) -> Option<crate::StructField> { | ||
213 | let expr_id = match field.expr() { | 164 | let expr_id = match field.expr() { |
214 | Some(it) => self.expr_id(&it)?, | 165 | Some(it) => self.expr_id(&it)?, |
215 | None => { | 166 | None => { |
@@ -220,17 +171,23 @@ impl SourceAnalyzer { | |||
220 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | 171 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) |
221 | } | 172 | } |
222 | 173 | ||
223 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | 174 | pub(crate) fn resolve_record_literal( |
175 | &self, | ||
176 | record_lit: &ast::RecordLit, | ||
177 | ) -> Option<crate::VariantDef> { | ||
224 | let expr_id = self.expr_id(&record_lit.clone().into())?; | 178 | let expr_id = self.expr_id(&record_lit.clone().into())?; |
225 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | 179 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) |
226 | } | 180 | } |
227 | 181 | ||
228 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | 182 | pub(crate) fn resolve_record_pattern( |
183 | &self, | ||
184 | record_pat: &ast::RecordPat, | ||
185 | ) -> Option<crate::VariantDef> { | ||
229 | let pat_id = self.pat_id(&record_pat.clone().into())?; | 186 | let pat_id = self.pat_id(&record_pat.clone().into())?; |
230 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | 187 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) |
231 | } | 188 | } |
232 | 189 | ||
233 | pub fn resolve_macro_call( | 190 | pub(crate) fn resolve_macro_call( |
234 | &self, | 191 | &self, |
235 | db: &impl HirDatabase, | 192 | db: &impl HirDatabase, |
236 | macro_call: InFile<&ast::MacroCall>, | 193 | macro_call: InFile<&ast::MacroCall>, |
@@ -240,52 +197,11 @@ impl SourceAnalyzer { | |||
240 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | 197 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) |
241 | } | 198 | } |
242 | 199 | ||
243 | pub fn resolve_hir_path( | 200 | pub(crate) fn resolve_path( |
244 | &self, | 201 | &self, |
245 | db: &impl HirDatabase, | 202 | db: &impl HirDatabase, |
246 | path: &crate::Path, | 203 | path: &ast::Path, |
247 | ) -> Option<PathResolution> { | 204 | ) -> Option<PathResolution> { |
248 | let types = | ||
249 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | ||
250 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
251 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
252 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | ||
253 | PathResolution::Def(Adt::from(it).into()) | ||
254 | } | ||
255 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
256 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
257 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
258 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
259 | }); | ||
260 | let values = | ||
261 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
262 | let res = match val { | ||
263 | ValueNs::LocalBinding(pat_id) => { | ||
264 | let var = Local { parent: self.body_owner?, pat_id }; | ||
265 | PathResolution::Local(var) | ||
266 | } | ||
267 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
268 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
269 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
270 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
271 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
272 | }; | ||
273 | Some(res) | ||
274 | }); | ||
275 | |||
276 | let items = self | ||
277 | .resolver | ||
278 | .resolve_module_path_in_items(db, path.mod_path()) | ||
279 | .take_types() | ||
280 | .map(|it| PathResolution::Def(it.into())); | ||
281 | types.or(values).or(items).or_else(|| { | ||
282 | self.resolver | ||
283 | .resolve_path_as_macro(db, path.mod_path()) | ||
284 | .map(|def| PathResolution::Macro(def.into())) | ||
285 | }) | ||
286 | } | ||
287 | |||
288 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | ||
289 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | 205 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { |
290 | let expr_id = self.expr_id(&path_expr.into())?; | 206 | let expr_id = self.expr_id(&path_expr.into())?; |
291 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | 207 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { |
@@ -300,40 +216,24 @@ impl SourceAnalyzer { | |||
300 | } | 216 | } |
301 | // This must be a normal source file rather than macro file. | 217 | // This must be a normal source file rather than macro file. |
302 | let hir_path = crate::Path::from_ast(path.clone())?; | 218 | let hir_path = crate::Path::from_ast(path.clone())?; |
303 | self.resolve_hir_path(db, &hir_path) | 219 | resolve_hir_path(db, &self.resolver, &hir_path) |
304 | } | 220 | } |
305 | 221 | ||
306 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | 222 | fn resolve_local_name( |
223 | &self, | ||
224 | name_ref: &ast::NameRef, | ||
225 | ) -> Option<Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>> { | ||
307 | let name = name_ref.as_name(); | 226 | let name = name_ref.as_name(); |
308 | let source_map = self.body_source_map.as_ref()?; | 227 | let source_map = self.body_source_map.as_ref()?; |
309 | let scopes = self.scopes.as_ref()?; | 228 | let scopes = self.scopes.as_ref()?; |
310 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; | 229 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; |
311 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | 230 | let entry = scopes.resolve_name_in_scope(scope, &name)?; |
312 | Some(ScopeEntryWithSyntax { | 231 | Some(source_map.pat_syntax(entry.pat())?.value) |
313 | name: entry.name().clone(), | ||
314 | ptr: source_map.pat_syntax(entry.pat())?.value, | ||
315 | }) | ||
316 | } | ||
317 | |||
318 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
319 | self.resolver.process_all_names(db, &mut |name, def| { | ||
320 | let def = match def { | ||
321 | resolver::ScopeDef::PerNs(it) => it.into(), | ||
322 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
323 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
324 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
325 | resolver::ScopeDef::Local(pat_id) => { | ||
326 | let parent = self.resolver.body_owner().unwrap().into(); | ||
327 | ScopeDef::Local(Local { parent, pat_id }) | ||
328 | } | ||
329 | }; | ||
330 | f(name, def) | ||
331 | }) | ||
332 | } | 232 | } |
333 | 233 | ||
334 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | 234 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we |
335 | // should switch to general reference search infra there. | 235 | // should switch to general reference search infra there. |
336 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | 236 | pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { |
337 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 237 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
338 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | 238 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); |
339 | fn_def | 239 | fn_def |
@@ -342,7 +242,7 @@ impl SourceAnalyzer { | |||
342 | .filter_map(ast::NameRef::cast) | 242 | .filter_map(ast::NameRef::cast) |
343 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { | 243 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { |
344 | None => false, | 244 | None => false, |
345 | Some(entry) => entry.ptr() == ptr, | 245 | Some(d_ptr) => d_ptr == ptr, |
346 | }) | 246 | }) |
347 | .map(|name_ref| ReferenceDescriptor { | 247 | .map(|name_ref| ReferenceDescriptor { |
348 | name: name_ref.text().to_string(), | 248 | name: name_ref.text().to_string(), |
@@ -351,19 +251,14 @@ impl SourceAnalyzer { | |||
351 | .collect() | 251 | .collect() |
352 | } | 252 | } |
353 | 253 | ||
354 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | 254 | pub(crate) fn expand( |
355 | pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> { | ||
356 | self.resolver.traits_in_scope(db) | ||
357 | } | ||
358 | |||
359 | pub fn expand( | ||
360 | &self, | 255 | &self, |
361 | db: &impl HirDatabase, | 256 | db: &impl HirDatabase, |
362 | macro_call: InFile<&ast::MacroCall>, | 257 | macro_call: InFile<&ast::MacroCall>, |
363 | ) -> Option<Expansion> { | 258 | ) -> Option<HirFileId> { |
364 | let macro_call_id = | 259 | let macro_call_id = |
365 | macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?; | 260 | macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?; |
366 | Some(Expansion { macro_call_id }) | 261 | Some(macro_call_id.as_file()) |
367 | } | 262 | } |
368 | } | 263 | } |
369 | 264 | ||
@@ -409,6 +304,47 @@ fn scope_for_offset( | |||
409 | }) | 304 | }) |
410 | } | 305 | } |
411 | 306 | ||
307 | pub(crate) fn resolve_hir_path( | ||
308 | db: &impl HirDatabase, | ||
309 | resolver: &Resolver, | ||
310 | path: &crate::Path, | ||
311 | ) -> Option<PathResolution> { | ||
312 | let types = resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | ||
313 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
314 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
315 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()), | ||
316 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
317 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
318 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
319 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
320 | }); | ||
321 | let body_owner = resolver.body_owner(); | ||
322 | let values = resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
323 | let res = match val { | ||
324 | ValueNs::LocalBinding(pat_id) => { | ||
325 | let var = Local { parent: body_owner?.into(), pat_id }; | ||
326 | PathResolution::Local(var) | ||
327 | } | ||
328 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
329 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
330 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
331 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
332 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
333 | }; | ||
334 | Some(res) | ||
335 | }); | ||
336 | |||
337 | let items = resolver | ||
338 | .resolve_module_path_in_items(db, path.mod_path()) | ||
339 | .take_types() | ||
340 | .map(|it| PathResolution::Def(it.into())); | ||
341 | types.or(values).or(items).or_else(|| { | ||
342 | resolver | ||
343 | .resolve_path_as_macro(db, path.mod_path()) | ||
344 | .map(|def| PathResolution::Macro(def.into())) | ||
345 | }) | ||
346 | } | ||
347 | |||
412 | // XXX: during completion, cursor might be outside of any particular | 348 | // XXX: during completion, cursor might be outside of any particular |
413 | // expression. Try to figure out the correct scope... | 349 | // expression. Try to figure out the correct scope... |
414 | fn adjust( | 350 | fn adjust( |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index f3150f578..4353e25ac 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -5,112 +5,89 @@ use hir_def::{ | |||
5 | child_by_source::ChildBySource, | 5 | child_by_source::ChildBySource, |
6 | dyn_map::DynMap, | 6 | dyn_map::DynMap, |
7 | keys::{self, Key}, | 7 | keys::{self, Key}, |
8 | resolver::{HasResolver, Resolver}, | ||
9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, | 8 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, |
10 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, | 9 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, |
11 | }; | 10 | }; |
12 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; | 11 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; |
12 | use ra_db::FileId; | ||
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | ast::{self, NameOwner}, | 15 | ast::{self, NameOwner}, |
16 | match_ast, AstNode, SyntaxNode, TextUnit, | 16 | match_ast, AstNode, SyntaxNode, |
17 | }; | 17 | }; |
18 | use rustc_hash::FxHashMap; | 18 | use rustc_hash::FxHashMap; |
19 | 19 | ||
20 | use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam}; | 20 | use crate::{db::HirDatabase, Module}; |
21 | use ra_db::FileId; | ||
22 | 21 | ||
23 | pub struct SourceBinder<'a, DB> { | 22 | pub(crate) struct SourceBinder { |
24 | pub db: &'a DB, | ||
25 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, | 23 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, |
26 | } | 24 | } |
27 | 25 | ||
28 | impl<DB: HirDatabase> SourceBinder<'_, DB> { | 26 | impl SourceBinder { |
29 | pub fn new(db: &DB) -> SourceBinder<DB> { | 27 | pub(crate) fn new() -> SourceBinder { |
30 | SourceBinder { db, child_by_source_cache: FxHashMap::default() } | 28 | SourceBinder { child_by_source_cache: FxHashMap::default() } |
31 | } | ||
32 | |||
33 | pub fn analyze( | ||
34 | &mut self, | ||
35 | src: InFile<&SyntaxNode>, | ||
36 | offset: Option<TextUnit>, | ||
37 | ) -> SourceAnalyzer { | ||
38 | let _p = profile("SourceBinder::analyzer"); | ||
39 | let container = match self.find_container(src) { | ||
40 | Some(it) => it, | ||
41 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
42 | }; | ||
43 | |||
44 | let resolver = match container { | ||
45 | ChildContainer::DefWithBodyId(def) => { | ||
46 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | ||
47 | } | ||
48 | ChildContainer::TraitId(it) => it.resolver(self.db), | ||
49 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
50 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
51 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
52 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
53 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | ||
54 | }; | ||
55 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
56 | } | ||
57 | |||
58 | pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> { | ||
59 | T::to_def(self, src) | ||
60 | } | 29 | } |
61 | 30 | ||
62 | pub fn to_module_def(&mut self, file: FileId) -> Option<Module> { | 31 | pub(crate) fn to_module_def(&mut self, db: &impl HirDatabase, file: FileId) -> Option<Module> { |
63 | let _p = profile("SourceBinder::to_module_def"); | 32 | let _p = profile("SourceBinder::to_module_def"); |
64 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { | 33 | let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| { |
65 | let crate_def_map = self.db.crate_def_map(crate_id); | 34 | let crate_def_map = db.crate_def_map(crate_id); |
66 | let local_id = crate_def_map.modules_for_file(file).next()?; | 35 | let local_id = crate_def_map.modules_for_file(file).next()?; |
67 | Some((crate_id, local_id)) | 36 | Some((crate_id, local_id)) |
68 | })?; | 37 | })?; |
69 | Some(Module { id: ModuleId { krate, local_id } }) | 38 | Some(Module { id: ModuleId { krate, local_id } }) |
70 | } | 39 | } |
71 | 40 | ||
72 | fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> { | 41 | pub(crate) fn to_id<T: ToId>( |
73 | T::to_id(self, src) | 42 | &mut self, |
43 | db: &impl HirDatabase, | ||
44 | src: InFile<T>, | ||
45 | ) -> Option<T::ID> { | ||
46 | T::to_id(db, self, src) | ||
74 | } | 47 | } |
75 | 48 | ||
76 | fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { | 49 | pub(crate) fn find_container( |
77 | for container in src.cloned().ancestors_with_macros(self.db).skip(1) { | 50 | &mut self, |
51 | db: &impl HirDatabase, | ||
52 | src: InFile<&SyntaxNode>, | ||
53 | ) -> Option<ChildContainer> { | ||
54 | for container in src.cloned().ancestors_with_macros(db).skip(1) { | ||
78 | let res: ChildContainer = match_ast! { | 55 | let res: ChildContainer = match_ast! { |
79 | match (container.value) { | 56 | match (container.value) { |
80 | ast::TraitDef(it) => { | 57 | ast::TraitDef(it) => { |
81 | let def: TraitId = self.to_id(container.with_value(it))?; | 58 | let def: TraitId = self.to_id(db, container.with_value(it))?; |
82 | def.into() | 59 | def.into() |
83 | }, | 60 | }, |
84 | ast::ImplBlock(it) => { | 61 | ast::ImplBlock(it) => { |
85 | let def: ImplId = self.to_id(container.with_value(it))?; | 62 | let def: ImplId = self.to_id(db, container.with_value(it))?; |
86 | def.into() | 63 | def.into() |
87 | }, | 64 | }, |
88 | ast::FnDef(it) => { | 65 | ast::FnDef(it) => { |
89 | let def: FunctionId = self.to_id(container.with_value(it))?; | 66 | let def: FunctionId = self.to_id(db, container.with_value(it))?; |
90 | DefWithBodyId::from(def).into() | 67 | DefWithBodyId::from(def).into() |
91 | }, | 68 | }, |
92 | ast::StaticDef(it) => { | 69 | ast::StaticDef(it) => { |
93 | let def: StaticId = self.to_id(container.with_value(it))?; | 70 | let def: StaticId = self.to_id(db, container.with_value(it))?; |
94 | DefWithBodyId::from(def).into() | 71 | DefWithBodyId::from(def).into() |
95 | }, | 72 | }, |
96 | ast::ConstDef(it) => { | 73 | ast::ConstDef(it) => { |
97 | let def: ConstId = self.to_id(container.with_value(it))?; | 74 | let def: ConstId = self.to_id(db, container.with_value(it))?; |
98 | DefWithBodyId::from(def).into() | 75 | DefWithBodyId::from(def).into() |
99 | }, | 76 | }, |
100 | ast::EnumDef(it) => { | 77 | ast::EnumDef(it) => { |
101 | let def: EnumId = self.to_id(container.with_value(it))?; | 78 | let def: EnumId = self.to_id(db, container.with_value(it))?; |
102 | def.into() | 79 | def.into() |
103 | }, | 80 | }, |
104 | ast::StructDef(it) => { | 81 | ast::StructDef(it) => { |
105 | let def: StructId = self.to_id(container.with_value(it))?; | 82 | let def: StructId = self.to_id(db, container.with_value(it))?; |
106 | VariantId::from(def).into() | 83 | VariantId::from(def).into() |
107 | }, | 84 | }, |
108 | ast::UnionDef(it) => { | 85 | ast::UnionDef(it) => { |
109 | let def: UnionId = self.to_id(container.with_value(it))?; | 86 | let def: UnionId = self.to_id(db, container.with_value(it))?; |
110 | VariantId::from(def).into() | 87 | VariantId::from(def).into() |
111 | }, | 88 | }, |
112 | ast::Module(it) => { | 89 | ast::Module(it) => { |
113 | let def: ModuleId = self.to_id(container.with_value(it))?; | 90 | let def: ModuleId = self.to_id(db, container.with_value(it))?; |
114 | def.into() | 91 | def.into() |
115 | }, | 92 | }, |
116 | _ => { continue }, | 93 | _ => { continue }, |
@@ -119,12 +96,11 @@ impl<DB: HirDatabase> SourceBinder<'_, DB> { | |||
119 | return Some(res); | 96 | return Some(res); |
120 | } | 97 | } |
121 | 98 | ||
122 | let c = self.to_module_def(src.file_id.original_file(self.db))?; | 99 | let c = self.to_module_def(db, src.file_id.original_file(db))?; |
123 | Some(c.id.into()) | 100 | Some(c.id.into()) |
124 | } | 101 | } |
125 | 102 | ||
126 | fn child_by_source(&mut self, container: ChildContainer) -> &DynMap { | 103 | fn child_by_source(&mut self, db: &impl HirDatabase, container: ChildContainer) -> &DynMap { |
127 | let db = self.db; | ||
128 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { | 104 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { |
129 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | 105 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
130 | ChildContainer::ModuleId(it) => it.child_by_source(db), | 106 | ChildContainer::ModuleId(it) => it.child_by_source(db), |
@@ -137,49 +113,17 @@ impl<DB: HirDatabase> SourceBinder<'_, DB> { | |||
137 | } | 113 | } |
138 | } | 114 | } |
139 | 115 | ||
140 | pub trait ToId: Sized { | 116 | pub(crate) trait ToId: Sized { |
141 | type ID: Sized + Copy + 'static; | 117 | type ID: Sized + Copy + 'static; |
142 | fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | 118 | fn to_id<DB: HirDatabase>( |
143 | -> Option<Self::ID>; | 119 | db: &DB, |
144 | } | 120 | sb: &mut SourceBinder, |
145 | |||
146 | pub trait ToDef: Sized + AstNode + 'static { | ||
147 | type Def; | ||
148 | fn to_def<DB: HirDatabase>( | ||
149 | sb: &mut SourceBinder<'_, DB>, | ||
150 | src: InFile<Self>, | 121 | src: InFile<Self>, |
151 | ) -> Option<Self::Def>; | 122 | ) -> Option<Self::ID>; |
152 | } | ||
153 | |||
154 | macro_rules! to_def_impls { | ||
155 | ($(($def:path, $ast:path)),* ,) => {$( | ||
156 | impl ToDef for $ast { | ||
157 | type Def = $def; | ||
158 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | ||
159 | -> Option<Self::Def> | ||
160 | { sb.to_id(src).map(Into::into) } | ||
161 | } | ||
162 | )*} | ||
163 | } | 123 | } |
164 | 124 | ||
165 | to_def_impls![ | ||
166 | (crate::Module, ast::Module), | ||
167 | (crate::Struct, ast::StructDef), | ||
168 | (crate::Enum, ast::EnumDef), | ||
169 | (crate::Union, ast::UnionDef), | ||
170 | (crate::Trait, ast::TraitDef), | ||
171 | (crate::ImplBlock, ast::ImplBlock), | ||
172 | (crate::TypeAlias, ast::TypeAliasDef), | ||
173 | (crate::Const, ast::ConstDef), | ||
174 | (crate::Static, ast::StaticDef), | ||
175 | (crate::Function, ast::FnDef), | ||
176 | (crate::StructField, ast::RecordFieldDef), | ||
177 | (crate::EnumVariant, ast::EnumVariant), | ||
178 | (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros | ||
179 | ]; | ||
180 | |||
181 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 125 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] |
182 | enum ChildContainer { | 126 | pub(crate) enum ChildContainer { |
183 | DefWithBodyId(DefWithBodyId), | 127 | DefWithBodyId(DefWithBodyId), |
184 | ModuleId(ModuleId), | 128 | ModuleId(ModuleId), |
185 | TraitId(TraitId), | 129 | TraitId(TraitId), |
@@ -201,7 +145,7 @@ impl_froms! { | |||
201 | GenericDefId | 145 | GenericDefId |
202 | } | 146 | } |
203 | 147 | ||
204 | pub trait ToIdByKey: Sized + AstNode + 'static { | 148 | pub(crate) trait ToIdByKey: Sized + AstNode + 'static { |
205 | type ID: Sized + Copy + 'static; | 149 | type ID: Sized + Copy + 'static; |
206 | const KEY: Key<Self, Self::ID>; | 150 | const KEY: Key<Self, Self::ID>; |
207 | } | 151 | } |
@@ -209,11 +153,11 @@ pub trait ToIdByKey: Sized + AstNode + 'static { | |||
209 | impl<T: ToIdByKey> ToId for T { | 153 | impl<T: ToIdByKey> ToId for T { |
210 | type ID = <T as ToIdByKey>::ID; | 154 | type ID = <T as ToIdByKey>::ID; |
211 | fn to_id<DB: HirDatabase>( | 155 | fn to_id<DB: HirDatabase>( |
212 | sb: &mut SourceBinder<'_, DB>, | 156 | db: &DB, |
157 | sb: &mut SourceBinder, | ||
213 | src: InFile<Self>, | 158 | src: InFile<Self>, |
214 | ) -> Option<Self::ID> { | 159 | ) -> Option<Self::ID> { |
215 | let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?; | 160 | let container = sb.find_container(db, src.as_ref().map(|it| it.syntax()))?; |
216 | let db = sb.db; | ||
217 | let dyn_map = | 161 | let dyn_map = |
218 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { | 162 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { |
219 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | 163 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
@@ -255,68 +199,44 @@ to_id_key_impls![ | |||
255 | impl ToId for ast::MacroCall { | 199 | impl ToId for ast::MacroCall { |
256 | type ID = MacroDefId; | 200 | type ID = MacroDefId; |
257 | fn to_id<DB: HirDatabase>( | 201 | fn to_id<DB: HirDatabase>( |
258 | sb: &mut SourceBinder<'_, DB>, | 202 | db: &DB, |
203 | sb: &mut SourceBinder, | ||
259 | src: InFile<Self>, | 204 | src: InFile<Self>, |
260 | ) -> Option<Self::ID> { | 205 | ) -> Option<Self::ID> { |
261 | let kind = MacroDefKind::Declarative; | 206 | let kind = MacroDefKind::Declarative; |
262 | 207 | ||
263 | let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate; | 208 | let krate = sb.to_module_def(db, src.file_id.original_file(db))?.id.krate; |
264 | 209 | ||
265 | let ast_id = | 210 | let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value))); |
266 | Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
267 | 211 | ||
268 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) | 212 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) |
269 | } | 213 | } |
270 | } | 214 | } |
271 | 215 | ||
272 | impl ToDef for ast::BindPat { | 216 | impl ToId for ast::TypeParam { |
273 | type Def = Local; | 217 | type ID = TypeParamId; |
274 | |||
275 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> { | ||
276 | let file_id = src.file_id; | ||
277 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { | ||
278 | let res = match_ast! { | ||
279 | match it { | ||
280 | ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
281 | ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
282 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
283 | _ => return None, | ||
284 | } | ||
285 | }; | ||
286 | Some(res) | ||
287 | })?; | ||
288 | let (_body, source_map) = sb.db.body_with_source_map(parent); | ||
289 | let src = src.map(ast::Pat::from); | ||
290 | let pat_id = source_map.node_pat(src.as_ref())?; | ||
291 | Some(Local { parent: parent.into(), pat_id }) | ||
292 | } | ||
293 | } | ||
294 | |||
295 | impl ToDef for ast::TypeParam { | ||
296 | type Def = TypeParam; | ||
297 | 218 | ||
298 | fn to_def<DB: HirDatabase>( | 219 | fn to_id<DB: HirDatabase>( |
299 | sb: &mut SourceBinder<'_, DB>, | 220 | db: &DB, |
300 | src: InFile<ast::TypeParam>, | 221 | sb: &mut SourceBinder, |
301 | ) -> Option<TypeParam> { | 222 | src: InFile<Self>, |
302 | let mut sb = SourceBinder::new(sb.db); | 223 | ) -> Option<Self::ID> { |
303 | let file_id = src.file_id; | 224 | let file_id = src.file_id; |
304 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { | 225 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { |
305 | let res = match_ast! { | 226 | let res = match_ast! { |
306 | match it { | 227 | match it { |
307 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 228 | ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
308 | ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 229 | ast::StructDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
309 | ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 230 | ast::EnumDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
310 | ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 231 | ast::TraitDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
311 | ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 232 | ast::TypeAliasDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
312 | ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() }, | 233 | ast::ImplBlock(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, |
313 | _ => return None, | 234 | _ => return None, |
314 | } | 235 | } |
315 | }; | 236 | }; |
316 | Some(res) | 237 | Some(res) |
317 | })?; | 238 | })?; |
318 | let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?; | 239 | sb.child_by_source(db, parent.into())[keys::TYPE_PARAM].get(&src).copied() |
319 | Some(TypeParam { id }) | ||
320 | } | 240 | } |
321 | } | 241 | } |
322 | 242 | ||
@@ -324,7 +244,8 @@ impl ToId for ast::Module { | |||
324 | type ID = ModuleId; | 244 | type ID = ModuleId; |
325 | 245 | ||
326 | fn to_id<DB: HirDatabase>( | 246 | fn to_id<DB: HirDatabase>( |
327 | sb: &mut SourceBinder<'_, DB>, | 247 | db: &DB, |
248 | sb: &mut SourceBinder, | ||
328 | src: InFile<ast::Module>, | 249 | src: InFile<ast::Module>, |
329 | ) -> Option<ModuleId> { | 250 | ) -> Option<ModuleId> { |
330 | { | 251 | { |
@@ -333,7 +254,7 @@ impl ToId for ast::Module { | |||
333 | .as_ref() | 254 | .as_ref() |
334 | .map(|it| it.syntax()) | 255 | .map(|it| it.syntax()) |
335 | .cloned() | 256 | .cloned() |
336 | .ancestors_with_macros(sb.db) | 257 | .ancestors_with_macros(db) |
337 | .skip(1) | 258 | .skip(1) |
338 | .find_map(|it| { | 259 | .find_map(|it| { |
339 | let m = ast::Module::cast(it.value.clone())?; | 260 | let m = ast::Module::cast(it.value.clone())?; |
@@ -341,15 +262,15 @@ impl ToId for ast::Module { | |||
341 | }); | 262 | }); |
342 | 263 | ||
343 | let parent_module = match parent_declaration { | 264 | let parent_module = match parent_declaration { |
344 | Some(parent_declaration) => sb.to_id(parent_declaration)?, | 265 | Some(parent_declaration) => sb.to_id(db, parent_declaration)?, |
345 | None => { | 266 | None => { |
346 | let file_id = src.file_id.original_file(sb.db); | 267 | let file_id = src.file_id.original_file(db); |
347 | sb.to_module_def(file_id)?.id | 268 | sb.to_module_def(db, file_id)?.id |
348 | } | 269 | } |
349 | }; | 270 | }; |
350 | 271 | ||
351 | let child_name = src.value.name()?.as_name(); | 272 | let child_name = src.value.name()?.as_name(); |
352 | let def_map = sb.db.crate_def_map(parent_module.krate); | 273 | let def_map = db.crate_def_map(parent_module.krate); |
353 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; | 274 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; |
354 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) | 275 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) |
355 | } | 276 | } |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 5053d0688..fa25cc4fb 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -14,7 +14,6 @@ rustc-hash = "1.1.0" | |||
14 | either = "1.5.3" | 14 | either = "1.5.3" |
15 | anymap = "0.12.1" | 15 | anymap = "0.12.1" |
16 | drop_bomb = "0.1.4" | 16 | drop_bomb = "0.1.4" |
17 | itertools = "0.8.2" | ||
18 | 17 | ||
19 | ra_arena = { path = "../ra_arena" } | 18 | ra_arena = { path = "../ra_arena" } |
20 | ra_db = { path = "../ra_db" } | 19 | ra_db = { path = "../ra_db" } |
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 49cafc539..99e2fe1bf 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -21,9 +21,9 @@ ra_prof = { path = "../ra_prof" } | |||
21 | ra_syntax = { path = "../ra_syntax" } | 21 | ra_syntax = { path = "../ra_syntax" } |
22 | test_utils = { path = "../test_utils" } | 22 | test_utils = { path = "../test_utils" } |
23 | 23 | ||
24 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "af48f302a1f571b3ca418f7c5aa639a144a34f75" } | 24 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2939913fb7bb94ac2a6721087dc086be11410702" } |
25 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "af48f302a1f571b3ca418f7c5aa639a144a34f75" } | 25 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2939913fb7bb94ac2a6721087dc086be11410702" } |
26 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "af48f302a1f571b3ca418f7c5aa639a144a34f75" } | 26 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2939913fb7bb94ac2a6721087dc086be11410702" } |
27 | 27 | ||
28 | lalrpop-intern = "0.15.1" | 28 | lalrpop-intern = "0.15.1" |
29 | 29 | ||
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 2317fcac3..17aef9490 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs | |||
@@ -14,7 +14,7 @@ use crate::db::HirDatabase; | |||
14 | 14 | ||
15 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; | 15 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; |
16 | 16 | ||
17 | use self::chalk::{from_chalk, ToChalk, TypeFamily}; | 17 | use self::chalk::{from_chalk, Interner, ToChalk}; |
18 | 18 | ||
19 | pub(crate) mod chalk; | 19 | pub(crate) mod chalk; |
20 | mod builtin; | 20 | mod builtin; |
@@ -22,7 +22,7 @@ mod builtin; | |||
22 | #[derive(Debug, Clone)] | 22 | #[derive(Debug, Clone)] |
23 | pub struct TraitSolver { | 23 | pub struct TraitSolver { |
24 | krate: CrateId, | 24 | krate: CrateId, |
25 | inner: Arc<Mutex<chalk_solve::Solver<TypeFamily>>>, | 25 | inner: Arc<Mutex<chalk_solve::Solver<Interner>>>, |
26 | } | 26 | } |
27 | 27 | ||
28 | /// We need eq for salsa | 28 | /// We need eq for salsa |
@@ -38,8 +38,8 @@ impl TraitSolver { | |||
38 | fn solve( | 38 | fn solve( |
39 | &self, | 39 | &self, |
40 | db: &impl HirDatabase, | 40 | db: &impl HirDatabase, |
41 | goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>, | 41 | goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>, |
42 | ) -> Option<chalk_solve::Solution<TypeFamily>> { | 42 | ) -> Option<chalk_solve::Solution<Interner>> { |
43 | let context = ChalkContext { db, krate: self.krate }; | 43 | let context = ChalkContext { db, krate: self.krate }; |
44 | log::debug!("solve goal: {:?}", goal); | 44 | log::debug!("solve goal: {:?}", goal); |
45 | let mut solver = match self.inner.lock() { | 45 | let mut solver = match self.inner.lock() { |
@@ -110,7 +110,7 @@ pub(crate) fn trait_solver_query( | |||
110 | TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) } | 110 | TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) } |
111 | } | 111 | } |
112 | 112 | ||
113 | fn create_chalk_solver() -> chalk_solve::Solver<TypeFamily> { | 113 | fn create_chalk_solver() -> chalk_solve::Solver<Interner> { |
114 | let solver_choice = | 114 | let solver_choice = |
115 | chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None }; | 115 | chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None }; |
116 | solver_choice.into_solver() | 116 | solver_choice.into_solver() |
@@ -242,9 +242,9 @@ pub(crate) fn trait_solve_query( | |||
242 | 242 | ||
243 | fn solution_from_chalk( | 243 | fn solution_from_chalk( |
244 | db: &impl HirDatabase, | 244 | db: &impl HirDatabase, |
245 | solution: chalk_solve::Solution<TypeFamily>, | 245 | solution: chalk_solve::Solution<Interner>, |
246 | ) -> Solution { | 246 | ) -> Solution { |
247 | let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<TypeFamily>>| { | 247 | let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| { |
248 | let value = subst | 248 | let value = subst |
249 | .value | 249 | .value |
250 | .into_iter() | 250 | .into_iter() |
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index e1e430aeb..5b6c1a62e 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -3,7 +3,7 @@ use std::{fmt, sync::Arc}; | |||
3 | 3 | ||
4 | use log::debug; | 4 | use log::debug; |
5 | 5 | ||
6 | use chalk_ir::{cast::Cast, GoalData, Parameter, PlaceholderIndex, TypeName, UniverseIndex}; | 6 | use chalk_ir::{cast::Cast, Goal, GoalData, Parameter, PlaceholderIndex, TypeName, UniverseIndex}; |
7 | 7 | ||
8 | use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; | 8 | use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; |
9 | use ra_db::{ | 9 | use ra_db::{ |
@@ -18,13 +18,14 @@ use crate::{ | |||
18 | }; | 18 | }; |
19 | 19 | ||
20 | #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] | 20 | #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] |
21 | pub struct TypeFamily {} | 21 | pub struct Interner {} |
22 | 22 | ||
23 | impl chalk_ir::family::TypeFamily for TypeFamily { | 23 | impl chalk_ir::interner::Interner for Interner { |
24 | type InternedType = Box<chalk_ir::TyData<Self>>; | 24 | type InternedType = Box<chalk_ir::TyData<Self>>; |
25 | type InternedLifetime = chalk_ir::LifetimeData<Self>; | 25 | type InternedLifetime = chalk_ir::LifetimeData<Self>; |
26 | type InternedParameter = chalk_ir::ParameterData<Self>; | 26 | type InternedParameter = chalk_ir::ParameterData<Self>; |
27 | type InternedGoal = Arc<GoalData<Self>>; | 27 | type InternedGoal = Arc<GoalData<Self>>; |
28 | type InternedGoals = Vec<Goal<Self>>; | ||
28 | type InternedSubstitution = Vec<Parameter<Self>>; | 29 | type InternedSubstitution = Vec<Parameter<Self>>; |
29 | type DefId = InternId; | 30 | type DefId = InternId; |
30 | 31 | ||
@@ -85,10 +86,18 @@ impl chalk_ir::family::TypeFamily for TypeFamily { | |||
85 | Arc::new(goal) | 86 | Arc::new(goal) |
86 | } | 87 | } |
87 | 88 | ||
89 | fn intern_goals(data: impl IntoIterator<Item = Goal<Self>>) -> Self::InternedGoals { | ||
90 | data.into_iter().collect() | ||
91 | } | ||
92 | |||
88 | fn goal_data(goal: &Arc<GoalData<Self>>) -> &GoalData<Self> { | 93 | fn goal_data(goal: &Arc<GoalData<Self>>) -> &GoalData<Self> { |
89 | goal | 94 | goal |
90 | } | 95 | } |
91 | 96 | ||
97 | fn goals_data(goals: &Vec<Goal<Interner>>) -> &[Goal<Interner>] { | ||
98 | goals | ||
99 | } | ||
100 | |||
92 | fn intern_substitution<E>( | 101 | fn intern_substitution<E>( |
93 | data: impl IntoIterator<Item = Result<Parameter<Self>, E>>, | 102 | data: impl IntoIterator<Item = Result<Parameter<Self>, E>>, |
94 | ) -> Result<Vec<Parameter<Self>>, E> { | 103 | ) -> Result<Vec<Parameter<Self>>, E> { |
@@ -100,20 +109,20 @@ impl chalk_ir::family::TypeFamily for TypeFamily { | |||
100 | } | 109 | } |
101 | } | 110 | } |
102 | 111 | ||
103 | impl chalk_ir::family::HasTypeFamily for TypeFamily { | 112 | impl chalk_ir::interner::HasInterner for Interner { |
104 | type TypeFamily = Self; | 113 | type Interner = Self; |
105 | } | 114 | } |
106 | 115 | ||
107 | pub type AssocTypeId = chalk_ir::AssocTypeId<TypeFamily>; | 116 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; |
108 | pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<TypeFamily>; | 117 | pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum<Interner>; |
109 | pub type TraitId = chalk_ir::TraitId<TypeFamily>; | 118 | pub type TraitId = chalk_ir::TraitId<Interner>; |
110 | pub type TraitDatum = chalk_rust_ir::TraitDatum<TypeFamily>; | 119 | pub type TraitDatum = chalk_rust_ir::TraitDatum<Interner>; |
111 | pub type StructId = chalk_ir::StructId<TypeFamily>; | 120 | pub type StructId = chalk_ir::StructId<Interner>; |
112 | pub type StructDatum = chalk_rust_ir::StructDatum<TypeFamily>; | 121 | pub type StructDatum = chalk_rust_ir::StructDatum<Interner>; |
113 | pub type ImplId = chalk_ir::ImplId<TypeFamily>; | 122 | pub type ImplId = chalk_ir::ImplId<Interner>; |
114 | pub type ImplDatum = chalk_rust_ir::ImplDatum<TypeFamily>; | 123 | pub type ImplDatum = chalk_rust_ir::ImplDatum<Interner>; |
115 | pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId; | 124 | pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId; |
116 | pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<TypeFamily>; | 125 | pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue<Interner>; |
117 | 126 | ||
118 | pub(super) trait ToChalk { | 127 | pub(super) trait ToChalk { |
119 | type Chalk; | 128 | type Chalk; |
@@ -129,8 +138,8 @@ where | |||
129 | } | 138 | } |
130 | 139 | ||
131 | impl ToChalk for Ty { | 140 | impl ToChalk for Ty { |
132 | type Chalk = chalk_ir::Ty<TypeFamily>; | 141 | type Chalk = chalk_ir::Ty<Interner>; |
133 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty<TypeFamily> { | 142 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty<Interner> { |
134 | match self { | 143 | match self { |
135 | Ty::Apply(apply_ty) => { | 144 | Ty::Apply(apply_ty) => { |
136 | let name = apply_ty.ctor.to_chalk(db); | 145 | let name = apply_ty.ctor.to_chalk(db); |
@@ -148,7 +157,7 @@ impl ToChalk for Ty { | |||
148 | ui: UniverseIndex::ROOT, | 157 | ui: UniverseIndex::ROOT, |
149 | idx: interned_id.as_intern_id().as_usize(), | 158 | idx: interned_id.as_intern_id().as_usize(), |
150 | } | 159 | } |
151 | .to_ty::<TypeFamily>() | 160 | .to_ty::<Interner>() |
152 | } | 161 | } |
153 | Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx as usize).intern(), | 162 | Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx as usize).intern(), |
154 | Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), | 163 | Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), |
@@ -169,7 +178,7 @@ impl ToChalk for Ty { | |||
169 | } | 178 | } |
170 | } | 179 | } |
171 | } | 180 | } |
172 | fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty<TypeFamily>) -> Self { | 181 | fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self { |
173 | match chalk.data().clone() { | 182 | match chalk.data().clone() { |
174 | chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name { | 183 | chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name { |
175 | TypeName::Error => Ty::Unknown, | 184 | TypeName::Error => Ty::Unknown, |
@@ -205,13 +214,13 @@ impl ToChalk for Ty { | |||
205 | } | 214 | } |
206 | 215 | ||
207 | impl ToChalk for Substs { | 216 | impl ToChalk for Substs { |
208 | type Chalk = chalk_ir::Substitution<TypeFamily>; | 217 | type Chalk = chalk_ir::Substitution<Interner>; |
209 | 218 | ||
210 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Substitution<TypeFamily> { | 219 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Substitution<Interner> { |
211 | chalk_ir::Substitution::from(self.iter().map(|ty| ty.clone().to_chalk(db))) | 220 | chalk_ir::Substitution::from(self.iter().map(|ty| ty.clone().to_chalk(db))) |
212 | } | 221 | } |
213 | 222 | ||
214 | fn from_chalk(db: &impl HirDatabase, parameters: chalk_ir::Substitution<TypeFamily>) -> Substs { | 223 | fn from_chalk(db: &impl HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs { |
215 | let tys = parameters | 224 | let tys = parameters |
216 | .into_iter() | 225 | .into_iter() |
217 | .map(|p| match p.ty() { | 226 | .map(|p| match p.ty() { |
@@ -224,15 +233,15 @@ impl ToChalk for Substs { | |||
224 | } | 233 | } |
225 | 234 | ||
226 | impl ToChalk for TraitRef { | 235 | impl ToChalk for TraitRef { |
227 | type Chalk = chalk_ir::TraitRef<TypeFamily>; | 236 | type Chalk = chalk_ir::TraitRef<Interner>; |
228 | 237 | ||
229 | fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef<TypeFamily> { | 238 | fn to_chalk(self: TraitRef, db: &impl HirDatabase) -> chalk_ir::TraitRef<Interner> { |
230 | let trait_id = self.trait_.to_chalk(db); | 239 | let trait_id = self.trait_.to_chalk(db); |
231 | let substitution = self.substs.to_chalk(db); | 240 | let substitution = self.substs.to_chalk(db); |
232 | chalk_ir::TraitRef { trait_id, substitution } | 241 | chalk_ir::TraitRef { trait_id, substitution } |
233 | } | 242 | } |
234 | 243 | ||
235 | fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef<TypeFamily>) -> Self { | 244 | fn from_chalk(db: &impl HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self { |
236 | let trait_ = from_chalk(db, trait_ref.trait_id); | 245 | let trait_ = from_chalk(db, trait_ref.trait_id); |
237 | let substs = from_chalk(db, trait_ref.substitution); | 246 | let substs = from_chalk(db, trait_ref.substitution); |
238 | TraitRef { trait_, substs } | 247 | TraitRef { trait_, substs } |
@@ -252,9 +261,9 @@ impl ToChalk for hir_def::TraitId { | |||
252 | } | 261 | } |
253 | 262 | ||
254 | impl ToChalk for TypeCtor { | 263 | impl ToChalk for TypeCtor { |
255 | type Chalk = TypeName<TypeFamily>; | 264 | type Chalk = TypeName<Interner>; |
256 | 265 | ||
257 | fn to_chalk(self, db: &impl HirDatabase) -> TypeName<TypeFamily> { | 266 | fn to_chalk(self, db: &impl HirDatabase) -> TypeName<Interner> { |
258 | match self { | 267 | match self { |
259 | TypeCtor::AssociatedType(type_alias) => { | 268 | TypeCtor::AssociatedType(type_alias) => { |
260 | let type_id = type_alias.to_chalk(db); | 269 | let type_id = type_alias.to_chalk(db); |
@@ -268,7 +277,7 @@ impl ToChalk for TypeCtor { | |||
268 | } | 277 | } |
269 | } | 278 | } |
270 | 279 | ||
271 | fn from_chalk(db: &impl HirDatabase, type_name: TypeName<TypeFamily>) -> TypeCtor { | 280 | fn from_chalk(db: &impl HirDatabase, type_name: TypeName<Interner>) -> TypeCtor { |
272 | match type_name { | 281 | match type_name { |
273 | TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), | 282 | TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), |
274 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), | 283 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), |
@@ -317,9 +326,9 @@ impl ToChalk for AssocTyValue { | |||
317 | } | 326 | } |
318 | 327 | ||
319 | impl ToChalk for GenericPredicate { | 328 | impl ToChalk for GenericPredicate { |
320 | type Chalk = chalk_ir::QuantifiedWhereClause<TypeFamily>; | 329 | type Chalk = chalk_ir::QuantifiedWhereClause<Interner>; |
321 | 330 | ||
322 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause<TypeFamily> { | 331 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::QuantifiedWhereClause<Interner> { |
323 | match self { | 332 | match self { |
324 | GenericPredicate::Implemented(trait_ref) => { | 333 | GenericPredicate::Implemented(trait_ref) => { |
325 | make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0) | 334 | make_binders(chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)), 0) |
@@ -337,7 +346,7 @@ impl ToChalk for GenericPredicate { | |||
337 | 346 | ||
338 | fn from_chalk( | 347 | fn from_chalk( |
339 | db: &impl HirDatabase, | 348 | db: &impl HirDatabase, |
340 | where_clause: chalk_ir::QuantifiedWhereClause<TypeFamily>, | 349 | where_clause: chalk_ir::QuantifiedWhereClause<Interner>, |
341 | ) -> GenericPredicate { | 350 | ) -> GenericPredicate { |
342 | match where_clause.value { | 351 | match where_clause.value { |
343 | chalk_ir::WhereClause::Implemented(tr) => { | 352 | chalk_ir::WhereClause::Implemented(tr) => { |
@@ -353,9 +362,9 @@ impl ToChalk for GenericPredicate { | |||
353 | } | 362 | } |
354 | 363 | ||
355 | impl ToChalk for ProjectionTy { | 364 | impl ToChalk for ProjectionTy { |
356 | type Chalk = chalk_ir::AliasTy<TypeFamily>; | 365 | type Chalk = chalk_ir::AliasTy<Interner>; |
357 | 366 | ||
358 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::AliasTy<TypeFamily> { | 367 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::AliasTy<Interner> { |
359 | chalk_ir::AliasTy { | 368 | chalk_ir::AliasTy { |
360 | associated_ty_id: self.associated_ty.to_chalk(db), | 369 | associated_ty_id: self.associated_ty.to_chalk(db), |
361 | substitution: self.parameters.to_chalk(db), | 370 | substitution: self.parameters.to_chalk(db), |
@@ -364,7 +373,7 @@ impl ToChalk for ProjectionTy { | |||
364 | 373 | ||
365 | fn from_chalk( | 374 | fn from_chalk( |
366 | db: &impl HirDatabase, | 375 | db: &impl HirDatabase, |
367 | projection_ty: chalk_ir::AliasTy<TypeFamily>, | 376 | projection_ty: chalk_ir::AliasTy<Interner>, |
368 | ) -> ProjectionTy { | 377 | ) -> ProjectionTy { |
369 | ProjectionTy { | 378 | ProjectionTy { |
370 | associated_ty: from_chalk(db, projection_ty.associated_ty_id), | 379 | associated_ty: from_chalk(db, projection_ty.associated_ty_id), |
@@ -374,28 +383,28 @@ impl ToChalk for ProjectionTy { | |||
374 | } | 383 | } |
375 | 384 | ||
376 | impl ToChalk for super::ProjectionPredicate { | 385 | impl ToChalk for super::ProjectionPredicate { |
377 | type Chalk = chalk_ir::Normalize<TypeFamily>; | 386 | type Chalk = chalk_ir::Normalize<Interner>; |
378 | 387 | ||
379 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize<TypeFamily> { | 388 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Normalize<Interner> { |
380 | chalk_ir::Normalize { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } | 389 | chalk_ir::Normalize { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } |
381 | } | 390 | } |
382 | 391 | ||
383 | fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize<TypeFamily>) -> Self { | 392 | fn from_chalk(_db: &impl HirDatabase, _normalize: chalk_ir::Normalize<Interner>) -> Self { |
384 | unimplemented!() | 393 | unimplemented!() |
385 | } | 394 | } |
386 | } | 395 | } |
387 | 396 | ||
388 | impl ToChalk for Obligation { | 397 | impl ToChalk for Obligation { |
389 | type Chalk = chalk_ir::DomainGoal<TypeFamily>; | 398 | type Chalk = chalk_ir::DomainGoal<Interner>; |
390 | 399 | ||
391 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal<TypeFamily> { | 400 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::DomainGoal<Interner> { |
392 | match self { | 401 | match self { |
393 | Obligation::Trait(tr) => tr.to_chalk(db).cast(), | 402 | Obligation::Trait(tr) => tr.to_chalk(db).cast(), |
394 | Obligation::Projection(pr) => pr.to_chalk(db).cast(), | 403 | Obligation::Projection(pr) => pr.to_chalk(db).cast(), |
395 | } | 404 | } |
396 | } | 405 | } |
397 | 406 | ||
398 | fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal<TypeFamily>) -> Self { | 407 | fn from_chalk(_db: &impl HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self { |
399 | unimplemented!() | 408 | unimplemented!() |
400 | } | 409 | } |
401 | } | 410 | } |
@@ -418,16 +427,16 @@ where | |||
418 | } | 427 | } |
419 | 428 | ||
420 | impl ToChalk for Arc<super::TraitEnvironment> { | 429 | impl ToChalk for Arc<super::TraitEnvironment> { |
421 | type Chalk = chalk_ir::Environment<TypeFamily>; | 430 | type Chalk = chalk_ir::Environment<Interner>; |
422 | 431 | ||
423 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment<TypeFamily> { | 432 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Environment<Interner> { |
424 | let mut clauses = Vec::new(); | 433 | let mut clauses = Vec::new(); |
425 | for pred in &self.predicates { | 434 | for pred in &self.predicates { |
426 | if pred.is_error() { | 435 | if pred.is_error() { |
427 | // for env, we just ignore errors | 436 | // for env, we just ignore errors |
428 | continue; | 437 | continue; |
429 | } | 438 | } |
430 | let program_clause: chalk_ir::ProgramClause<TypeFamily> = | 439 | let program_clause: chalk_ir::ProgramClause<Interner> = |
431 | pred.clone().to_chalk(db).cast(); | 440 | pred.clone().to_chalk(db).cast(); |
432 | clauses.push(program_clause.into_from_env_clause()); | 441 | clauses.push(program_clause.into_from_env_clause()); |
433 | } | 442 | } |
@@ -436,7 +445,7 @@ impl ToChalk for Arc<super::TraitEnvironment> { | |||
436 | 445 | ||
437 | fn from_chalk( | 446 | fn from_chalk( |
438 | _db: &impl HirDatabase, | 447 | _db: &impl HirDatabase, |
439 | _env: chalk_ir::Environment<TypeFamily>, | 448 | _env: chalk_ir::Environment<Interner>, |
440 | ) -> Arc<super::TraitEnvironment> { | 449 | ) -> Arc<super::TraitEnvironment> { |
441 | unimplemented!() | 450 | unimplemented!() |
442 | } | 451 | } |
@@ -444,7 +453,7 @@ impl ToChalk for Arc<super::TraitEnvironment> { | |||
444 | 453 | ||
445 | impl<T: ToChalk> ToChalk for super::InEnvironment<T> | 454 | impl<T: ToChalk> ToChalk for super::InEnvironment<T> |
446 | where | 455 | where |
447 | T::Chalk: chalk_ir::family::HasTypeFamily<TypeFamily = TypeFamily>, | 456 | T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>, |
448 | { | 457 | { |
449 | type Chalk = chalk_ir::InEnvironment<T::Chalk>; | 458 | type Chalk = chalk_ir::InEnvironment<T::Chalk>; |
450 | 459 | ||
@@ -522,7 +531,7 @@ fn convert_where_clauses( | |||
522 | db: &impl HirDatabase, | 531 | db: &impl HirDatabase, |
523 | def: GenericDefId, | 532 | def: GenericDefId, |
524 | substs: &Substs, | 533 | substs: &Substs, |
525 | ) -> Vec<chalk_ir::QuantifiedWhereClause<TypeFamily>> { | 534 | ) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> { |
526 | let generic_predicates = db.generic_predicates(def); | 535 | let generic_predicates = db.generic_predicates(def); |
527 | let mut result = Vec::with_capacity(generic_predicates.len()); | 536 | let mut result = Vec::with_capacity(generic_predicates.len()); |
528 | for pred in generic_predicates.iter() { | 537 | for pred in generic_predicates.iter() { |
@@ -535,7 +544,7 @@ fn convert_where_clauses( | |||
535 | result | 544 | result |
536 | } | 545 | } |
537 | 546 | ||
538 | impl<'a, DB> chalk_solve::RustIrDatabase<TypeFamily> for ChalkContext<'a, DB> | 547 | impl<'a, DB> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a, DB> |
539 | where | 548 | where |
540 | DB: HirDatabase, | 549 | DB: HirDatabase, |
541 | { | 550 | { |
@@ -554,7 +563,7 @@ where | |||
554 | fn impls_for_trait( | 563 | fn impls_for_trait( |
555 | &self, | 564 | &self, |
556 | trait_id: TraitId, | 565 | trait_id: TraitId, |
557 | parameters: &[Parameter<TypeFamily>], | 566 | parameters: &[Parameter<Interner>], |
558 | ) -> Vec<ImplId> { | 567 | ) -> Vec<ImplId> { |
559 | debug!("impls_for_trait {:?}", trait_id); | 568 | debug!("impls_for_trait {:?}", trait_id); |
560 | let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); | 569 | let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); |
@@ -589,14 +598,14 @@ where | |||
589 | fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { | 598 | fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { |
590 | self.db.associated_ty_value(self.krate, id) | 599 | self.db.associated_ty_value(self.krate, id) |
591 | } | 600 | } |
592 | fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<TypeFamily>> { | 601 | fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { |
593 | vec![] | 602 | vec![] |
594 | } | 603 | } |
595 | fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec<ImplId> { | 604 | fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec<ImplId> { |
596 | // We don't do coherence checking (yet) | 605 | // We don't do coherence checking (yet) |
597 | unimplemented!() | 606 | unimplemented!() |
598 | } | 607 | } |
599 | fn as_struct_id(&self, id: &TypeName<TypeFamily>) -> Option<StructId> { | 608 | fn as_struct_id(&self, id: &TypeName<Interner>) -> Option<StructId> { |
600 | match id { | 609 | match id { |
601 | TypeName::Struct(struct_id) => Some(*struct_id), | 610 | TypeName::Struct(struct_id) => Some(*struct_id), |
602 | _ => None, | 611 | _ => None, |
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 3407d2598..7625fc8c8 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -17,11 +17,7 @@ indexmap = "1.3.2" | |||
17 | itertools = "0.8.2" | 17 | itertools = "0.8.2" |
18 | join_to_string = "0.1.3" | 18 | join_to_string = "0.1.3" |
19 | log = "0.4.8" | 19 | log = "0.4.8" |
20 | rayon = "1.3.0" | ||
21 | fst = { version = "0.3.5", default-features = false } | ||
22 | rustc-hash = "1.1.0" | 20 | rustc-hash = "1.1.0" |
23 | unicase = "2.6.0" | ||
24 | superslice = "1.0.0" | ||
25 | rand = { version = "0.7.3", features = ["small_rng"] } | 21 | rand = { version = "0.7.3", features = ["small_rng"] } |
26 | once_cell = "1.3.1" | 22 | once_cell = "1.3.1" |
27 | 23 | ||
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index f984f40ad..b00b6d431 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -2,18 +2,13 @@ | |||
2 | 2 | ||
3 | use indexmap::IndexMap; | 3 | use indexmap::IndexMap; |
4 | 4 | ||
5 | use hir::db::AstDatabase; | 5 | use hir::Semantics; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ast, match_ast, AstNode, TextRange}; |
8 | ast::{self, DocCommentsOwner}, | ||
9 | match_ast, AstNode, TextRange, | ||
10 | }; | ||
11 | 8 | ||
12 | use crate::{ | 9 | use crate::{ |
13 | call_info::FnCallNode, | 10 | call_info::FnCallNode, display::ToNav, goto_definition, references, FilePosition, |
14 | display::{ShortLabel, ToNav}, | 11 | NavigationTarget, RangeInfo, |
15 | expand::descend_into_macros, | ||
16 | goto_definition, references, FilePosition, NavigationTarget, RangeInfo, | ||
17 | }; | 12 | }; |
18 | 13 | ||
19 | #[derive(Debug, Clone)] | 14 | #[derive(Debug, Clone)] |
@@ -43,6 +38,7 @@ pub(crate) fn call_hierarchy( | |||
43 | } | 38 | } |
44 | 39 | ||
45 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { | 40 | pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { |
41 | let sema = Semantics::new(db); | ||
46 | // 1. Find all refs | 42 | // 1. Find all refs |
47 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. | 43 | // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. |
48 | // 3. Add ranges relative to the start of the fndef. | 44 | // 3. Add ranges relative to the start of the fndef. |
@@ -52,24 +48,21 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
52 | 48 | ||
53 | for reference in refs.info.references() { | 49 | for reference in refs.info.references() { |
54 | let file_id = reference.file_range.file_id; | 50 | let file_id = reference.file_range.file_id; |
55 | let file = db.parse_or_expand(file_id.into())?; | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | ||
56 | let token = file.token_at_offset(reference.file_range.range.start()).next()?; | 53 | let token = file.token_at_offset(reference.file_range.range.start()).next()?; |
57 | let token = descend_into_macros(db, file_id, token); | 54 | let token = sema.descend_into_macros(token); |
58 | let syntax = token.value.parent(); | 55 | let syntax = token.parent(); |
59 | 56 | ||
60 | // This target is the containing function | 57 | // This target is the containing function |
61 | if let Some(nav) = syntax.ancestors().find_map(|node| { | 58 | if let Some(nav) = syntax.ancestors().find_map(|node| { |
62 | match_ast! { | 59 | match_ast! { |
63 | match node { | 60 | match node { |
64 | ast::FnDef(it) => { | 61 | ast::FnDef(it) => { |
65 | Some(NavigationTarget::from_named( | 62 | let def = sema.to_def(&it)?; |
66 | db, | 63 | Some(def.to_nav(sema.db)) |
67 | token.with_value(&it), | ||
68 | it.doc_comment_text(), | ||
69 | it.short_label(), | ||
70 | )) | ||
71 | }, | 64 | }, |
72 | _ => { None }, | 65 | _ => None, |
73 | } | 66 | } |
74 | } | 67 | } |
75 | }) { | 68 | }) { |
@@ -82,11 +75,13 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
82 | } | 75 | } |
83 | 76 | ||
84 | pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { | 77 | pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { |
78 | let sema = Semantics::new(db); | ||
85 | let file_id = position.file_id; | 79 | let file_id = position.file_id; |
86 | let file = db.parse_or_expand(file_id.into())?; | 80 | let file = sema.parse(file_id); |
81 | let file = file.syntax(); | ||
87 | let token = file.token_at_offset(position.offset).next()?; | 82 | let token = file.token_at_offset(position.offset).next()?; |
88 | let token = descend_into_macros(db, file_id, token); | 83 | let token = sema.descend_into_macros(token); |
89 | let syntax = token.value.parent(); | 84 | let syntax = token.parent(); |
90 | 85 | ||
91 | let mut calls = CallLocations::default(); | 86 | let mut calls = CallLocations::default(); |
92 | 87 | ||
@@ -95,14 +90,11 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
95 | .filter_map(|node| FnCallNode::with_node_exact(&node)) | 90 | .filter_map(|node| FnCallNode::with_node_exact(&node)) |
96 | .filter_map(|call_node| { | 91 | .filter_map(|call_node| { |
97 | let name_ref = call_node.name_ref()?; | 92 | let name_ref = call_node.name_ref()?; |
98 | let name_ref = token.with_value(name_ref.syntax()); | ||
99 | |||
100 | let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); | ||
101 | 93 | ||
102 | if let Some(func_target) = match &call_node { | 94 | if let Some(func_target) = match &call_node { |
103 | FnCallNode::CallExpr(expr) => { | 95 | FnCallNode::CallExpr(expr) => { |
104 | //FIXME: Type::as_callable is broken | 96 | //FIXME: Type::as_callable is broken |
105 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 97 | let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; |
106 | match callable_def { | 98 | match callable_def { |
107 | hir::CallableDef::FunctionId(it) => { | 99 | hir::CallableDef::FunctionId(it) => { |
108 | let fn_def: hir::Function = it.into(); | 100 | let fn_def: hir::Function = it.into(); |
@@ -113,15 +105,15 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
113 | } | 105 | } |
114 | } | 106 | } |
115 | FnCallNode::MethodCallExpr(expr) => { | 107 | FnCallNode::MethodCallExpr(expr) => { |
116 | let function = analyzer.resolve_method_call(&expr)?; | 108 | let function = sema.resolve_method_call(&expr)?; |
117 | Some(function.to_nav(db)) | 109 | Some(function.to_nav(db)) |
118 | } | 110 | } |
119 | FnCallNode::MacroCallExpr(expr) => { | 111 | FnCallNode::MacroCallExpr(macro_call) => { |
120 | let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?; | 112 | let macro_def = sema.resolve_macro_call(¯o_call)?; |
121 | Some(macro_def.to_nav(db)) | 113 | Some(macro_def.to_nav(db)) |
122 | } | 114 | } |
123 | } { | 115 | } { |
124 | Some((func_target, name_ref.value.text_range())) | 116 | Some((func_target, name_ref.syntax().text_range())) |
125 | } else { | 117 | } else { |
126 | None | 118 | None |
127 | } | 119 | } |
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 7c6322cb4..9a1fc0d35 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use hir::db::AstDatabase; | 2 | use hir::Semantics; |
3 | use ra_ide_db::RootDatabase; | 3 | use ra_ide_db::RootDatabase; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, ArgListOwner}, | 5 | ast::{self, ArgListOwner}, |
@@ -7,24 +7,23 @@ use ra_syntax::{ | |||
7 | }; | 7 | }; |
8 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
9 | 9 | ||
10 | use crate::{expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature}; | 10 | use crate::{CallInfo, FilePosition, FunctionSignature}; |
11 | 11 | ||
12 | /// Computes parameter information for the given call expression. | 12 | /// Computes parameter information for the given call expression. |
13 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { | 13 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { |
14 | let file = db.parse_or_expand(position.file_id.into())?; | 14 | let sema = Semantics::new(db); |
15 | let file = sema.parse(position.file_id); | ||
16 | let file = file.syntax(); | ||
15 | let token = file.token_at_offset(position.offset).next()?; | 17 | let token = file.token_at_offset(position.offset).next()?; |
16 | let token = descend_into_macros(db, position.file_id, token); | 18 | let token = sema.descend_into_macros(token); |
17 | 19 | ||
18 | // Find the calling expression and it's NameRef | 20 | // Find the calling expression and it's NameRef |
19 | let calling_node = FnCallNode::with_node(&token.value.parent())?; | 21 | let calling_node = FnCallNode::with_node(&token.parent())?; |
20 | let name_ref = calling_node.name_ref()?; | ||
21 | let name_ref = token.with_value(name_ref.syntax()); | ||
22 | 22 | ||
23 | let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); | ||
24 | let (mut call_info, has_self) = match &calling_node { | 23 | let (mut call_info, has_self) = match &calling_node { |
25 | FnCallNode::CallExpr(expr) => { | 24 | FnCallNode::CallExpr(call) => { |
26 | //FIXME: Type::as_callable is broken | 25 | //FIXME: Type::as_callable is broken |
27 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 26 | let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; |
28 | match callable_def { | 27 | match callable_def { |
29 | hir::CallableDef::FunctionId(it) => { | 28 | hir::CallableDef::FunctionId(it) => { |
30 | let fn_def = it.into(); | 29 | let fn_def = it.into(); |
@@ -36,12 +35,12 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
36 | } | 35 | } |
37 | } | 36 | } |
38 | } | 37 | } |
39 | FnCallNode::MethodCallExpr(expr) => { | 38 | FnCallNode::MethodCallExpr(method_call) => { |
40 | let function = analyzer.resolve_method_call(&expr)?; | 39 | let function = sema.resolve_method_call(&method_call)?; |
41 | (CallInfo::with_fn(db, function), function.has_self_param(db)) | 40 | (CallInfo::with_fn(db, function), function.has_self_param(db)) |
42 | } | 41 | } |
43 | FnCallNode::MacroCallExpr(expr) => { | 42 | FnCallNode::MacroCallExpr(macro_call) => { |
44 | let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?; | 43 | let macro_def = sema.resolve_macro_call(¯o_call)?; |
45 | (CallInfo::with_macro(db, macro_def)?, false) | 44 | (CallInfo::with_macro(db, macro_def)?, false) |
46 | } | 45 | } |
47 | }; | 46 | }; |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4bdc6ba23..c378c2c62 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -17,7 +17,6 @@ mod complete_postfix; | |||
17 | mod complete_macro_in_item_position; | 17 | mod complete_macro_in_item_position; |
18 | mod complete_trait_impl; | 18 | mod complete_trait_impl; |
19 | 19 | ||
20 | use ra_db::SourceDatabase; | ||
21 | use ra_ide_db::RootDatabase; | 20 | use ra_ide_db::RootDatabase; |
22 | 21 | ||
23 | #[cfg(test)] | 22 | #[cfg(test)] |
@@ -57,8 +56,7 @@ pub use crate::completion::completion_item::{ | |||
57 | /// identifier prefix/fuzzy match should be done higher in the stack, together | 56 | /// identifier prefix/fuzzy match should be done higher in the stack, together |
58 | /// with ordering of completions (currently this is done by the client). | 57 | /// with ordering of completions (currently this is done by the client). |
59 | pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> { | 58 | pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> { |
60 | let original_parse = db.parse(position.file_id); | 59 | let ctx = CompletionContext::new(db, position)?; |
61 | let ctx = CompletionContext::new(db, &original_parse, position)?; | ||
62 | 60 | ||
63 | let mut acc = Completions::default(); | 61 | let mut acc = Completions::default(); |
64 | 62 | ||
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 2ca78c927..a6e0158b2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -16,7 +16,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | 19 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
20 | Some(ty) => ty, | 20 | Some(ty) => ty, |
21 | _ => return, | 21 | _ => return, |
22 | }; | 22 | }; |
@@ -55,7 +55,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { | 56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { |
57 | let mut seen_methods = FxHashSet::default(); | 57 | let mut seen_methods = FxHashSet::default(); |
58 | let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); | 58 | let traits_in_scope = ctx.scope().traits_in_scope(); |
59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { | 59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { |
60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | 60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { |
61 | acc.add_function(ctx, func); | 61 | acc.add_function(ctx, func); |
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs index faadd1e3f..1866d9e6c 100644 --- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs +++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs | |||
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions}; | |||
5 | pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { |
6 | // Show only macros in top level. | 6 | // Show only macros in top level. |
7 | if ctx.is_new_item { | 7 | if ctx.is_new_item { |
8 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 8 | ctx.scope().process_all_names(&mut |name, res| { |
9 | if let hir::ScopeDef::MacroDef(mac) = res { | 9 | if let hir::ScopeDef::MacroDef(mac) = res { |
10 | acc.add_macro(ctx, Some(name.to_string()), mac); | 10 | acc.add_macro(ctx, Some(name.to_string()), mac); |
11 | } | 11 | } |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 2d7f09a6c..c626e90cc 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -11,7 +11,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
11 | Some(path) => path.clone(), | 11 | Some(path) => path.clone(), |
12 | _ => return, | 12 | _ => return, |
13 | }; | 13 | }; |
14 | let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path) { | 14 | let def = match ctx.scope().resolve_hir_path(&path) { |
15 | Some(PathResolution::Def(def)) => def, | 15 | Some(PathResolution::Def(def)) => def, |
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
@@ -49,7 +49,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
49 | // FIXME: complete T::AssocType | 49 | // FIXME: complete T::AssocType |
50 | let krate = ctx.module.map(|m| m.krate()); | 50 | let krate = ctx.module.map(|m| m.krate()); |
51 | if let Some(krate) = krate { | 51 | if let Some(krate) = krate { |
52 | let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); | 52 | let traits_in_scope = ctx.scope().traits_in_scope(); |
53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
54 | match item { | 54 | match item { |
55 | hir::AssocItem::Function(func) => { | 55 | hir::AssocItem::Function(func) => { |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index fd03b1c40..c2c6ca002 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -9,7 +9,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
9 | } | 9 | } |
10 | // FIXME: ideally, we should look at the type we are matching against and | 10 | // FIXME: ideally, we should look at the type we are matching against and |
11 | // suggest variants + auto-imports | 11 | // suggest variants + auto-imports |
12 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 12 | ctx.scope().process_all_names(&mut |name, res| { |
13 | let def = match &res { | 13 | let def = match &res { |
14 | hir::ScopeDef::ModuleDef(def) => def, | 14 | hir::ScopeDef::ModuleDef(def) => def, |
15 | _ => return, | 15 | _ => return, |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 5470dc291..8a74f993a 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -29,7 +29,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
29 | dot_receiver.syntax().text().to_string() | 29 | dot_receiver.syntax().text().to_string() |
30 | }; | 30 | }; |
31 | 31 | ||
32 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | 32 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
33 | Some(it) => it, | 33 | Some(it) => it, |
34 | None => return, | 34 | None => return, |
35 | }; | 35 | }; |
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index 577c394d2..f98353d76 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs | |||
@@ -5,10 +5,7 @@ use crate::completion::{CompletionContext, Completions}; | |||
5 | /// Complete fields in fields literals. | 5 | /// Complete fields in fields literals. |
6 | pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { |
7 | let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { | 7 | let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { |
8 | Some(( | 8 | Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?)) |
9 | ctx.analyzer.type_of(ctx.db, &it.clone().into())?, | ||
10 | ctx.analyzer.resolve_record_literal(it)?, | ||
11 | )) | ||
12 | }) { | 9 | }) { |
13 | Some(it) => it, | 10 | Some(it) => it, |
14 | _ => return, | 11 | _ => return, |
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index a56c7e3a1..9bdeae49f 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs | |||
@@ -4,10 +4,7 @@ use crate::completion::{CompletionContext, Completions}; | |||
4 | 4 | ||
5 | pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
6 | let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { | 6 | let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { |
7 | Some(( | 7 | Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?)) |
8 | ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?, | ||
9 | ctx.analyzer.resolve_record_pattern(it)?, | ||
10 | )) | ||
11 | }) { | 8 | }) { |
12 | Some(it) => it, | 9 | Some(it) => it, |
13 | _ => return, | 10 | _ => return, |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index e2ee86dd1..aad016d4a 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -7,9 +7,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | |||
7 | return; | 7 | return; |
8 | } | 8 | } |
9 | 9 | ||
10 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 10 | ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); |
11 | acc.add_resolution(ctx, name.to_string(), &res) | ||
12 | }); | ||
13 | } | 11 | } |
14 | 12 | ||
15 | #[cfg(test)] | 13 | #[cfg(test)] |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 83628e35c..9a27c164b 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -64,11 +64,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
64 | if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) { | 64 | if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) { |
65 | match trigger.kind() { | 65 | match trigger.kind() { |
66 | SyntaxKind::FN_DEF => { | 66 | SyntaxKind::FN_DEF => { |
67 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | 67 | for missing_fn in |
68 | .iter() | 68 | get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { |
69 | .filter_map(|item| match item { | 69 | match item { |
70 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | _ => None, | 71 | _ => None, |
72 | } | ||
72 | }) | 73 | }) |
73 | { | 74 | { |
74 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 75 | add_function_impl(&trigger, acc, ctx, &missing_fn); |
@@ -76,11 +77,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
76 | } | 77 | } |
77 | 78 | ||
78 | SyntaxKind::TYPE_ALIAS_DEF => { | 79 | SyntaxKind::TYPE_ALIAS_DEF => { |
79 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | 80 | for missing_fn in |
80 | .iter() | 81 | get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { |
81 | .filter_map(|item| match item { | 82 | match item { |
82 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
83 | _ => None, | 84 | _ => None, |
85 | } | ||
84 | }) | 86 | }) |
85 | { | 87 | { |
86 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); |
@@ -88,11 +90,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
88 | } | 90 | } |
89 | 91 | ||
90 | SyntaxKind::CONST_DEF => { | 92 | SyntaxKind::CONST_DEF => { |
91 | for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) | 93 | for missing_fn in |
92 | .iter() | 94 | get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { |
93 | .filter_map(|item| match item { | 95 | match item { |
94 | hir::AssocItem::Const(const_item) => Some(const_item), | 96 | hir::AssocItem::Const(const_item) => Some(const_item), |
95 | _ => None, | 97 | _ => None, |
98 | } | ||
96 | }) | 99 | }) |
97 | { | 100 | { |
98 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 101 | add_const_impl(&trigger, acc, ctx, &missing_fn); |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 8678a3234..81321a897 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Semantics, SemanticsScope}; | ||
4 | use ra_db::SourceDatabase; | ||
3 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
4 | use ra_syntax::{ | 6 | use ra_syntax::{ |
5 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
6 | ast, AstNode, Parse, SourceFile, | 8 | ast, AstNode, SourceFile, |
7 | SyntaxKind::*, | 9 | SyntaxKind::*, |
8 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | 10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
9 | }; | 11 | }; |
@@ -15,8 +17,8 @@ use crate::FilePosition; | |||
15 | /// exactly is the cursor, syntax-wise. | 17 | /// exactly is the cursor, syntax-wise. |
16 | #[derive(Debug)] | 18 | #[derive(Debug)] |
17 | pub(crate) struct CompletionContext<'a> { | 19 | pub(crate) struct CompletionContext<'a> { |
20 | pub(super) sema: Semantics<'a, RootDatabase>, | ||
18 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
19 | pub(super) analyzer: hir::SourceAnalyzer, | ||
20 | pub(super) offset: TextUnit, | 22 | pub(super) offset: TextUnit, |
21 | pub(super) token: SyntaxToken, | 23 | pub(super) token: SyntaxToken, |
22 | pub(super) module: Option<hir::Module>, | 24 | pub(super) module: Option<hir::Module>, |
@@ -51,20 +53,26 @@ pub(crate) struct CompletionContext<'a> { | |||
51 | impl<'a> CompletionContext<'a> { | 53 | impl<'a> CompletionContext<'a> { |
52 | pub(super) fn new( | 54 | pub(super) fn new( |
53 | db: &'a RootDatabase, | 55 | db: &'a RootDatabase, |
54 | original_parse: &'a Parse<ast::SourceFile>, | ||
55 | position: FilePosition, | 56 | position: FilePosition, |
56 | ) -> Option<CompletionContext<'a>> { | 57 | ) -> Option<CompletionContext<'a>> { |
57 | let mut sb = hir::SourceBinder::new(db); | 58 | let sema = Semantics::new(db); |
58 | let module = sb.to_module_def(position.file_id); | 59 | |
59 | let token = | 60 | let original_file = sema.parse(position.file_id); |
60 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; | 61 | |
61 | let analyzer = sb.analyze( | 62 | // Insert a fake ident to get a valid parse tree. We will use this file |
62 | hir::InFile::new(position.file_id.into(), &token.parent()), | 63 | // to determine context, though the original_file will be used for |
63 | Some(position.offset), | 64 | // actual completion. |
64 | ); | 65 | let file_with_fake_ident = { |
66 | let parse = db.parse(position.file_id); | ||
67 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | ||
68 | parse.reparse(&edit).tree() | ||
69 | }; | ||
70 | |||
71 | let module = sema.to_module_def(position.file_id); | ||
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
65 | let mut ctx = CompletionContext { | 73 | let mut ctx = CompletionContext { |
74 | sema, | ||
66 | db, | 75 | db, |
67 | analyzer, | ||
68 | token, | 76 | token, |
69 | offset: position.offset, | 77 | offset: position.offset, |
70 | module, | 78 | module, |
@@ -87,7 +95,7 @@ impl<'a> CompletionContext<'a> { | |||
87 | has_type_args: false, | 95 | has_type_args: false, |
88 | dot_receiver_is_ambiguous_float_literal: false, | 96 | dot_receiver_is_ambiguous_float_literal: false, |
89 | }; | 97 | }; |
90 | ctx.fill(&original_parse, position.offset); | 98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); |
91 | Some(ctx) | 99 | Some(ctx) |
92 | } | 100 | } |
93 | 101 | ||
@@ -100,29 +108,33 @@ impl<'a> CompletionContext<'a> { | |||
100 | } | 108 | } |
101 | } | 109 | } |
102 | 110 | ||
103 | fn fill(&mut self, original_parse: &'a Parse<ast::SourceFile>, offset: TextUnit) { | 111 | pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { |
104 | // Insert a fake ident to get a valid parse tree. We will use this file | 112 | self.sema.scope_at_offset(&self.token.parent(), self.offset) |
105 | // to determine context, though the original_file will be used for | 113 | } |
106 | // actual completion. | ||
107 | let file = { | ||
108 | let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string()); | ||
109 | original_parse.reparse(&edit).tree() | ||
110 | }; | ||
111 | 114 | ||
115 | fn fill( | ||
116 | &mut self, | ||
117 | original_file: &ast::SourceFile, | ||
118 | file_with_fake_ident: ast::SourceFile, | ||
119 | offset: TextUnit, | ||
120 | ) { | ||
112 | // First, let's try to complete a reference to some declaration. | 121 | // First, let's try to complete a reference to some declaration. |
113 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) { | 122 | if let Some(name_ref) = |
123 | find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset) | ||
124 | { | ||
114 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 125 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
115 | // See RFC#1685. | 126 | // See RFC#1685. |
116 | if is_node::<ast::Param>(name_ref.syntax()) { | 127 | if is_node::<ast::Param>(name_ref.syntax()) { |
117 | self.is_param = true; | 128 | self.is_param = true; |
118 | return; | 129 | return; |
119 | } | 130 | } |
120 | self.classify_name_ref(original_parse.tree(), name_ref); | 131 | self.classify_name_ref(original_file, name_ref); |
121 | } | 132 | } |
122 | 133 | ||
123 | // Otherwise, see if this is a declaration. We can use heuristics to | 134 | // Otherwise, see if this is a declaration. We can use heuristics to |
124 | // suggest declaration names, see `CompletionKind::Magic`. | 135 | // suggest declaration names, see `CompletionKind::Magic`. |
125 | if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) { | 136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) |
137 | { | ||
126 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 138 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
127 | let parent = bind_pat.syntax().parent(); | 139 | let parent = bind_pat.syntax().parent(); |
128 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -136,13 +148,12 @@ impl<'a> CompletionContext<'a> { | |||
136 | return; | 148 | return; |
137 | } | 149 | } |
138 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
139 | self.record_lit_pat = | 151 | self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); |
140 | find_node_at_offset(original_parse.tree().syntax(), self.offset); | ||
141 | } | 152 | } |
142 | } | 153 | } |
143 | } | 154 | } |
144 | 155 | ||
145 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { | 156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { |
146 | self.name_ref_syntax = | 157 | self.name_ref_syntax = |
147 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); |
148 | let name_range = name_ref.syntax().text_range(); | 159 | let name_range = name_ref.syntax().text_range(); |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 9cf86b26d..a52f7fdd9 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -2,7 +2,10 @@ | |||
2 | 2 | ||
3 | use std::cell::RefCell; | 3 | use std::cell::RefCell; |
4 | 4 | ||
5 | use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; | 5 | use hir::{ |
6 | diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, | ||
7 | Semantics, | ||
8 | }; | ||
6 | use itertools::Itertools; | 9 | use itertools::Itertools; |
7 | use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; | 10 | use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; |
8 | use ra_ide_db::RootDatabase; | 11 | use ra_ide_db::RootDatabase; |
@@ -24,7 +27,7 @@ pub enum Severity { | |||
24 | 27 | ||
25 | pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { | 28 | pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { |
26 | let _p = profile("diagnostics"); | 29 | let _p = profile("diagnostics"); |
27 | let mut sb = hir::SourceBinder::new(db); | 30 | let sema = Semantics::new(db); |
28 | let parse = db.parse(file_id); | 31 | let parse = db.parse(file_id); |
29 | let mut res = Vec::new(); | 32 | let mut res = Vec::new(); |
30 | 33 | ||
@@ -110,7 +113,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
110 | fix: Some(fix), | 113 | fix: Some(fix), |
111 | }) | 114 | }) |
112 | }); | 115 | }); |
113 | if let Some(m) = sb.to_module_def(file_id) { | 116 | if let Some(m) = sema.to_module_def(file_id) { |
114 | m.diagnostics(db, &mut sink); | 117 | m.diagnostics(db, &mut sink); |
115 | }; | 118 | }; |
116 | drop(sink); | 119 | drop(sink); |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index b42cb477e..5afb23764 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; | 4 | use hir::{original_range, AssocItem, FieldSource, HasSource, InFile, ModuleSource}; |
5 | use ra_db::{FileId, SourceDatabase}; | 5 | use ra_db::{FileId, SourceDatabase}; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
@@ -11,7 +11,11 @@ use ra_syntax::{ | |||
11 | TextRange, | 11 | TextRange, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{expand::original_range, references::NameDefinition, FileSymbol}; | 14 | use crate::{ |
15 | // expand::original_range, | ||
16 | references::NameDefinition, | ||
17 | FileSymbol, | ||
18 | }; | ||
15 | 19 | ||
16 | use super::short_label::ShortLabel; | 20 | use super::short_label::ShortLabel; |
17 | 21 | ||
@@ -125,7 +129,7 @@ impl NavigationTarget { | |||
125 | } | 129 | } |
126 | 130 | ||
127 | /// Allows `NavigationTarget` to be created from a `NameOwner` | 131 | /// Allows `NavigationTarget` to be created from a `NameOwner` |
128 | pub(crate) fn from_named( | 132 | fn from_named( |
129 | db: &RootDatabase, | 133 | db: &RootDatabase, |
130 | node: InFile<&dyn ast::NameOwner>, | 134 | node: InFile<&dyn ast::NameOwner>, |
131 | docs: Option<String>, | 135 | docs: Option<String>, |
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs deleted file mode 100644 index 9f3aaa3a3..000000000 --- a/crates/ra_ide/src/expand.rs +++ /dev/null | |||
@@ -1,102 +0,0 @@ | |||
1 | //! Utilities to work with files, produced by macros. | ||
2 | use std::iter::successors; | ||
3 | |||
4 | use hir::{InFile, Origin}; | ||
5 | use ra_db::FileId; | ||
6 | use ra_ide_db::RootDatabase; | ||
7 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; | ||
8 | |||
9 | use crate::FileRange; | ||
10 | |||
11 | pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { | ||
12 | if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { | ||
13 | return range; | ||
14 | } | ||
15 | |||
16 | if let Some(expansion) = node.file_id.expansion_info(db) { | ||
17 | if let Some(call_node) = expansion.call_node() { | ||
18 | return FileRange { | ||
19 | file_id: call_node.file_id.original_file(db), | ||
20 | range: call_node.value.text_range(), | ||
21 | }; | ||
22 | } | ||
23 | } | ||
24 | |||
25 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | ||
26 | } | ||
27 | |||
28 | fn original_range_and_origin( | ||
29 | db: &RootDatabase, | ||
30 | node: InFile<&SyntaxNode>, | ||
31 | ) -> Option<(FileRange, Origin)> { | ||
32 | let expansion = node.file_id.expansion_info(db)?; | ||
33 | |||
34 | // the input node has only one token ? | ||
35 | let single = node.value.first_token()? == node.value.last_token()?; | ||
36 | |||
37 | // FIXME: We should handle recurside macro expansions | ||
38 | let (range, origin) = node.value.descendants().find_map(|it| { | ||
39 | let first = it.first_token()?; | ||
40 | let last = it.last_token()?; | ||
41 | |||
42 | if !single && first == last { | ||
43 | return None; | ||
44 | } | ||
45 | |||
46 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | ||
47 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | ||
48 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
49 | |||
50 | if first.file_id != last.file_id || first_origin != last_origin { | ||
51 | return None; | ||
52 | } | ||
53 | |||
54 | // FIXME: Add union method in TextRange | ||
55 | Some(( | ||
56 | first.with_value(union_range(first.value.text_range(), last.value.text_range())), | ||
57 | first_origin, | ||
58 | )) | ||
59 | })?; | ||
60 | |||
61 | return Some(( | ||
62 | FileRange { file_id: range.file_id.original_file(db), range: range.value }, | ||
63 | origin, | ||
64 | )); | ||
65 | |||
66 | fn union_range(a: TextRange, b: TextRange) -> TextRange { | ||
67 | let start = a.start().min(b.start()); | ||
68 | let end = a.end().max(b.end()); | ||
69 | TextRange::from_to(start, end) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | pub(crate) fn descend_into_macros( | ||
74 | db: &RootDatabase, | ||
75 | file_id: FileId, | ||
76 | token: SyntaxToken, | ||
77 | ) -> InFile<SyntaxToken> { | ||
78 | let src = InFile::new(file_id.into(), token); | ||
79 | |||
80 | let source_analyzer = | ||
81 | hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None); | ||
82 | |||
83 | descend_into_macros_with_analyzer(db, &source_analyzer, src) | ||
84 | } | ||
85 | |||
86 | pub(crate) fn descend_into_macros_with_analyzer( | ||
87 | db: &RootDatabase, | ||
88 | source_analyzer: &hir::SourceAnalyzer, | ||
89 | src: InFile<SyntaxToken>, | ||
90 | ) -> InFile<SyntaxToken> { | ||
91 | successors(Some(src), |token| { | ||
92 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | ||
93 | let tt = macro_call.token_tree()?; | ||
94 | if !token.value.text_range().is_subrange(&tt.syntax().text_range()) { | ||
95 | return None; | ||
96 | } | ||
97 | let exp = source_analyzer.expand(db, token.with_value(¯o_call))?; | ||
98 | exp.map_token_down(db, token.as_ref()) | ||
99 | }) | ||
100 | .last() | ||
101 | .unwrap() | ||
102 | } | ||
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index af2783bef..f2814e684 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! This modules implements "expand macro" functionality in the IDE | 1 | //! This modules implements "expand macro" functionality in the IDE |
2 | 2 | ||
3 | use hir::db::AstDatabase; | 3 | use hir::Semantics; |
4 | use ra_db::SourceDatabase; | ||
5 | use ra_ide_db::RootDatabase; | 4 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 5 | use ra_syntax::{ |
7 | algo::{find_node_at_offset, replace_descendants}, | 6 | algo::{find_node_at_offset, replace_descendants}, |
@@ -17,13 +16,12 @@ pub struct ExpandedMacro { | |||
17 | } | 16 | } |
18 | 17 | ||
19 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | 18 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { |
20 | let parse = db.parse(position.file_id); | 19 | let sema = Semantics::new(db); |
21 | let file = parse.tree(); | 20 | let file = sema.parse(position.file_id); |
22 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; | 21 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; |
23 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; | 22 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; |
24 | 23 | ||
25 | let source = hir::InFile::new(position.file_id.into(), mac.syntax()); | 24 | let expanded = expand_macro_recur(&sema, &mac)?; |
26 | let expanded = expand_macro_recur(db, source, source.with_value(&mac))?; | ||
27 | 25 | ||
28 | // FIXME: | 26 | // FIXME: |
29 | // macro expansion may lose all white space information | 27 | // macro expansion may lose all white space information |
@@ -33,21 +31,16 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | |||
33 | } | 31 | } |
34 | 32 | ||
35 | fn expand_macro_recur( | 33 | fn expand_macro_recur( |
36 | db: &RootDatabase, | 34 | sema: &Semantics<RootDatabase>, |
37 | source: hir::InFile<&SyntaxNode>, | 35 | macro_call: &ast::MacroCall, |
38 | macro_call: hir::InFile<&ast::MacroCall>, | ||
39 | ) -> Option<SyntaxNode> { | 36 | ) -> Option<SyntaxNode> { |
40 | let analyzer = hir::SourceAnalyzer::new(db, source, None); | 37 | let mut expanded = sema.expand(macro_call)?; |
41 | let expansion = analyzer.expand(db, macro_call)?; | ||
42 | let macro_file_id = expansion.file_id(); | ||
43 | let mut expanded: SyntaxNode = db.parse_or_expand(macro_file_id)?; | ||
44 | 38 | ||
45 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); | 39 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); |
46 | let mut replaces: FxHashMap<SyntaxElement, SyntaxElement> = FxHashMap::default(); | 40 | let mut replaces: FxHashMap<SyntaxElement, SyntaxElement> = FxHashMap::default(); |
47 | 41 | ||
48 | for child in children.into_iter() { | 42 | for child in children.into_iter() { |
49 | let node = hir::InFile::new(macro_file_id, &child); | 43 | if let Some(new_node) = expand_macro_recur(sema, &child) { |
50 | if let Some(new_node) = expand_macro_recur(db, source, node) { | ||
51 | // Replace the whole node if it is root | 44 | // Replace the whole node if it is root |
52 | // `replace_descendants` will not replace the parent node | 45 | // `replace_descendants` will not replace the parent node |
53 | // but `SyntaxNode::descendants include itself | 46 | // but `SyntaxNode::descendants include itself |
@@ -120,10 +113,12 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
120 | 113 | ||
121 | #[cfg(test)] | 114 | #[cfg(test)] |
122 | mod tests { | 115 | mod tests { |
123 | use super::*; | ||
124 | use crate::mock_analysis::analysis_and_position; | ||
125 | use insta::assert_snapshot; | 116 | use insta::assert_snapshot; |
126 | 117 | ||
118 | use crate::mock_analysis::analysis_and_position; | ||
119 | |||
120 | use super::*; | ||
121 | |||
127 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { | 122 | fn check_expand_macro(fixture: &str) -> ExpandedMacro { |
128 | let (analysis, pos) = analysis_and_position(fixture); | 123 | let (analysis, pos) = analysis_and_position(fixture); |
129 | analysis.expand_macro(pos).unwrap().unwrap() | 124 | analysis.expand_macro(pos).unwrap().unwrap() |
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 4757d8e22..f5a063351 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs | |||
@@ -2,26 +2,26 @@ | |||
2 | 2 | ||
3 | use std::iter::successors; | 3 | use std::iter::successors; |
4 | 4 | ||
5 | use hir::db::AstDatabase; | 5 | use hir::Semantics; |
6 | use ra_db::SourceDatabase; | ||
7 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
8 | use ra_syntax::{ | 7 | use ra_syntax::{ |
9 | algo::find_covering_element, | 8 | algo::{self, find_covering_element, skip_trivia_token}, |
10 | ast::{self, AstNode, AstToken}, | 9 | ast::{self, AstNode, AstToken}, |
11 | Direction, NodeOrToken, SyntaxElement, | 10 | Direction, NodeOrToken, |
12 | SyntaxKind::{self, *}, | 11 | SyntaxKind::{self, *}, |
13 | SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, | 12 | SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, |
14 | }; | 13 | }; |
15 | 14 | ||
16 | use crate::{expand::descend_into_macros, FileId, FileRange}; | 15 | use crate::FileRange; |
17 | 16 | ||
18 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { | 17 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { |
19 | let src = db.parse(frange.file_id).tree(); | 18 | let sema = Semantics::new(db); |
20 | try_extend_selection(db, src.syntax(), frange).unwrap_or(frange.range) | 19 | let src = sema.parse(frange.file_id); |
20 | try_extend_selection(&sema, src.syntax(), frange).unwrap_or(frange.range) | ||
21 | } | 21 | } |
22 | 22 | ||
23 | fn try_extend_selection( | 23 | fn try_extend_selection( |
24 | db: &RootDatabase, | 24 | sema: &Semantics<RootDatabase>, |
25 | root: &SyntaxNode, | 25 | root: &SyntaxNode, |
26 | frange: FileRange, | 26 | frange: FileRange, |
27 | ) -> Option<TextRange> { | 27 | ) -> Option<TextRange> { |
@@ -86,7 +86,7 @@ fn try_extend_selection( | |||
86 | // if we are in single token_tree, we maybe live in macro or attr | 86 | // if we are in single token_tree, we maybe live in macro or attr |
87 | if node.kind() == TOKEN_TREE { | 87 | if node.kind() == TOKEN_TREE { |
88 | if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) { | 88 | if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) { |
89 | if let Some(range) = extend_tokens_from_range(db, frange.file_id, macro_call, range) { | 89 | if let Some(range) = extend_tokens_from_range(sema, macro_call, range) { |
90 | return Some(range); | 90 | return Some(range); |
91 | } | 91 | } |
92 | } | 92 | } |
@@ -96,7 +96,7 @@ fn try_extend_selection( | |||
96 | return Some(node.text_range()); | 96 | return Some(node.text_range()); |
97 | } | 97 | } |
98 | 98 | ||
99 | let node = shallowest_node(&node.into()).unwrap(); | 99 | let node = shallowest_node(&node.into()); |
100 | 100 | ||
101 | if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) { | 101 | if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) { |
102 | if let Some(range) = extend_list_item(&node) { | 102 | if let Some(range) = extend_list_item(&node) { |
@@ -108,8 +108,7 @@ fn try_extend_selection( | |||
108 | } | 108 | } |
109 | 109 | ||
110 | fn extend_tokens_from_range( | 110 | fn extend_tokens_from_range( |
111 | db: &RootDatabase, | 111 | sema: &Semantics<RootDatabase>, |
112 | file_id: FileId, | ||
113 | macro_call: ast::MacroCall, | 112 | macro_call: ast::MacroCall, |
114 | original_range: TextRange, | 113 | original_range: TextRange, |
115 | ) -> Option<TextRange> { | 114 | ) -> Option<TextRange> { |
@@ -119,54 +118,50 @@ fn extend_tokens_from_range( | |||
119 | NodeOrToken::Token(it) => (it.clone(), it), | 118 | NodeOrToken::Token(it) => (it.clone(), it), |
120 | }; | 119 | }; |
121 | 120 | ||
122 | let mut first_token = skip_whitespace(first_token, Direction::Next)?; | 121 | let mut first_token = skip_trivia_token(first_token, Direction::Next)?; |
123 | let mut last_token = skip_whitespace(last_token, Direction::Prev)?; | 122 | let mut last_token = skip_trivia_token(last_token, Direction::Prev)?; |
124 | 123 | ||
125 | while !first_token.text_range().is_subrange(&original_range) { | 124 | while !first_token.text_range().is_subrange(&original_range) { |
126 | first_token = skip_whitespace(first_token.next_token()?, Direction::Next)?; | 125 | first_token = skip_trivia_token(first_token.next_token()?, Direction::Next)?; |
127 | } | 126 | } |
128 | while !last_token.text_range().is_subrange(&original_range) { | 127 | while !last_token.text_range().is_subrange(&original_range) { |
129 | last_token = skip_whitespace(last_token.prev_token()?, Direction::Prev)?; | 128 | last_token = skip_trivia_token(last_token.prev_token()?, Direction::Prev)?; |
130 | } | 129 | } |
131 | 130 | ||
132 | // compute original mapped token range | 131 | // compute original mapped token range |
133 | let expanded = { | 132 | let extended = { |
134 | let first_node = descend_into_macros(db, file_id, first_token.clone()); | 133 | let fst_expanded = sema.descend_into_macros(first_token.clone()); |
135 | let first_node = first_node.map(|it| it.text_range()); | 134 | let lst_expanded = sema.descend_into_macros(last_token.clone()); |
136 | 135 | let mut lca = algo::least_common_ancestor(&fst_expanded.parent(), &lst_expanded.parent())?; | |
137 | let last_node = descend_into_macros(db, file_id, last_token.clone()); | 136 | lca = shallowest_node(&lca); |
138 | if last_node.file_id == file_id.into() || first_node.file_id != last_node.file_id { | 137 | if lca.first_token() == Some(fst_expanded) && lca.last_token() == Some(lst_expanded) { |
139 | return None; | 138 | lca = lca.parent()?; |
140 | } | 139 | } |
141 | first_node.map(|it| union_range(it, last_node.value.text_range())) | 140 | lca |
142 | }; | 141 | }; |
143 | 142 | ||
144 | // Compute parent node range | 143 | // Compute parent node range |
145 | let src = db.parse_or_expand(expanded.file_id)?; | 144 | let validate = |token: &SyntaxToken| { |
146 | let parent = shallowest_node(&find_covering_element(&src, expanded.value))?.parent()?; | 145 | let expanded = sema.descend_into_macros(token.clone()); |
147 | 146 | algo::least_common_ancestor(&extended, &expanded.parent()).as_ref() == Some(&extended) | |
148 | let validate = |token: SyntaxToken| { | ||
149 | let node = descend_into_macros(db, file_id, token.clone()); | ||
150 | if node.file_id == expanded.file_id | ||
151 | && node.value.text_range().is_subrange(&parent.text_range()) | ||
152 | { | ||
153 | Some(token) | ||
154 | } else { | ||
155 | None | ||
156 | } | ||
157 | }; | 147 | }; |
158 | 148 | ||
159 | // Find the first and last text range under expanded parent | 149 | // Find the first and last text range under expanded parent |
160 | let first = successors(Some(first_token), |token| { | 150 | let first = successors(Some(first_token), |token| { |
161 | validate(skip_whitespace(token.prev_token()?, Direction::Prev)?) | 151 | let token = token.prev_token()?; |
152 | skip_trivia_token(token, Direction::Prev) | ||
162 | }) | 153 | }) |
154 | .take_while(validate) | ||
163 | .last()?; | 155 | .last()?; |
156 | |||
164 | let last = successors(Some(last_token), |token| { | 157 | let last = successors(Some(last_token), |token| { |
165 | validate(skip_whitespace(token.next_token()?, Direction::Next)?) | 158 | let token = token.next_token()?; |
159 | skip_trivia_token(token, Direction::Next) | ||
166 | }) | 160 | }) |
161 | .take_while(validate) | ||
167 | .last()?; | 162 | .last()?; |
168 | 163 | ||
169 | let range = union_range(first.text_range(), last.text_range()); | 164 | let range = first.text_range().extend_to(&last.text_range()); |
170 | if original_range.is_subrange(&range) && original_range != range { | 165 | if original_range.is_subrange(&range) && original_range != range { |
171 | Some(range) | 166 | Some(range) |
172 | } else { | 167 | } else { |
@@ -174,25 +169,9 @@ fn extend_tokens_from_range( | |||
174 | } | 169 | } |
175 | } | 170 | } |
176 | 171 | ||
177 | fn skip_whitespace(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> { | ||
178 | while token.kind() == WHITESPACE { | ||
179 | token = match direction { | ||
180 | Direction::Next => token.next_token()?, | ||
181 | Direction::Prev => token.prev_token()?, | ||
182 | } | ||
183 | } | ||
184 | Some(token) | ||
185 | } | ||
186 | |||
187 | fn union_range(range: TextRange, r: TextRange) -> TextRange { | ||
188 | let start = range.start().min(r.start()); | ||
189 | let end = range.end().max(r.end()); | ||
190 | TextRange::from_to(start, end) | ||
191 | } | ||
192 | |||
193 | /// Find the shallowest node with same range, which allows us to traverse siblings. | 172 | /// Find the shallowest node with same range, which allows us to traverse siblings. |
194 | fn shallowest_node(node: &SyntaxElement) -> Option<SyntaxNode> { | 173 | fn shallowest_node(node: &SyntaxNode) -> SyntaxNode { |
195 | node.ancestors().take_while(|n| n.text_range() == node.text_range()).last() | 174 | node.ancestors().take_while(|n| n.text_range() == node.text_range()).last().unwrap() |
196 | } | 175 | } |
197 | 176 | ||
198 | fn extend_single_word_in_comment_or_string( | 177 | fn extend_single_word_in_comment_or_string( |
@@ -334,10 +313,12 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment { | |||
334 | 313 | ||
335 | #[cfg(test)] | 314 | #[cfg(test)] |
336 | mod tests { | 315 | mod tests { |
337 | use super::*; | ||
338 | use crate::mock_analysis::single_file; | ||
339 | use test_utils::extract_offset; | 316 | use test_utils::extract_offset; |
340 | 317 | ||
318 | use crate::mock_analysis::single_file; | ||
319 | |||
320 | use super::*; | ||
321 | |||
341 | fn do_check(before: &str, afters: &[&str]) { | 322 | fn do_check(before: &str, afters: &[&str]) { |
342 | let (cursor, before) = extract_offset(before); | 323 | let (cursor, before) = extract_offset(before); |
343 | let (analysis, file_id) = single_file(&before); | 324 | let (analysis, file_id) = single_file(&before); |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index feff1ec3f..6053c1bb6 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, InFile, SourceBinder}; | 3 | use hir::Semantics; |
4 | use ra_ide_db::{symbol_index, RootDatabase}; | 4 | use ra_ide_db::{defs::classify_name, symbol_index, RootDatabase}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | ast::{self}, | 6 | ast::{self}, |
7 | match_ast, AstNode, | 7 | match_ast, AstNode, |
@@ -11,8 +11,7 @@ use ra_syntax::{ | |||
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | display::{ToNav, TryToNav}, | 13 | display::{ToNav, TryToNav}, |
14 | expand::descend_into_macros, | 14 | references::classify_name_ref, |
15 | references::{classify_name, classify_name_ref}, | ||
16 | FilePosition, NavigationTarget, RangeInfo, | 15 | FilePosition, NavigationTarget, RangeInfo, |
17 | }; | 16 | }; |
18 | 17 | ||
@@ -20,18 +19,18 @@ pub(crate) fn goto_definition( | |||
20 | db: &RootDatabase, | 19 | db: &RootDatabase, |
21 | position: FilePosition, | 20 | position: FilePosition, |
22 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 21 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
23 | let file = db.parse_or_expand(position.file_id.into())?; | 22 | let sema = Semantics::new(db); |
23 | let file = sema.parse(position.file_id).syntax().clone(); | ||
24 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 24 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
25 | let token = descend_into_macros(db, position.file_id, original_token.clone()); | 25 | let token = sema.descend_into_macros(original_token.clone()); |
26 | 26 | ||
27 | let mut sb = SourceBinder::new(db); | ||
28 | let nav_targets = match_ast! { | 27 | let nav_targets = match_ast! { |
29 | match (token.value.parent()) { | 28 | match (token.parent()) { |
30 | ast::NameRef(name_ref) => { | 29 | ast::NameRef(name_ref) => { |
31 | reference_definition(&mut sb, token.with_value(&name_ref)).to_vec() | 30 | reference_definition(&sema, &name_ref).to_vec() |
32 | }, | 31 | }, |
33 | ast::Name(name) => { | 32 | ast::Name(name) => { |
34 | name_definition(&mut sb, token.with_value(&name))? | 33 | name_definition(&sema, &name)? |
35 | }, | 34 | }, |
36 | _ => return None, | 35 | _ => return None, |
37 | } | 36 | } |
@@ -68,33 +67,33 @@ impl ReferenceResult { | |||
68 | } | 67 | } |
69 | 68 | ||
70 | pub(crate) fn reference_definition( | 69 | pub(crate) fn reference_definition( |
71 | sb: &mut SourceBinder<RootDatabase>, | 70 | sema: &Semantics<RootDatabase>, |
72 | name_ref: InFile<&ast::NameRef>, | 71 | name_ref: &ast::NameRef, |
73 | ) -> ReferenceResult { | 72 | ) -> ReferenceResult { |
74 | use self::ReferenceResult::*; | 73 | use self::ReferenceResult::*; |
75 | 74 | ||
76 | let name_kind = classify_name_ref(sb, name_ref); | 75 | let name_kind = classify_name_ref(sema, name_ref); |
77 | if let Some(def) = name_kind { | 76 | if let Some(def) = name_kind { |
78 | return match def.try_to_nav(sb.db) { | 77 | return match def.try_to_nav(sema.db) { |
79 | Some(nav) => ReferenceResult::Exact(nav), | 78 | Some(nav) => ReferenceResult::Exact(nav), |
80 | None => ReferenceResult::Approximate(Vec::new()), | 79 | None => ReferenceResult::Approximate(Vec::new()), |
81 | }; | 80 | }; |
82 | } | 81 | } |
83 | 82 | ||
84 | // Fallback index based approach: | 83 | // Fallback index based approach: |
85 | let navs = symbol_index::index_resolve(sb.db, name_ref.value) | 84 | let navs = symbol_index::index_resolve(sema.db, name_ref) |
86 | .into_iter() | 85 | .into_iter() |
87 | .map(|s| s.to_nav(sb.db)) | 86 | .map(|s| s.to_nav(sema.db)) |
88 | .collect(); | 87 | .collect(); |
89 | Approximate(navs) | 88 | Approximate(navs) |
90 | } | 89 | } |
91 | 90 | ||
92 | fn name_definition( | 91 | fn name_definition( |
93 | sb: &mut SourceBinder<RootDatabase>, | 92 | sema: &Semantics<RootDatabase>, |
94 | name: InFile<&ast::Name>, | 93 | name: &ast::Name, |
95 | ) -> Option<Vec<NavigationTarget>> { | 94 | ) -> Option<Vec<NavigationTarget>> { |
96 | let def = classify_name(sb, name)?; | 95 | let def = classify_name(sema, name)?; |
97 | let nav = def.try_to_nav(sb.db)?; | 96 | let nav = def.try_to_nav(sema.db)?; |
98 | Some(vec![nav]) | 97 | Some(vec![nav]) |
99 | } | 98 | } |
100 | 99 | ||
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 69940fc36..869a4708b 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -1,31 +1,31 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::db::AstDatabase; | ||
4 | use ra_ide_db::RootDatabase; | 3 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 4 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
6 | 5 | ||
7 | use crate::{ | 6 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; |
8 | display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, RangeInfo, | ||
9 | }; | ||
10 | 7 | ||
11 | pub(crate) fn goto_type_definition( | 8 | pub(crate) fn goto_type_definition( |
12 | db: &RootDatabase, | 9 | db: &RootDatabase, |
13 | position: FilePosition, | 10 | position: FilePosition, |
14 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 11 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
15 | let file = db.parse_or_expand(position.file_id.into())?; | 12 | let sema = hir::Semantics::new(db); |
16 | let token = pick_best(file.token_at_offset(position.offset))?; | 13 | |
17 | let token = descend_into_macros(db, position.file_id, token); | 14 | let file: ast::SourceFile = sema.parse(position.file_id); |
18 | 15 | let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; | |
19 | let node = token | 16 | let token: SyntaxToken = sema.descend_into_macros(token); |
20 | .value | 17 | |
21 | .ancestors() | 18 | let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { |
22 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | 19 | let ty = match_ast! { |
23 | 20 | match node { | |
24 | let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None); | 21 | ast::Expr(expr) => { sema.type_of_expr(&expr)? }, |
22 | ast::Pat(pat) => { sema.type_of_pat(&pat)? }, | ||
23 | _ => { return None }, | ||
24 | } | ||
25 | }; | ||
25 | 26 | ||
26 | let ty: hir::Type = ast::Expr::cast(node.clone()) | 27 | Some((ty, node)) |
27 | .and_then(|e| analyzer.type_of(db, &e)) | 28 | })?; |
28 | .or_else(|| ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p)))?; | ||
29 | 29 | ||
30 | let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; | 30 | let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; |
31 | 31 | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 1c6ca36df..177038e20 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; | 3 | use hir::{Adt, HasSource, HirDisplay, Semantics}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_ide_db::{ |
5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; | 5 | defs::{classify_name, NameDefinition}, |
6 | RootDatabase, | ||
7 | }; | ||
6 | use ra_syntax::{ | 8 | use ra_syntax::{ |
7 | algo::find_covering_element, | 9 | algo::find_covering_element, |
8 | ast::{self, DocCommentsOwner}, | 10 | ast::{self, DocCommentsOwner}, |
@@ -13,8 +15,7 @@ use ra_syntax::{ | |||
13 | 15 | ||
14 | use crate::{ | 16 | use crate::{ |
15 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 17 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
16 | expand::{descend_into_macros, original_range}, | 18 | references::classify_name_ref, |
17 | references::{classify_name, classify_name_ref}, | ||
18 | FilePosition, FileRange, RangeInfo, | 19 | FilePosition, FileRange, RangeInfo, |
19 | }; | 20 | }; |
20 | 21 | ||
@@ -143,25 +144,25 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: NameDefinition) -> Option<S | |||
143 | } | 144 | } |
144 | 145 | ||
145 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 146 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { |
146 | let file = db.parse_or_expand(position.file_id.into())?; | 147 | let sema = Semantics::new(db); |
148 | let file = sema.parse(position.file_id).syntax().clone(); | ||
147 | let token = pick_best(file.token_at_offset(position.offset))?; | 149 | let token = pick_best(file.token_at_offset(position.offset))?; |
148 | let token = descend_into_macros(db, position.file_id, token); | 150 | let token = sema.descend_into_macros(token); |
149 | 151 | ||
150 | let mut res = HoverResult::new(); | 152 | let mut res = HoverResult::new(); |
151 | 153 | ||
152 | let mut sb = SourceBinder::new(db); | ||
153 | if let Some((node, name_kind)) = match_ast! { | 154 | if let Some((node, name_kind)) = match_ast! { |
154 | match (token.value.parent()) { | 155 | match (token.parent()) { |
155 | ast::NameRef(name_ref) => { | 156 | ast::NameRef(name_ref) => { |
156 | classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().clone(), d)) | 157 | classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d)) |
157 | }, | 158 | }, |
158 | ast::Name(name) => { | 159 | ast::Name(name) => { |
159 | classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().clone(), d)) | 160 | classify_name(&sema, &name).map(|d| (name.syntax().clone(), d)) |
160 | }, | 161 | }, |
161 | _ => None, | 162 | _ => None, |
162 | } | 163 | } |
163 | } { | 164 | } { |
164 | let range = original_range(db, token.with_value(&node)).range; | 165 | let range = sema.original_range(&node).range; |
165 | res.extend(hover_text_from_name_kind(db, name_kind)); | 166 | res.extend(hover_text_from_name_kind(db, name_kind)); |
166 | 167 | ||
167 | if !res.is_empty() { | 168 | if !res.is_empty() { |
@@ -170,11 +171,14 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
170 | } | 171 | } |
171 | 172 | ||
172 | let node = token | 173 | let node = token |
173 | .value | ||
174 | .ancestors() | 174 | .ancestors() |
175 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | 175 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; |
176 | 176 | ||
177 | let frange = original_range(db, token.with_value(&node)); | 177 | // FIXME: Currently `hover::typeof` do not work inside |
178 | // macro expansion such that if the hover range is pointing to | ||
179 | // a string literal, the following type_of will return None. | ||
180 | // See also `test_hover_through_literal_string_in_macro` | ||
181 | let frange = sema.original_range(&node); | ||
178 | res.extend(type_of(db, frange).map(rust_code_markup)); | 182 | res.extend(type_of(db, frange).map(rust_code_markup)); |
179 | if res.is_empty() { | 183 | if res.is_empty() { |
180 | return None; | 184 | return None; |
@@ -197,19 +201,17 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
197 | } | 201 | } |
198 | 202 | ||
199 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | 203 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { |
200 | let parse = db.parse(frange.file_id); | 204 | let sema = Semantics::new(db); |
201 | let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); | 205 | let source_file = sema.parse(frange.file_id); |
206 | let leaf_node = find_covering_element(source_file.syntax(), frange.range); | ||
202 | // if we picked identifier, expand to pattern/expression | 207 | // if we picked identifier, expand to pattern/expression |
203 | let node = leaf_node | 208 | let node = leaf_node |
204 | .ancestors() | 209 | .ancestors() |
205 | .take_while(|it| it.text_range() == leaf_node.text_range()) | 210 | .take_while(|it| it.text_range() == leaf_node.text_range()) |
206 | .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; | 211 | .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; |
207 | let analyzer = | 212 | let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| sema.type_of_expr(&e)) { |
208 | hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None); | ||
209 | let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) | ||
210 | { | ||
211 | ty | 213 | ty |
212 | } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, &p)) { | 214 | } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| sema.type_of_pat(&p)) { |
213 | ty | 215 | ty |
214 | } else { | 216 | } else { |
215 | return None; | 217 | return None; |
@@ -219,11 +221,12 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
219 | 221 | ||
220 | #[cfg(test)] | 222 | #[cfg(test)] |
221 | mod tests { | 223 | mod tests { |
224 | use ra_db::FileLoader; | ||
225 | use ra_syntax::TextRange; | ||
226 | |||
222 | use crate::mock_analysis::{ | 227 | use crate::mock_analysis::{ |
223 | analysis_and_position, single_file_with_position, single_file_with_range, | 228 | analysis_and_position, single_file_with_position, single_file_with_range, |
224 | }; | 229 | }; |
225 | use ra_db::FileLoader; | ||
226 | use ra_syntax::TextRange; | ||
227 | 230 | ||
228 | fn trim_markup(s: &str) -> &str { | 231 | fn trim_markup(s: &str) -> &str { |
229 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") | 232 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") |
@@ -251,6 +254,11 @@ mod tests { | |||
251 | content[hover.range].to_string() | 254 | content[hover.range].to_string() |
252 | } | 255 | } |
253 | 256 | ||
257 | fn check_hover_no_result(fixture: &str) { | ||
258 | let (analysis, position) = analysis_and_position(fixture); | ||
259 | assert!(analysis.hover(position).unwrap().is_none()); | ||
260 | } | ||
261 | |||
254 | #[test] | 262 | #[test] |
255 | fn hover_shows_type_of_an_expression() { | 263 | fn hover_shows_type_of_an_expression() { |
256 | let (analysis, position) = single_file_with_position( | 264 | let (analysis, position) = single_file_with_position( |
@@ -755,6 +763,45 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
755 | } | 763 | } |
756 | 764 | ||
757 | #[test] | 765 | #[test] |
766 | fn test_hover_through_expr_in_macro_recursive() { | ||
767 | let hover_on = check_hover_result( | ||
768 | " | ||
769 | //- /lib.rs | ||
770 | macro_rules! id_deep { | ||
771 | ($($tt:tt)*) => { $($tt)* } | ||
772 | } | ||
773 | macro_rules! id { | ||
774 | ($($tt:tt)*) => { id_deep!($($tt)*) } | ||
775 | } | ||
776 | fn foo(bar:u32) { | ||
777 | let a = id!(ba<|>r); | ||
778 | } | ||
779 | ", | ||
780 | &["u32"], | ||
781 | ); | ||
782 | |||
783 | assert_eq!(hover_on, "bar") | ||
784 | } | ||
785 | |||
786 | #[test] | ||
787 | fn test_hover_through_literal_string_in_macro() { | ||
788 | // FIXME: Currently `hover::type_of` do not work inside | ||
789 | // macro expansion | ||
790 | check_hover_no_result( | ||
791 | r#" | ||
792 | //- /lib.rs | ||
793 | macro_rules! arr { | ||
794 | ($($tt:tt)*) => { [$($tt)*)] } | ||
795 | } | ||
796 | fn foo() { | ||
797 | let mastered_for_itunes = ""; | ||
798 | let _ = arr!("Tr<|>acks", &mastered_for_itunes); | ||
799 | } | ||
800 | "#, | ||
801 | ); | ||
802 | } | ||
803 | |||
804 | #[test] | ||
758 | fn test_hover_non_ascii_space_doc() { | 805 | fn test_hover_non_ascii_space_doc() { |
759 | check_hover_result( | 806 | check_hover_result( |
760 | " | 807 | " |
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs index 64a2dadc8..bf82b2a16 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/impls.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Crate, ImplBlock, SourceBinder}; | 3 | use hir::{Crate, ImplBlock, Semantics}; |
4 | use ra_db::SourceDatabase; | ||
5 | use ra_ide_db::RootDatabase; | 4 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | 5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; |
7 | 6 | ||
@@ -11,21 +10,21 @@ pub(crate) fn goto_implementation( | |||
11 | db: &RootDatabase, | 10 | db: &RootDatabase, |
12 | position: FilePosition, | 11 | position: FilePosition, |
13 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 12 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
14 | let parse = db.parse(position.file_id); | 13 | let sema = Semantics::new(db); |
15 | let syntax = parse.tree().syntax().clone(); | 14 | let source_file = sema.parse(position.file_id); |
16 | let mut sb = SourceBinder::new(db); | 15 | let syntax = source_file.syntax().clone(); |
17 | 16 | ||
18 | let krate = sb.to_module_def(position.file_id)?.krate(); | 17 | let krate = sema.to_module_def(position.file_id)?.krate(); |
19 | 18 | ||
20 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { | 19 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { |
21 | return Some(RangeInfo::new( | 20 | return Some(RangeInfo::new( |
22 | nominal_def.syntax().text_range(), | 21 | nominal_def.syntax().text_range(), |
23 | impls_for_def(&mut sb, position, &nominal_def, krate)?, | 22 | impls_for_def(&sema, &nominal_def, krate)?, |
24 | )); | 23 | )); |
25 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { | 24 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { |
26 | return Some(RangeInfo::new( | 25 | return Some(RangeInfo::new( |
27 | trait_def.syntax().text_range(), | 26 | trait_def.syntax().text_range(), |
28 | impls_for_trait(&mut sb, position, &trait_def, krate)?, | 27 | impls_for_trait(&sema, &trait_def, krate)?, |
29 | )); | 28 | )); |
30 | } | 29 | } |
31 | 30 | ||
@@ -33,49 +32,37 @@ pub(crate) fn goto_implementation( | |||
33 | } | 32 | } |
34 | 33 | ||
35 | fn impls_for_def( | 34 | fn impls_for_def( |
36 | sb: &mut SourceBinder<RootDatabase>, | 35 | sema: &Semantics<RootDatabase>, |
37 | position: FilePosition, | ||
38 | node: &ast::NominalDef, | 36 | node: &ast::NominalDef, |
39 | krate: Crate, | 37 | krate: Crate, |
40 | ) -> Option<Vec<NavigationTarget>> { | 38 | ) -> Option<Vec<NavigationTarget>> { |
41 | let ty = match node { | 39 | let ty = match node { |
42 | ast::NominalDef::StructDef(def) => { | 40 | ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db), |
43 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | 41 | ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db), |
44 | sb.to_def(src)?.ty(sb.db) | 42 | ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db), |
45 | } | ||
46 | ast::NominalDef::EnumDef(def) => { | ||
47 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | ||
48 | sb.to_def(src)?.ty(sb.db) | ||
49 | } | ||
50 | ast::NominalDef::UnionDef(def) => { | ||
51 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | ||
52 | sb.to_def(src)?.ty(sb.db) | ||
53 | } | ||
54 | }; | 43 | }; |
55 | 44 | ||
56 | let impls = ImplBlock::all_in_crate(sb.db, krate); | 45 | let impls = ImplBlock::all_in_crate(sema.db, krate); |
57 | 46 | ||
58 | Some( | 47 | Some( |
59 | impls | 48 | impls |
60 | .into_iter() | 49 | .into_iter() |
61 | .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sb.db))) | 50 | .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sema.db))) |
62 | .map(|imp| imp.to_nav(sb.db)) | 51 | .map(|imp| imp.to_nav(sema.db)) |
63 | .collect(), | 52 | .collect(), |
64 | ) | 53 | ) |
65 | } | 54 | } |
66 | 55 | ||
67 | fn impls_for_trait( | 56 | fn impls_for_trait( |
68 | sb: &mut SourceBinder<RootDatabase>, | 57 | sema: &Semantics<RootDatabase>, |
69 | position: FilePosition, | ||
70 | node: &ast::TraitDef, | 58 | node: &ast::TraitDef, |
71 | krate: Crate, | 59 | krate: Crate, |
72 | ) -> Option<Vec<NavigationTarget>> { | 60 | ) -> Option<Vec<NavigationTarget>> { |
73 | let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() }; | 61 | let tr = sema.to_def(node)?; |
74 | let tr = sb.to_def(src)?; | ||
75 | 62 | ||
76 | let impls = ImplBlock::for_trait(sb.db, krate, tr); | 63 | let impls = ImplBlock::for_trait(sema.db, krate, tr); |
77 | 64 | ||
78 | Some(impls.into_iter().map(|imp| imp.to_nav(sb.db)).collect()) | 65 | Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) |
79 | } | 66 | } |
80 | 67 | ||
81 | #[cfg(test)] | 68 | #[cfg(test)] |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index a484dfdeb..35e3f782d 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,12 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; | 3 | use hir::{Adt, HirDisplay, Semantics, Type}; |
4 | use once_cell::unsync::Lazy; | ||
5 | use ra_ide_db::RootDatabase; | 4 | use ra_ide_db::RootDatabase; |
6 | use ra_prof::profile; | 5 | use ra_prof::profile; |
7 | use ra_syntax::{ | 6 | use ra_syntax::{ |
8 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, | 7 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, |
9 | match_ast, SmolStr, SourceFile, SyntaxNode, TextRange, | 8 | match_ast, SmolStr, SyntaxNode, TextRange, |
10 | }; | 9 | }; |
11 | 10 | ||
12 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
@@ -27,42 +26,38 @@ pub struct InlayHint { | |||
27 | pub(crate) fn inlay_hints( | 26 | pub(crate) fn inlay_hints( |
28 | db: &RootDatabase, | 27 | db: &RootDatabase, |
29 | file_id: FileId, | 28 | file_id: FileId, |
30 | file: &SourceFile, | ||
31 | max_inlay_hint_length: Option<usize>, | 29 | max_inlay_hint_length: Option<usize>, |
32 | ) -> Vec<InlayHint> { | 30 | ) -> Vec<InlayHint> { |
33 | let mut sb = SourceBinder::new(db); | 31 | let sema = Semantics::new(db); |
32 | let file = sema.parse(file_id); | ||
34 | let mut res = Vec::new(); | 33 | let mut res = Vec::new(); |
35 | for node in file.syntax().descendants() { | 34 | for node in file.syntax().descendants() { |
36 | get_inlay_hints(&mut res, &mut sb, file_id, &node, max_inlay_hint_length); | 35 | get_inlay_hints(&mut res, &sema, &node, max_inlay_hint_length); |
37 | } | 36 | } |
38 | res | 37 | res |
39 | } | 38 | } |
40 | 39 | ||
41 | fn get_inlay_hints( | 40 | fn get_inlay_hints( |
42 | acc: &mut Vec<InlayHint>, | 41 | acc: &mut Vec<InlayHint>, |
43 | sb: &mut SourceBinder<RootDatabase>, | 42 | sema: &Semantics<RootDatabase>, |
44 | file_id: FileId, | ||
45 | node: &SyntaxNode, | 43 | node: &SyntaxNode, |
46 | max_inlay_hint_length: Option<usize>, | 44 | max_inlay_hint_length: Option<usize>, |
47 | ) -> Option<()> { | 45 | ) -> Option<()> { |
48 | let _p = profile("get_inlay_hints"); | 46 | let _p = profile("get_inlay_hints"); |
49 | let db = sb.db; | 47 | let db = sema.db; |
50 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); | ||
51 | match_ast! { | 48 | match_ast! { |
52 | match node { | 49 | match node { |
53 | ast::CallExpr(it) => { | 50 | ast::CallExpr(it) => { |
54 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 51 | get_param_name_hints(acc, sema, ast::Expr::from(it)); |
55 | }, | 52 | }, |
56 | ast::MethodCallExpr(it) => { | 53 | ast::MethodCallExpr(it) => { |
57 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 54 | get_param_name_hints(acc, sema, ast::Expr::from(it)); |
58 | }, | 55 | }, |
59 | ast::BindPat(it) => { | 56 | ast::BindPat(it) => { |
60 | if should_not_display_type_hint(&it) { | 57 | let pat = ast::Pat::from(it.clone()); |
61 | return None; | 58 | let ty = sema.type_of_pat(&pat)?; |
62 | } | 59 | |
63 | let pat = ast::Pat::from(it); | 60 | if should_not_display_type_hint(db, &it, &ty) { |
64 | let ty = analyzer.type_of_pat(db, &pat)?; | ||
65 | if ty.is_unknown() { | ||
66 | return None; | 61 | return None; |
67 | } | 62 | } |
68 | 63 | ||
@@ -80,7 +75,24 @@ fn get_inlay_hints( | |||
80 | Some(()) | 75 | Some(()) |
81 | } | 76 | } |
82 | 77 | ||
83 | fn should_not_display_type_hint(bind_pat: &ast::BindPat) -> bool { | 78 | fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool { |
79 | if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { | ||
80 | let pat_text = bind_pat.syntax().to_string(); | ||
81 | enum_data | ||
82 | .variants(db) | ||
83 | .into_iter() | ||
84 | .map(|variant| variant.name(db).to_string()) | ||
85 | .any(|enum_name| enum_name == pat_text) | ||
86 | } else { | ||
87 | false | ||
88 | } | ||
89 | } | ||
90 | |||
91 | fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool { | ||
92 | if pat_ty.is_unknown() { | ||
93 | return true; | ||
94 | } | ||
95 | |||
84 | for node in bind_pat.syntax().ancestors() { | 96 | for node in bind_pat.syntax().ancestors() { |
85 | match_ast! { | 97 | match_ast! { |
86 | match node { | 98 | match node { |
@@ -90,6 +102,17 @@ fn should_not_display_type_hint(bind_pat: &ast::BindPat) -> bool { | |||
90 | ast::Param(it) => { | 102 | ast::Param(it) => { |
91 | return it.ascribed_type().is_some() | 103 | return it.ascribed_type().is_some() |
92 | }, | 104 | }, |
105 | ast::MatchArm(_it) => { | ||
106 | return pat_is_enum_variant(db, bind_pat, pat_ty); | ||
107 | }, | ||
108 | ast::IfExpr(it) => { | ||
109 | return it.condition().and_then(|condition| condition.pat()).is_some() | ||
110 | && pat_is_enum_variant(db, bind_pat, pat_ty); | ||
111 | }, | ||
112 | ast::WhileExpr(it) => { | ||
113 | return it.condition().and_then(|condition| condition.pat()).is_some() | ||
114 | && pat_is_enum_variant(db, bind_pat, pat_ty); | ||
115 | }, | ||
93 | _ => (), | 116 | _ => (), |
94 | } | 117 | } |
95 | } | 118 | } |
@@ -99,8 +122,7 @@ fn should_not_display_type_hint(bind_pat: &ast::BindPat) -> bool { | |||
99 | 122 | ||
100 | fn get_param_name_hints( | 123 | fn get_param_name_hints( |
101 | acc: &mut Vec<InlayHint>, | 124 | acc: &mut Vec<InlayHint>, |
102 | db: &RootDatabase, | 125 | sema: &Semantics<RootDatabase>, |
103 | analyzer: &SourceAnalyzer, | ||
104 | expr: ast::Expr, | 126 | expr: ast::Expr, |
105 | ) -> Option<()> { | 127 | ) -> Option<()> { |
106 | let args = match &expr { | 128 | let args = match &expr { |
@@ -112,20 +134,19 @@ fn get_param_name_hints( | |||
112 | // we need args len to determine whether to skip or not the &self parameter | 134 | // we need args len to determine whether to skip or not the &self parameter |
113 | .collect::<Vec<_>>(); | 135 | .collect::<Vec<_>>(); |
114 | 136 | ||
115 | let fn_signature = get_fn_signature(db, analyzer, &expr)?; | 137 | let fn_signature = get_fn_signature(sema, &expr)?; |
116 | let n_params_to_skip = | 138 | let n_params_to_skip = |
117 | if fn_signature.has_self_param && fn_signature.parameter_names.len() > args.len() { | 139 | if fn_signature.has_self_param && fn_signature.parameter_names.len() > args.len() { |
118 | 1 | 140 | 1 |
119 | } else { | 141 | } else { |
120 | 0 | 142 | 0 |
121 | }; | 143 | }; |
122 | let parameters = fn_signature.parameter_names.iter().skip(n_params_to_skip); | 144 | let hints = fn_signature |
123 | 145 | .parameter_names | |
124 | let hints = parameters | 146 | .iter() |
147 | .skip(n_params_to_skip) | ||
125 | .zip(args) | 148 | .zip(args) |
126 | .filter(|(param, arg)| { | 149 | .filter(|(param, arg)| should_show_param_hint(&fn_signature, param, &arg)) |
127 | should_show_param_hint(&fn_signature, param, &arg.syntax().to_string()) | ||
128 | }) | ||
129 | .map(|(param_name, arg)| InlayHint { | 150 | .map(|(param_name, arg)| InlayHint { |
130 | range: arg.syntax().text_range(), | 151 | range: arg.syntax().text_range(), |
131 | kind: InlayKind::ParameterHint, | 152 | kind: InlayKind::ParameterHint, |
@@ -139,8 +160,9 @@ fn get_param_name_hints( | |||
139 | fn should_show_param_hint( | 160 | fn should_show_param_hint( |
140 | fn_signature: &FunctionSignature, | 161 | fn_signature: &FunctionSignature, |
141 | param_name: &str, | 162 | param_name: &str, |
142 | argument_string: &str, | 163 | argument: &ast::Expr, |
143 | ) -> bool { | 164 | ) -> bool { |
165 | let argument_string = argument.syntax().to_string(); | ||
144 | if param_name.is_empty() || argument_string.ends_with(param_name) { | 166 | if param_name.is_empty() || argument_string.ends_with(param_name) { |
145 | return false; | 167 | return false; |
146 | } | 168 | } |
@@ -158,28 +180,26 @@ fn should_show_param_hint( | |||
158 | true | 180 | true |
159 | } | 181 | } |
160 | 182 | ||
161 | fn get_fn_signature( | 183 | fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { |
162 | db: &RootDatabase, | ||
163 | analyzer: &SourceAnalyzer, | ||
164 | expr: &ast::Expr, | ||
165 | ) -> Option<FunctionSignature> { | ||
166 | match expr { | 184 | match expr { |
167 | ast::Expr::CallExpr(expr) => { | 185 | ast::Expr::CallExpr(expr) => { |
168 | // FIXME: Type::as_callable is broken for closures | 186 | // FIXME: Type::as_callable is broken for closures |
169 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 187 | let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; |
170 | match callable_def { | 188 | match callable_def { |
171 | hir::CallableDef::FunctionId(it) => { | 189 | hir::CallableDef::FunctionId(it) => { |
172 | Some(FunctionSignature::from_hir(db, it.into())) | 190 | Some(FunctionSignature::from_hir(sema.db, it.into())) |
191 | } | ||
192 | hir::CallableDef::StructId(it) => { | ||
193 | FunctionSignature::from_struct(sema.db, it.into()) | ||
173 | } | 194 | } |
174 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), | ||
175 | hir::CallableDef::EnumVariantId(it) => { | 195 | hir::CallableDef::EnumVariantId(it) => { |
176 | FunctionSignature::from_enum_variant(db, it.into()) | 196 | FunctionSignature::from_enum_variant(sema.db, it.into()) |
177 | } | 197 | } |
178 | } | 198 | } |
179 | } | 199 | } |
180 | ast::Expr::MethodCallExpr(expr) => { | 200 | ast::Expr::MethodCallExpr(expr) => { |
181 | let fn_def = analyzer.resolve_method_call(&expr)?; | 201 | let fn_def = sema.resolve_method_call(&expr)?; |
182 | Some(FunctionSignature::from_hir(db, fn_def)) | 202 | Some(FunctionSignature::from_hir(sema.db, fn_def)) |
183 | } | 203 | } |
184 | _ => None, | 204 | _ => None, |
185 | } | 205 | } |
@@ -440,75 +460,77 @@ struct Test { | |||
440 | b: u8, | 460 | b: u8, |
441 | } | 461 | } |
442 | 462 | ||
463 | use CustomOption::*; | ||
464 | |||
443 | fn main() { | 465 | fn main() { |
444 | let test = CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }); | 466 | let test = Some(Test { a: Some(3), b: 1 }); |
445 | if let CustomOption::None = &test {}; | 467 | if let None = &test {}; |
446 | if let test = &test {}; | 468 | if let test = &test {}; |
447 | if let CustomOption::Some(test) = &test {}; | 469 | if let Some(test) = &test {}; |
448 | if let CustomOption::Some(Test { a, b }) = &test {}; | 470 | if let Some(Test { a, b }) = &test {}; |
449 | if let CustomOption::Some(Test { a: x, b: y }) = &test {}; | 471 | if let Some(Test { a: x, b: y }) = &test {}; |
450 | if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; | 472 | if let Some(Test { a: Some(x), b: y }) = &test {}; |
451 | if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; | 473 | if let Some(Test { a: None, b: y }) = &test {}; |
452 | if let CustomOption::Some(Test { b: y, .. }) = &test {}; | 474 | if let Some(Test { b: y, .. }) = &test {}; |
453 | 475 | ||
454 | if test == CustomOption::None {} | 476 | if test == None {} |
455 | }"#, | 477 | }"#, |
456 | ); | 478 | ); |
457 | 479 | ||
458 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 480 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
459 | [ | 481 | [ |
460 | InlayHint { | 482 | InlayHint { |
461 | range: [166; 170), | 483 | range: [188; 192), |
462 | kind: TypeHint, | 484 | kind: TypeHint, |
463 | label: "CustomOption<Test>", | 485 | label: "CustomOption<Test>", |
464 | }, | 486 | }, |
465 | InlayHint { | 487 | InlayHint { |
466 | range: [287; 291), | 488 | range: [267; 271), |
467 | kind: TypeHint, | 489 | kind: TypeHint, |
468 | label: "&CustomOption<Test>", | 490 | label: "&CustomOption<Test>", |
469 | }, | 491 | }, |
470 | InlayHint { | 492 | InlayHint { |
471 | range: [334; 338), | 493 | range: [300; 304), |
472 | kind: TypeHint, | 494 | kind: TypeHint, |
473 | label: "&Test", | 495 | label: "&Test", |
474 | }, | 496 | }, |
475 | InlayHint { | 497 | InlayHint { |
476 | range: [389; 390), | 498 | range: [341; 342), |
477 | kind: TypeHint, | 499 | kind: TypeHint, |
478 | label: "&CustomOption<u32>", | 500 | label: "&CustomOption<u32>", |
479 | }, | 501 | }, |
480 | InlayHint { | 502 | InlayHint { |
481 | range: [392; 393), | 503 | range: [344; 345), |
482 | kind: TypeHint, | 504 | kind: TypeHint, |
483 | label: "&u8", | 505 | label: "&u8", |
484 | }, | 506 | }, |
485 | InlayHint { | 507 | InlayHint { |
486 | range: [449; 450), | 508 | range: [387; 388), |
487 | kind: TypeHint, | 509 | kind: TypeHint, |
488 | label: "&CustomOption<u32>", | 510 | label: "&CustomOption<u32>", |
489 | }, | 511 | }, |
490 | InlayHint { | 512 | InlayHint { |
491 | range: [455; 456), | 513 | range: [393; 394), |
492 | kind: TypeHint, | 514 | kind: TypeHint, |
493 | label: "&u8", | 515 | label: "&u8", |
494 | }, | 516 | }, |
495 | InlayHint { | 517 | InlayHint { |
496 | range: [531; 532), | 518 | range: [441; 442), |
497 | kind: TypeHint, | 519 | kind: TypeHint, |
498 | label: "&u32", | 520 | label: "&u32", |
499 | }, | 521 | }, |
500 | InlayHint { | 522 | InlayHint { |
501 | range: [538; 539), | 523 | range: [448; 449), |
502 | kind: TypeHint, | 524 | kind: TypeHint, |
503 | label: "&u8", | 525 | label: "&u8", |
504 | }, | 526 | }, |
505 | InlayHint { | 527 | InlayHint { |
506 | range: [618; 619), | 528 | range: [500; 501), |
507 | kind: TypeHint, | 529 | kind: TypeHint, |
508 | label: "&u8", | 530 | label: "&u8", |
509 | }, | 531 | }, |
510 | InlayHint { | 532 | InlayHint { |
511 | range: [675; 676), | 533 | range: [543; 544), |
512 | kind: TypeHint, | 534 | kind: TypeHint, |
513 | label: "&u8", | 535 | label: "&u8", |
514 | }, | 536 | }, |
@@ -533,75 +555,77 @@ struct Test { | |||
533 | b: u8, | 555 | b: u8, |
534 | } | 556 | } |
535 | 557 | ||
558 | use CustomOption::*; | ||
559 | |||
536 | fn main() { | 560 | fn main() { |
537 | let test = CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }); | 561 | let test = Some(Test { a: Some(3), b: 1 }); |
538 | while let CustomOption::None = &test {}; | 562 | while let None = &test {}; |
539 | while let test = &test {}; | 563 | while let test = &test {}; |
540 | while let CustomOption::Some(test) = &test {}; | 564 | while let Some(test) = &test {}; |
541 | while let CustomOption::Some(Test { a, b }) = &test {}; | 565 | while let Some(Test { a, b }) = &test {}; |
542 | while let CustomOption::Some(Test { a: x, b: y }) = &test {}; | 566 | while let Some(Test { a: x, b: y }) = &test {}; |
543 | while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; | 567 | while let Some(Test { a: Some(x), b: y }) = &test {}; |
544 | while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; | 568 | while let Some(Test { a: None, b: y }) = &test {}; |
545 | while let CustomOption::Some(Test { b: y, .. }) = &test {}; | 569 | while let Some(Test { b: y, .. }) = &test {}; |
546 | 570 | ||
547 | while test == CustomOption::None {} | 571 | while test == None {} |
548 | }"#, | 572 | }"#, |
549 | ); | 573 | ); |
550 | 574 | ||
551 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 575 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
552 | [ | 576 | [ |
553 | InlayHint { | 577 | InlayHint { |
554 | range: [166; 170), | 578 | range: [188; 192), |
555 | kind: TypeHint, | 579 | kind: TypeHint, |
556 | label: "CustomOption<Test>", | 580 | label: "CustomOption<Test>", |
557 | }, | 581 | }, |
558 | InlayHint { | 582 | InlayHint { |
559 | range: [293; 297), | 583 | range: [273; 277), |
560 | kind: TypeHint, | 584 | kind: TypeHint, |
561 | label: "&CustomOption<Test>", | 585 | label: "&CustomOption<Test>", |
562 | }, | 586 | }, |
563 | InlayHint { | 587 | InlayHint { |
564 | range: [343; 347), | 588 | range: [309; 313), |
565 | kind: TypeHint, | 589 | kind: TypeHint, |
566 | label: "&Test", | 590 | label: "&Test", |
567 | }, | 591 | }, |
568 | InlayHint { | 592 | InlayHint { |
569 | range: [401; 402), | 593 | range: [353; 354), |
570 | kind: TypeHint, | 594 | kind: TypeHint, |
571 | label: "&CustomOption<u32>", | 595 | label: "&CustomOption<u32>", |
572 | }, | 596 | }, |
573 | InlayHint { | 597 | InlayHint { |
574 | range: [404; 405), | 598 | range: [356; 357), |
575 | kind: TypeHint, | 599 | kind: TypeHint, |
576 | label: "&u8", | 600 | label: "&u8", |
577 | }, | 601 | }, |
578 | InlayHint { | 602 | InlayHint { |
579 | range: [464; 465), | 603 | range: [402; 403), |
580 | kind: TypeHint, | 604 | kind: TypeHint, |
581 | label: "&CustomOption<u32>", | 605 | label: "&CustomOption<u32>", |
582 | }, | 606 | }, |
583 | InlayHint { | 607 | InlayHint { |
584 | range: [470; 471), | 608 | range: [408; 409), |
585 | kind: TypeHint, | 609 | kind: TypeHint, |
586 | label: "&u8", | 610 | label: "&u8", |
587 | }, | 611 | }, |
588 | InlayHint { | 612 | InlayHint { |
589 | range: [549; 550), | 613 | range: [459; 460), |
590 | kind: TypeHint, | 614 | kind: TypeHint, |
591 | label: "&u32", | 615 | label: "&u32", |
592 | }, | 616 | }, |
593 | InlayHint { | 617 | InlayHint { |
594 | range: [556; 557), | 618 | range: [466; 467), |
595 | kind: TypeHint, | 619 | kind: TypeHint, |
596 | label: "&u8", | 620 | label: "&u8", |
597 | }, | 621 | }, |
598 | InlayHint { | 622 | InlayHint { |
599 | range: [639; 640), | 623 | range: [521; 522), |
600 | kind: TypeHint, | 624 | kind: TypeHint, |
601 | label: "&u8", | 625 | label: "&u8", |
602 | }, | 626 | }, |
603 | InlayHint { | 627 | InlayHint { |
604 | range: [699; 700), | 628 | range: [567; 568), |
605 | kind: TypeHint, | 629 | kind: TypeHint, |
606 | label: "&u8", | 630 | label: "&u8", |
607 | }, | 631 | }, |
@@ -626,16 +650,18 @@ struct Test { | |||
626 | b: u8, | 650 | b: u8, |
627 | } | 651 | } |
628 | 652 | ||
653 | use CustomOption::*; | ||
654 | |||
629 | fn main() { | 655 | fn main() { |
630 | match CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }) { | 656 | match Some(Test { a: Some(3), b: 1 }) { |
631 | CustomOption::None => (), | 657 | None => (), |
632 | test => (), | 658 | test => (), |
633 | CustomOption::Some(test) => (), | 659 | Some(test) => (), |
634 | CustomOption::Some(Test { a, b }) => (), | 660 | Some(Test { a, b }) => (), |
635 | CustomOption::Some(Test { a: x, b: y }) => (), | 661 | Some(Test { a: x, b: y }) => (), |
636 | CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) => (), | 662 | Some(Test { a: Some(x), b: y }) => (), |
637 | CustomOption::Some(Test { a: CustomOption::None, b: y }) => (), | 663 | Some(Test { a: None, b: y }) => (), |
638 | CustomOption::Some(Test { b: y, .. }) => (), | 664 | Some(Test { b: y, .. }) => (), |
639 | _ => {} | 665 | _ => {} |
640 | } | 666 | } |
641 | }"#, | 667 | }"#, |
@@ -644,52 +670,52 @@ fn main() { | |||
644 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 670 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
645 | [ | 671 | [ |
646 | InlayHint { | 672 | InlayHint { |
647 | range: [272; 276), | 673 | range: [252; 256), |
648 | kind: TypeHint, | 674 | kind: TypeHint, |
649 | label: "CustomOption<Test>", | 675 | label: "CustomOption<Test>", |
650 | }, | 676 | }, |
651 | InlayHint { | 677 | InlayHint { |
652 | range: [311; 315), | 678 | range: [277; 281), |
653 | kind: TypeHint, | 679 | kind: TypeHint, |
654 | label: "Test", | 680 | label: "Test", |
655 | }, | 681 | }, |
656 | InlayHint { | 682 | InlayHint { |
657 | range: [358; 359), | 683 | range: [310; 311), |
658 | kind: TypeHint, | 684 | kind: TypeHint, |
659 | label: "CustomOption<u32>", | 685 | label: "CustomOption<u32>", |
660 | }, | 686 | }, |
661 | InlayHint { | 687 | InlayHint { |
662 | range: [361; 362), | 688 | range: [313; 314), |
663 | kind: TypeHint, | 689 | kind: TypeHint, |
664 | label: "u8", | 690 | label: "u8", |
665 | }, | 691 | }, |
666 | InlayHint { | 692 | InlayHint { |
667 | range: [410; 411), | 693 | range: [348; 349), |
668 | kind: TypeHint, | 694 | kind: TypeHint, |
669 | label: "CustomOption<u32>", | 695 | label: "CustomOption<u32>", |
670 | }, | 696 | }, |
671 | InlayHint { | 697 | InlayHint { |
672 | range: [416; 417), | 698 | range: [354; 355), |
673 | kind: TypeHint, | 699 | kind: TypeHint, |
674 | label: "u8", | 700 | label: "u8", |
675 | }, | 701 | }, |
676 | InlayHint { | 702 | InlayHint { |
677 | range: [484; 485), | 703 | range: [394; 395), |
678 | kind: TypeHint, | 704 | kind: TypeHint, |
679 | label: "u32", | 705 | label: "u32", |
680 | }, | 706 | }, |
681 | InlayHint { | 707 | InlayHint { |
682 | range: [491; 492), | 708 | range: [401; 402), |
683 | kind: TypeHint, | 709 | kind: TypeHint, |
684 | label: "u8", | 710 | label: "u8", |
685 | }, | 711 | }, |
686 | InlayHint { | 712 | InlayHint { |
687 | range: [563; 564), | 713 | range: [445; 446), |
688 | kind: TypeHint, | 714 | kind: TypeHint, |
689 | label: "u8", | 715 | label: "u8", |
690 | }, | 716 | }, |
691 | InlayHint { | 717 | InlayHint { |
692 | range: [612; 613), | 718 | range: [480; 481), |
693 | kind: TypeHint, | 719 | kind: TypeHint, |
694 | label: "u8", | 720 | label: "u8", |
695 | }, | 721 | }, |
@@ -743,6 +769,7 @@ enum CustomOption<T> { | |||
743 | None, | 769 | None, |
744 | Some(T), | 770 | Some(T), |
745 | } | 771 | } |
772 | use CustomOption::*; | ||
746 | 773 | ||
747 | struct FileId {} | 774 | struct FileId {} |
748 | struct SmolStr {} | 775 | struct SmolStr {} |
@@ -791,11 +818,11 @@ fn main() { | |||
791 | Test::from_syntax( | 818 | Test::from_syntax( |
792 | FileId {}, | 819 | FileId {}, |
793 | "impl".into(), | 820 | "impl".into(), |
794 | CustomOption::None, | 821 | None, |
795 | TextRange {}, | 822 | TextRange {}, |
796 | SyntaxKind {}, | 823 | SyntaxKind {}, |
797 | CustomOption::None, | 824 | None, |
798 | CustomOption::None, | 825 | None, |
799 | ); | 826 | ); |
800 | }"#, | 827 | }"#, |
801 | ); | 828 | ); |
@@ -803,77 +830,77 @@ fn main() { | |||
803 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 830 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
804 | [ | 831 | [ |
805 | InlayHint { | 832 | InlayHint { |
806 | range: [777; 788), | 833 | range: [798; 809), |
807 | kind: TypeHint, | 834 | kind: TypeHint, |
808 | label: "i32", | 835 | label: "i32", |
809 | }, | 836 | }, |
810 | InlayHint { | 837 | InlayHint { |
811 | range: [821; 822), | 838 | range: [842; 843), |
812 | kind: ParameterHint, | 839 | kind: ParameterHint, |
813 | label: "foo", | 840 | label: "foo", |
814 | }, | 841 | }, |
815 | InlayHint { | 842 | InlayHint { |
816 | range: [824; 825), | 843 | range: [845; 846), |
817 | kind: ParameterHint, | 844 | kind: ParameterHint, |
818 | label: "bar", | 845 | label: "bar", |
819 | }, | 846 | }, |
820 | InlayHint { | 847 | InlayHint { |
821 | range: [827; 834), | 848 | range: [848; 855), |
822 | kind: ParameterHint, | 849 | kind: ParameterHint, |
823 | label: "msg", | 850 | label: "msg", |
824 | }, | 851 | }, |
825 | InlayHint { | 852 | InlayHint { |
826 | range: [839; 850), | 853 | range: [860; 871), |
827 | kind: ParameterHint, | 854 | kind: ParameterHint, |
828 | label: "last", | 855 | label: "last", |
829 | }, | 856 | }, |
830 | InlayHint { | 857 | InlayHint { |
831 | range: [893; 896), | 858 | range: [914; 917), |
832 | kind: ParameterHint, | 859 | kind: ParameterHint, |
833 | label: "param", | 860 | label: "param", |
834 | }, | 861 | }, |
835 | InlayHint { | 862 | InlayHint { |
836 | range: [916; 918), | 863 | range: [937; 939), |
837 | kind: ParameterHint, | 864 | kind: ParameterHint, |
838 | label: "&self", | 865 | label: "&self", |
839 | }, | 866 | }, |
840 | InlayHint { | 867 | InlayHint { |
841 | range: [920; 924), | 868 | range: [941; 945), |
842 | kind: ParameterHint, | 869 | kind: ParameterHint, |
843 | label: "param", | 870 | label: "param", |
844 | }, | 871 | }, |
845 | InlayHint { | 872 | InlayHint { |
846 | range: [959; 968), | 873 | range: [980; 989), |
847 | kind: ParameterHint, | 874 | kind: ParameterHint, |
848 | label: "file_id", | 875 | label: "file_id", |
849 | }, | 876 | }, |
850 | InlayHint { | 877 | InlayHint { |
851 | range: [978; 991), | 878 | range: [999; 1012), |
852 | kind: ParameterHint, | 879 | kind: ParameterHint, |
853 | label: "name", | 880 | label: "name", |
854 | }, | 881 | }, |
855 | InlayHint { | 882 | InlayHint { |
856 | range: [1001; 1019), | 883 | range: [1022; 1026), |
857 | kind: ParameterHint, | 884 | kind: ParameterHint, |
858 | label: "focus_range", | 885 | label: "focus_range", |
859 | }, | 886 | }, |
860 | InlayHint { | 887 | InlayHint { |
861 | range: [1029; 1041), | 888 | range: [1036; 1048), |
862 | kind: ParameterHint, | 889 | kind: ParameterHint, |
863 | label: "full_range", | 890 | label: "full_range", |
864 | }, | 891 | }, |
865 | InlayHint { | 892 | InlayHint { |
866 | range: [1051; 1064), | 893 | range: [1058; 1071), |
867 | kind: ParameterHint, | 894 | kind: ParameterHint, |
868 | label: "kind", | 895 | label: "kind", |
869 | }, | 896 | }, |
870 | InlayHint { | 897 | InlayHint { |
871 | range: [1074; 1092), | 898 | range: [1081; 1085), |
872 | kind: ParameterHint, | 899 | kind: ParameterHint, |
873 | label: "docs", | 900 | label: "docs", |
874 | }, | 901 | }, |
875 | InlayHint { | 902 | InlayHint { |
876 | range: [1102; 1120), | 903 | range: [1095; 1099), |
877 | kind: ParameterHint, | 904 | kind: ParameterHint, |
878 | label: "description", | 905 | label: "description", |
879 | }, | 906 | }, |
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index 01fb32b3d..7d70dab9c 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -228,6 +228,31 @@ fn foo() { | |||
228 | } | 228 | } |
229 | 229 | ||
230 | #[test] | 230 | #[test] |
231 | fn test_join_lines_diverging_block() { | ||
232 | let before = r" | ||
233 | fn foo() { | ||
234 | loop { | ||
235 | match x { | ||
236 | 92 => <|>{ | ||
237 | continue; | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | "; | ||
243 | let after = r" | ||
244 | fn foo() { | ||
245 | loop { | ||
246 | match x { | ||
247 | 92 => <|>continue, | ||
248 | } | ||
249 | } | ||
250 | } | ||
251 | "; | ||
252 | check_join_lines(before, after); | ||
253 | } | ||
254 | |||
255 | #[test] | ||
231 | fn join_lines_adds_comma_for_block_in_match_arm() { | 256 | fn join_lines_adds_comma_for_block_in_match_arm() { |
232 | check_join_lines( | 257 | check_join_lines( |
233 | r" | 258 | r" |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index f86f98be7..d509de14e 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -35,7 +35,6 @@ mod typing; | |||
35 | mod matching_brace; | 35 | mod matching_brace; |
36 | mod display; | 36 | mod display; |
37 | mod inlay_hints; | 37 | mod inlay_hints; |
38 | mod expand; | ||
39 | mod expand_macro; | 38 | mod expand_macro; |
40 | mod ssr; | 39 | mod ssr; |
41 | 40 | ||
@@ -75,7 +74,9 @@ pub use crate::{ | |||
75 | runnables::{Runnable, RunnableKind, TestId}, | 74 | runnables::{Runnable, RunnableKind, TestId}, |
76 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 75 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
77 | ssr::SsrError, | 76 | ssr::SsrError, |
78 | syntax_highlighting::HighlightedRange, | 77 | syntax_highlighting::{ |
78 | Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange, | ||
79 | }, | ||
79 | }; | 80 | }; |
80 | 81 | ||
81 | pub use hir::Documentation; | 82 | pub use hir::Documentation; |
@@ -319,9 +320,7 @@ impl Analysis { | |||
319 | file_id: FileId, | 320 | file_id: FileId, |
320 | max_inlay_hint_length: Option<usize>, | 321 | max_inlay_hint_length: Option<usize>, |
321 | ) -> Cancelable<Vec<InlayHint>> { | 322 | ) -> Cancelable<Vec<InlayHint>> { |
322 | self.with_db(|db| { | 323 | self.with_db(|db| inlay_hints::inlay_hints(db, file_id, max_inlay_hint_length)) |
323 | inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree(), max_inlay_hint_length) | ||
324 | }) | ||
325 | } | 324 | } |
326 | 325 | ||
327 | /// Returns the set of folding ranges. | 326 | /// Returns the set of folding ranges. |
@@ -425,9 +424,14 @@ impl Analysis { | |||
425 | self.with_db(|db| runnables::runnables(db, file_id)) | 424 | self.with_db(|db| runnables::runnables(db, file_id)) |
426 | } | 425 | } |
427 | 426 | ||
428 | /// Computes syntax highlighting for the given file. | 427 | /// Computes syntax highlighting for the given file |
429 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { | 428 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { |
430 | self.with_db(|db| syntax_highlighting::highlight(db, file_id)) | 429 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None)) |
430 | } | ||
431 | |||
432 | /// Computes syntax highlighting for the given file range. | ||
433 | pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { | ||
434 | self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range))) | ||
431 | } | 435 | } |
432 | 436 | ||
433 | /// Computes syntax highlighting for the given file. | 437 | /// Computes syntax highlighting for the given file. |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index bcb67e373..7b8b727b4 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs | |||
@@ -11,4 +11,5 @@ test_utils::marks!( | |||
11 | call_info_bad_offset | 11 | call_info_bad_offset |
12 | dont_complete_current_use | 12 | dont_complete_current_use |
13 | test_resolve_parent_module_on_module_decl | 13 | test_resolve_parent_module_on_module_decl |
14 | search_filters_by_range | ||
14 | ); | 15 | ); |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index af14d6ab3..2c4bdb039 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; | 3 | use hir::Semantics; |
4 | use ra_db::{CrateId, FileId, FilePosition}; | ||
4 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::{ | 6 | use ra_syntax::{ |
6 | algo::find_node_at_offset, | 7 | algo::find_node_at_offset, |
@@ -13,10 +14,10 @@ use crate::NavigationTarget; | |||
13 | /// This returns `Vec` because a module may be included from several places. We | 14 | /// This returns `Vec` because a module may be included from several places. We |
14 | /// don't handle this case yet though, so the Vec has length at most one. | 15 | /// don't handle this case yet though, so the Vec has length at most one. |
15 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { | 16 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { |
16 | let mut sb = hir::SourceBinder::new(db); | 17 | let sema = Semantics::new(db); |
17 | let parse = db.parse(position.file_id); | 18 | let source_file = sema.parse(position.file_id); |
18 | 19 | ||
19 | let mut module = find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset); | 20 | let mut module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset); |
20 | 21 | ||
21 | // If cursor is literally on `mod foo`, go to the grandpa. | 22 | // If cursor is literally on `mod foo`, go to the grandpa. |
22 | if let Some(m) = &module { | 23 | if let Some(m) = &module { |
@@ -30,8 +31,8 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
30 | } | 31 | } |
31 | 32 | ||
32 | let module = match module { | 33 | let module = match module { |
33 | Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)), | 34 | Some(module) => sema.to_def(&module), |
34 | None => sb.to_module_def(position.file_id), | 35 | None => sema.to_module_def(position.file_id), |
35 | }; | 36 | }; |
36 | let module = match module { | 37 | let module = match module { |
37 | None => return Vec::new(), | 38 | None => return Vec::new(), |
@@ -43,8 +44,8 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
43 | 44 | ||
44 | /// Returns `Vec` for the same reason as `parent_module` | 45 | /// Returns `Vec` for the same reason as `parent_module` |
45 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | 46 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { |
46 | let mut sb = hir::SourceBinder::new(db); | 47 | let sema = Semantics::new(db); |
47 | let module = match sb.to_module_def(file_id) { | 48 | let module = match sema.to_module_def(file_id) { |
48 | Some(it) => it, | 49 | Some(it) => it, |
49 | None => return Vec::new(), | 50 | None => return Vec::new(), |
50 | }; | 51 | }; |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index aadc2dbcb..baa8a4d29 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -13,25 +13,22 @@ mod classify; | |||
13 | mod rename; | 13 | mod rename; |
14 | mod search_scope; | 14 | mod search_scope; |
15 | 15 | ||
16 | use crate::expand::descend_into_macros_with_analyzer; | 16 | use hir::Semantics; |
17 | use hir::{InFile, SourceBinder}; | ||
18 | use once_cell::unsync::Lazy; | 17 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 18 | use ra_db::SourceDatabaseExt; |
20 | use ra_ide_db::RootDatabase; | 19 | use ra_ide_db::RootDatabase; |
21 | use ra_prof::profile; | 20 | use ra_prof::profile; |
22 | use ra_syntax::{ | 21 | use ra_syntax::{ |
23 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
24 | ast::{self, NameOwner}, | 23 | ast::{self, NameOwner}, |
25 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, | 24 | match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, |
26 | }; | 25 | }; |
26 | use test_utils::tested_by; | ||
27 | 27 | ||
28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
29 | 29 | ||
30 | pub(crate) use self::{ | 30 | pub(crate) use self::{classify::classify_name_ref, rename::rename}; |
31 | classify::{classify_name, classify_name_ref}, | 31 | pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition}; |
32 | rename::rename, | ||
33 | }; | ||
34 | pub(crate) use ra_ide_db::defs::NameDefinition; | ||
35 | 32 | ||
36 | pub use self::search_scope::SearchScope; | 33 | pub use self::search_scope::SearchScope; |
37 | 34 | ||
@@ -114,8 +111,8 @@ pub(crate) fn find_all_refs( | |||
114 | position: FilePosition, | 111 | position: FilePosition, |
115 | search_scope: Option<SearchScope>, | 112 | search_scope: Option<SearchScope>, |
116 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 113 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
117 | let parse = db.parse(position.file_id); | 114 | let sema = Semantics::new(db); |
118 | let syntax = parse.tree().syntax().clone(); | 115 | let syntax = sema.parse(position.file_id).syntax().clone(); |
119 | 116 | ||
120 | let (opt_name, search_kind) = | 117 | let (opt_name, search_kind) = |
121 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { | 118 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { |
@@ -124,7 +121,7 @@ pub(crate) fn find_all_refs( | |||
124 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) | 121 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) |
125 | }; | 122 | }; |
126 | 123 | ||
127 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?; | 124 | let RangeInfo { range, info: (name, def) } = find_name(&sema, &syntax, position, opt_name)?; |
128 | let declaration = def.try_to_nav(db)?; | 125 | let declaration = def.try_to_nav(db)?; |
129 | 126 | ||
130 | let search_scope = { | 127 | let search_scope = { |
@@ -152,19 +149,18 @@ pub(crate) fn find_all_refs( | |||
152 | } | 149 | } |
153 | 150 | ||
154 | fn find_name( | 151 | fn find_name( |
155 | db: &RootDatabase, | 152 | sema: &Semantics<RootDatabase>, |
156 | syntax: &SyntaxNode, | 153 | syntax: &SyntaxNode, |
157 | position: FilePosition, | 154 | position: FilePosition, |
158 | opt_name: Option<ast::Name>, | 155 | opt_name: Option<ast::Name>, |
159 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 156 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
160 | let mut sb = SourceBinder::new(db); | ||
161 | if let Some(name) = opt_name { | 157 | if let Some(name) = opt_name { |
162 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; | 158 | let def = classify_name(sema, &name)?; |
163 | let range = name.syntax().text_range(); | 159 | let range = name.syntax().text_range(); |
164 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 160 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
165 | } | 161 | } |
166 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | 162 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
167 | let def = classify_name_ref(&mut sb, InFile::new(position.file_id.into(), &name_ref))?; | 163 | let def = classify_name_ref(sema, &name_ref)?; |
168 | let range = name_ref.syntax().text_range(); | 164 | let range = name_ref.syntax().text_range(); |
169 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) | 165 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) |
170 | } | 166 | } |
@@ -182,64 +178,53 @@ fn process_definition( | |||
182 | 178 | ||
183 | for (file_id, search_range) in scope { | 179 | for (file_id, search_range) in scope { |
184 | let text = db.file_text(file_id); | 180 | let text = db.file_text(file_id); |
181 | let search_range = | ||
182 | search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); | ||
185 | 183 | ||
186 | let parse = Lazy::new(|| SourceFile::parse(&text)); | 184 | let sema = Semantics::new(db); |
187 | let mut sb = Lazy::new(|| SourceBinder::new(db)); | 185 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); |
188 | let mut analyzer = None; | ||
189 | 186 | ||
190 | for (idx, _) in text.match_indices(pat) { | 187 | for (idx, _) in text.match_indices(pat) { |
191 | let offset = TextUnit::from_usize(idx); | 188 | let offset = TextUnit::from_usize(idx); |
189 | if !search_range.contains_inclusive(offset) { | ||
190 | tested_by!(search_filters_by_range); | ||
191 | continue; | ||
192 | } | ||
192 | 193 | ||
193 | let (name_ref, range) = if let Some(name_ref) = | 194 | let name_ref = |
194 | find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset) | 195 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&tree, offset) { |
195 | { | 196 | name_ref |
196 | let range = name_ref.syntax().text_range(); | ||
197 | (InFile::new(file_id.into(), name_ref), range) | ||
198 | } else { | ||
199 | // Handle macro token cases | ||
200 | let t = match parse.tree().syntax().token_at_offset(offset) { | ||
201 | TokenAtOffset::None => continue, | ||
202 | TokenAtOffset::Single(t) => t, | ||
203 | TokenAtOffset::Between(_, t) => t, | ||
204 | }; | ||
205 | let range = t.text_range(); | ||
206 | let analyzer = analyzer.get_or_insert_with(|| { | ||
207 | sb.analyze(InFile::new(file_id.into(), parse.tree().syntax()), None) | ||
208 | }); | ||
209 | let expanded = descend_into_macros_with_analyzer( | ||
210 | db, | ||
211 | &analyzer, | ||
212 | InFile::new(file_id.into(), t), | ||
213 | ); | ||
214 | if let Some(token) = ast::NameRef::cast(expanded.value.parent()) { | ||
215 | (expanded.with_value(token), range) | ||
216 | } else { | 197 | } else { |
217 | continue; | 198 | // Handle macro token cases |
218 | } | 199 | let token = match tree.token_at_offset(offset) { |
219 | }; | 200 | TokenAtOffset::None => continue, |
201 | TokenAtOffset::Single(t) => t, | ||
202 | TokenAtOffset::Between(_, t) => t, | ||
203 | }; | ||
204 | let expanded = sema.descend_into_macros(token); | ||
205 | match ast::NameRef::cast(expanded.parent()) { | ||
206 | Some(name_ref) => name_ref, | ||
207 | _ => continue, | ||
208 | } | ||
209 | }; | ||
220 | 210 | ||
221 | if let Some(search_range) = search_range { | ||
222 | if !range.is_subrange(&search_range) { | ||
223 | continue; | ||
224 | } | ||
225 | } | ||
226 | // FIXME: reuse sb | 211 | // FIXME: reuse sb |
227 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 | 212 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 |
228 | 213 | ||
229 | if let Some(d) = classify_name_ref(&mut sb, name_ref.as_ref()) { | 214 | if let Some(d) = classify_name_ref(&sema, &name_ref) { |
230 | if d == def { | 215 | if d == def { |
231 | let kind = if is_record_lit_name_ref(&name_ref.value) | 216 | let kind = |
232 | || is_call_expr_name_ref(&name_ref.value) | 217 | if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { |
233 | { | 218 | ReferenceKind::StructLiteral |
234 | ReferenceKind::StructLiteral | 219 | } else { |
235 | } else { | 220 | ReferenceKind::Other |
236 | ReferenceKind::Other | 221 | }; |
237 | }; | 222 | |
238 | 223 | let file_range = sema.original_range(name_ref.syntax()); | |
239 | refs.push(Reference { | 224 | refs.push(Reference { |
240 | file_range: FileRange { file_id, range }, | 225 | file_range, |
241 | kind, | 226 | kind, |
242 | access: reference_access(&d, &name_ref.value), | 227 | access: reference_access(&d, &name_ref), |
243 | }); | 228 | }); |
244 | } | 229 | } |
245 | } | 230 | } |
@@ -348,6 +333,8 @@ fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | |||
348 | 333 | ||
349 | #[cfg(test)] | 334 | #[cfg(test)] |
350 | mod tests { | 335 | mod tests { |
336 | use test_utils::covers; | ||
337 | |||
351 | use crate::{ | 338 | use crate::{ |
352 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, | 339 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, |
353 | Declaration, Reference, ReferenceSearchResult, SearchScope, | 340 | Declaration, Reference, ReferenceSearchResult, SearchScope, |
@@ -456,6 +443,27 @@ mod tests { | |||
456 | } | 443 | } |
457 | 444 | ||
458 | #[test] | 445 | #[test] |
446 | fn search_filters_by_range() { | ||
447 | covers!(search_filters_by_range); | ||
448 | let code = r#" | ||
449 | fn foo() { | ||
450 | let spam<|> = 92; | ||
451 | spam + spam | ||
452 | } | ||
453 | fn bar() { | ||
454 | let spam = 92; | ||
455 | spam + spam | ||
456 | } | ||
457 | "#; | ||
458 | let refs = get_all_refs(code); | ||
459 | check_result( | ||
460 | refs, | ||
461 | "spam BIND_PAT FileId(1) [44; 48) Other Write", | ||
462 | &["FileId(1) [71; 75) Other Read", "FileId(1) [78; 82) Other Read"], | ||
463 | ); | ||
464 | } | ||
465 | |||
466 | #[test] | ||
459 | fn test_find_all_refs_for_param_inside() { | 467 | fn test_find_all_refs_for_param_inside() { |
460 | let code = r#" | 468 | let code = r#" |
461 | fn foo(i : u32) -> u32 { | 469 | fn foo(i : u32) -> u32 { |
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 478e18871..91b21429a 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs | |||
@@ -1,34 +1,32 @@ | |||
1 | //! Functions that are used to classify an element from its definition or reference. | 1 | //! Functions that are used to classify an element from its definition or reference. |
2 | 2 | ||
3 | use hir::{InFile, PathResolution, SourceBinder}; | 3 | use hir::{PathResolution, Semantics}; |
4 | use ra_ide_db::defs::NameDefinition; | ||
5 | use ra_ide_db::RootDatabase; | ||
4 | use ra_prof::profile; | 6 | use ra_prof::profile; |
5 | use ra_syntax::{ast, AstNode}; | 7 | use ra_syntax::{ast, AstNode}; |
6 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
7 | 9 | ||
8 | use super::NameDefinition; | 10 | pub use ra_ide_db::defs::{from_module_def, from_struct_field}; |
9 | use ra_ide_db::RootDatabase; | ||
10 | |||
11 | pub use ra_ide_db::defs::{classify_name, from_module_def, from_struct_field}; | ||
12 | 11 | ||
13 | pub(crate) fn classify_name_ref( | 12 | pub(crate) fn classify_name_ref( |
14 | sb: &mut SourceBinder<RootDatabase>, | 13 | sema: &Semantics<RootDatabase>, |
15 | name_ref: InFile<&ast::NameRef>, | 14 | name_ref: &ast::NameRef, |
16 | ) -> Option<NameDefinition> { | 15 | ) -> Option<NameDefinition> { |
17 | let _p = profile("classify_name_ref"); | 16 | let _p = profile("classify_name_ref"); |
18 | 17 | ||
19 | let parent = name_ref.value.syntax().parent()?; | 18 | let parent = name_ref.syntax().parent()?; |
20 | let analyzer = sb.analyze(name_ref.map(|it| it.syntax()), None); | ||
21 | 19 | ||
22 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 20 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
23 | tested_by!(goto_def_for_methods); | 21 | tested_by!(goto_def_for_methods); |
24 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | 22 | if let Some(func) = sema.resolve_method_call(&method_call) { |
25 | return Some(from_module_def(func.into())); | 23 | return Some(from_module_def(func.into())); |
26 | } | 24 | } |
27 | } | 25 | } |
28 | 26 | ||
29 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 27 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
30 | tested_by!(goto_def_for_fields); | 28 | tested_by!(goto_def_for_fields); |
31 | if let Some(field) = analyzer.resolve_field(&field_expr) { | 29 | if let Some(field) = sema.resolve_field(&field_expr) { |
32 | return Some(from_struct_field(field)); | 30 | return Some(from_struct_field(field)); |
33 | } | 31 | } |
34 | } | 32 | } |
@@ -36,22 +34,20 @@ pub(crate) fn classify_name_ref( | |||
36 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { | 34 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { |
37 | tested_by!(goto_def_for_record_fields); | 35 | tested_by!(goto_def_for_record_fields); |
38 | tested_by!(goto_def_for_field_init_shorthand); | 36 | tested_by!(goto_def_for_field_init_shorthand); |
39 | if let Some(field_def) = analyzer.resolve_record_field(&record_field) { | 37 | if let Some(field_def) = sema.resolve_record_field(&record_field) { |
40 | return Some(from_struct_field(field_def)); | 38 | return Some(from_struct_field(field_def)); |
41 | } | 39 | } |
42 | } | 40 | } |
43 | 41 | ||
44 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 42 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
45 | tested_by!(goto_def_for_macros); | 43 | tested_by!(goto_def_for_macros); |
46 | if let Some(macro_def) = | 44 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { |
47 | analyzer.resolve_macro_call(sb.db, name_ref.with_value(¯o_call)) | ||
48 | { | ||
49 | return Some(NameDefinition::Macro(macro_def)); | 45 | return Some(NameDefinition::Macro(macro_def)); |
50 | } | 46 | } |
51 | } | 47 | } |
52 | 48 | ||
53 | let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?; | 49 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; |
54 | let resolved = analyzer.resolve_path(sb.db, &path)?; | 50 | let resolved = sema.resolve_path(&path)?; |
55 | let res = match resolved { | 51 | let res = match resolved { |
56 | PathResolution::Def(def) => from_module_def(def), | 52 | PathResolution::Def(def) => from_module_def(def), |
57 | PathResolution::AssocItem(item) => { | 53 | PathResolution::AssocItem(item) => { |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index bdb90020b..5b4bcf434 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::ModuleSource; | 3 | use hir::{ModuleSource, Semantics}; |
4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; | 4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, | 7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, |
@@ -24,15 +24,16 @@ pub(crate) fn rename( | |||
24 | _ => return None, | 24 | _ => return None, |
25 | } | 25 | } |
26 | 26 | ||
27 | let parse = db.parse(position.file_id); | 27 | let sema = Semantics::new(db); |
28 | let source_file = sema.parse(position.file_id); | ||
28 | if let Some((ast_name, ast_module)) = | 29 | if let Some((ast_name, ast_module)) = |
29 | find_name_and_module_at_offset(parse.tree().syntax(), position) | 30 | find_name_and_module_at_offset(source_file.syntax(), position) |
30 | { | 31 | { |
31 | let range = ast_name.syntax().text_range(); | 32 | let range = ast_name.syntax().text_range(); |
32 | rename_mod(db, &ast_name, &ast_module, position, new_name) | 33 | rename_mod(&sema, &ast_name, &ast_module, position, new_name) |
33 | .map(|info| RangeInfo::new(range, info)) | 34 | .map(|info| RangeInfo::new(range, info)) |
34 | } else { | 35 | } else { |
35 | rename_reference(db, position, new_name) | 36 | rename_reference(sema.db, position, new_name) |
36 | } | 37 | } |
37 | } | 38 | } |
38 | 39 | ||
@@ -54,7 +55,7 @@ fn source_edit_from_file_id_range( | |||
54 | } | 55 | } |
55 | 56 | ||
56 | fn rename_mod( | 57 | fn rename_mod( |
57 | db: &RootDatabase, | 58 | sema: &Semantics<RootDatabase>, |
58 | ast_name: &ast::Name, | 59 | ast_name: &ast::Name, |
59 | ast_module: &ast::Module, | 60 | ast_module: &ast::Module, |
60 | position: FilePosition, | 61 | position: FilePosition, |
@@ -62,13 +63,12 @@ fn rename_mod( | |||
62 | ) -> Option<SourceChange> { | 63 | ) -> Option<SourceChange> { |
63 | let mut source_file_edits = Vec::new(); | 64 | let mut source_file_edits = Vec::new(); |
64 | let mut file_system_edits = Vec::new(); | 65 | let mut file_system_edits = Vec::new(); |
65 | let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() }; | 66 | if let Some(module) = sema.to_def(ast_module) { |
66 | if let Some(module) = hir::SourceBinder::new(db).to_def(module_src) { | 67 | let src = module.definition_source(sema.db); |
67 | let src = module.definition_source(db); | 68 | let file_id = src.file_id.original_file(sema.db); |
68 | let file_id = src.file_id.original_file(db); | ||
69 | match src.value { | 69 | match src.value { |
70 | ModuleSource::SourceFile(..) => { | 70 | ModuleSource::SourceFile(..) => { |
71 | let mod_path: RelativePathBuf = db.file_relative_path(file_id); | 71 | let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); |
72 | // mod is defined in path/to/dir/mod.rs | 72 | // mod is defined in path/to/dir/mod.rs |
73 | let dst_path = if mod_path.file_stem() == Some("mod") { | 73 | let dst_path = if mod_path.file_stem() == Some("mod") { |
74 | mod_path | 74 | mod_path |
@@ -82,7 +82,7 @@ fn rename_mod( | |||
82 | if let Some(path) = dst_path { | 82 | if let Some(path) = dst_path { |
83 | let move_file = FileSystemEdit::MoveFile { | 83 | let move_file = FileSystemEdit::MoveFile { |
84 | src: file_id, | 84 | src: file_id, |
85 | dst_source_root: db.file_source_root(position.file_id), | 85 | dst_source_root: sema.db.file_source_root(position.file_id), |
86 | dst_path: path, | 86 | dst_path: path, |
87 | }; | 87 | }; |
88 | file_system_edits.push(move_file); | 88 | file_system_edits.push(move_file); |
@@ -98,7 +98,7 @@ fn rename_mod( | |||
98 | }; | 98 | }; |
99 | source_file_edits.push(edit); | 99 | source_file_edits.push(edit); |
100 | 100 | ||
101 | if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(db, position, None) { | 101 | if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { |
102 | let ref_edits = refs.references.into_iter().map(|reference| { | 102 | let ref_edits = refs.references.into_iter().map(|reference| { |
103 | source_edit_from_file_id_range( | 103 | source_edit_from_file_id_range( |
104 | reference.file_range.file_id, | 104 | reference.file_range.file_id, |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index be2a67d0a..74877e90f 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,8 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{InFile, SourceBinder}; | 3 | use hir::Semantics; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_db::SourceDatabase; | ||
6 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::{ | 6 | use ra_syntax::{ |
8 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, | 7 | ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, |
@@ -42,46 +41,33 @@ pub enum RunnableKind { | |||
42 | } | 41 | } |
43 | 42 | ||
44 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 43 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
45 | let parse = db.parse(file_id); | 44 | let sema = Semantics::new(db); |
46 | let mut sb = SourceBinder::new(db); | 45 | let source_file = sema.parse(file_id); |
47 | parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect() | 46 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() |
48 | } | 47 | } |
49 | 48 | ||
50 | fn runnable( | 49 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { |
51 | db: &RootDatabase, | ||
52 | source_binder: &mut SourceBinder<RootDatabase>, | ||
53 | file_id: FileId, | ||
54 | item: SyntaxNode, | ||
55 | ) -> Option<Runnable> { | ||
56 | match_ast! { | 50 | match_ast! { |
57 | match item { | 51 | match item { |
58 | ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) }, | 52 | ast::FnDef(it) => { runnable_fn(sema, it) }, |
59 | ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) }, | 53 | ast::Module(it) => { runnable_mod(sema, it) }, |
60 | _ => { None }, | 54 | _ => None, |
61 | } | 55 | } |
62 | } | 56 | } |
63 | } | 57 | } |
64 | 58 | ||
65 | fn runnable_fn( | 59 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { |
66 | db: &RootDatabase, | ||
67 | source_binder: &mut SourceBinder<RootDatabase>, | ||
68 | file_id: FileId, | ||
69 | fn_def: ast::FnDef, | ||
70 | ) -> Option<Runnable> { | ||
71 | let name_string = fn_def.name()?.text().to_string(); | 60 | let name_string = fn_def.name()?.text().to_string(); |
72 | 61 | ||
73 | let kind = if name_string == "main" { | 62 | let kind = if name_string == "main" { |
74 | RunnableKind::Bin | 63 | RunnableKind::Bin |
75 | } else { | 64 | } else { |
76 | let test_id = if let Some(module) = source_binder | 65 | let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { |
77 | .to_def(InFile::new(file_id.into(), fn_def.clone())) | ||
78 | .map(|def| def.module(db)) | ||
79 | { | ||
80 | let path = module | 66 | let path = module |
81 | .path_to_root(db) | 67 | .path_to_root(sema.db) |
82 | .into_iter() | 68 | .into_iter() |
83 | .rev() | 69 | .rev() |
84 | .filter_map(|it| it.name(db)) | 70 | .filter_map(|it| it.name(sema.db)) |
85 | .map(|name| name.to_string()) | 71 | .map(|name| name.to_string()) |
86 | .chain(std::iter::once(name_string)) | 72 | .chain(std::iter::once(name_string)) |
87 | .join("::"); | 73 | .join("::"); |
@@ -115,12 +101,7 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
115 | .any(|attribute_text| attribute_text.contains("test")) | 101 | .any(|attribute_text| attribute_text.contains("test")) |
116 | } | 102 | } |
117 | 103 | ||
118 | fn runnable_mod( | 104 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { |
119 | db: &RootDatabase, | ||
120 | source_binder: &mut SourceBinder<RootDatabase>, | ||
121 | file_id: FileId, | ||
122 | module: ast::Module, | ||
123 | ) -> Option<Runnable> { | ||
124 | let has_test_function = module | 105 | let has_test_function = module |
125 | .item_list()? | 106 | .item_list()? |
126 | .items() | 107 | .items() |
@@ -133,9 +114,10 @@ fn runnable_mod( | |||
133 | return None; | 114 | return None; |
134 | } | 115 | } |
135 | let range = module.syntax().text_range(); | 116 | let range = module.syntax().text_range(); |
136 | let module = source_binder.to_def(InFile::new(file_id.into(), module))?; | 117 | let module = sema.to_def(&module)?; |
137 | 118 | ||
138 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); | 119 | let path = |
120 | module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); | ||
139 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 121 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) |
140 | } | 122 | } |
141 | 123 | ||
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index a02dbaf2f..51851763e 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -10,29 +10,29 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
10 | .parameter { color: #94BFF3; } | 10 | .parameter { color: #94BFF3; } |
11 | .text { color: #DCDCCC; } | 11 | .text { color: #DCDCCC; } |
12 | .type { color: #7CB8BB; } | 12 | .type { color: #7CB8BB; } |
13 | .type\.builtin { color: #8CD0D3; } | 13 | .type.builtin { color: #8CD0D3; } |
14 | .type\.param { color: #20999D; } | 14 | .type.param { color: #20999D; } |
15 | .attribute { color: #94BFF3; } | 15 | .attribute { color: #94BFF3; } |
16 | .literal { color: #BFEBBF; } | 16 | .literal { color: #BFEBBF; } |
17 | .literal\.numeric { color: #6A8759; } | 17 | .literal.numeric { color: #6A8759; } |
18 | .macro { color: #94BFF3; } | 18 | .macro { color: #94BFF3; } |
19 | .module { color: #AFD8AF; } | 19 | .module { color: #AFD8AF; } |
20 | .variable { color: #DCDCCC; } | 20 | .variable { color: #DCDCCC; } |
21 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } | 21 | .variable.mut { color: #DCDCCC; text-decoration: underline; } |
22 | 22 | ||
23 | .keyword { color: #F0DFAF; } | 23 | .keyword { color: #F0DFAF; } |
24 | .keyword\.unsafe { color: #DFAF8F; } | 24 | .keyword.unsafe { color: #DFAF8F; } |
25 | .keyword\.control { color: #F0DFAF; font-weight: bold; } | 25 | .keyword.control { color: #F0DFAF; font-weight: bold; } |
26 | </style> | 26 | </style> |
27 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> | 27 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> |
28 | <span class="keyword">struct</span> <span class="type">Foo</span> { | 28 | <span class="keyword">struct</span> <span class="type">Foo</span> { |
29 | <span class="keyword">pub</span> <span class="field">x</span>: <span class="type.builtin">i32</span>, | 29 | <span class="keyword">pub</span> <span class="field">x</span>: <span class="type builtin">i32</span>, |
30 | <span class="keyword">pub</span> <span class="field">y</span>: <span class="type.builtin">i32</span>, | 30 | <span class="keyword">pub</span> <span class="field">y</span>: <span class="type builtin">i32</span>, |
31 | } | 31 | } |
32 | 32 | ||
33 | <span class="keyword">fn</span> <span class="function">foo</span><<span class="type.param">T</span>>() -> <span class="type.param">T</span> { | 33 | <span class="keyword">fn</span> <span class="function">foo</span><<span class="type param">T</span>>() -> <span class="type param">T</span> { |
34 | <span class="macro">unimplemented</span><span class="macro">!</span>(); | 34 | <span class="macro">unimplemented</span><span class="macro">!</span>(); |
35 | <span class="function">foo</span>::<<span class="type.builtin">i32</span>>(); | 35 | <span class="function">foo</span>::<<span class="type builtin">i32</span>>(); |
36 | } | 36 | } |
37 | 37 | ||
38 | <span class="macro">macro_rules</span><span class="macro">!</span> def_fn { | 38 | <span class="macro">macro_rules</span><span class="macro">!</span> def_fn { |
@@ -40,33 +40,33 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
40 | } | 40 | } |
41 | 41 | ||
42 | <span class="macro">def_fn</span><span class="macro">!</span>{ | 42 | <span class="macro">def_fn</span><span class="macro">!</span>{ |
43 | <span class="keyword">fn</span> <span class="function">bar</span>() -> <span class="type.builtin">u32</span> { | 43 | <span class="keyword">fn</span> <span class="function">bar</span>() -> <span class="type builtin">u32</span> { |
44 | <span class="literal.numeric">100</span> | 44 | <span class="literal numeric">100</span> |
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
48 | <span class="comment">// comment</span> | 48 | <span class="comment">// comment</span> |
49 | <span class="keyword">fn</span> <span class="function">main</span>() { | 49 | <span class="keyword">fn</span> <span class="function">main</span>() { |
50 | <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); | 50 | <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal numeric">92</span>); |
51 | 51 | ||
52 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = Vec::new(); | 52 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable mutable">vec</span> = Vec::new(); |
53 | <span class="keyword.control">if</span> <span class="keyword">true</span> { | 53 | <span class="keyword control">if</span> <span class="keyword">true</span> { |
54 | <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>; | 54 | <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal numeric">92</span>; |
55 | <span class="variable.mut">vec</span>.push(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> }); | 55 | <span class="variable mutable">vec</span>.push(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal numeric">1</span> }); |
56 | } | 56 | } |
57 | <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.set_len(<span class="literal.numeric">0</span>); } | 57 | <span class="keyword unsafe">unsafe</span> { <span class="variable mutable">vec</span>.set_len(<span class="literal numeric">0</span>); } |
58 | 58 | ||
59 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>; | 59 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable mutable">x</span> = <span class="literal numeric">42</span>; |
60 | <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; | 60 | <span class="keyword">let</span> <span class="variable mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; |
61 | <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable.mut">y</span>; | 61 | <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable mutable">y</span>; |
62 | 62 | ||
63 | <span class="variable.mut">y</span>; | 63 | <span class="variable mutable">y</span>; |
64 | } | 64 | } |
65 | 65 | ||
66 | <span class="keyword">enum</span> <span class="type">E</span><<span class="type.param">X</span>> { | 66 | <span class="keyword">enum</span> <span class="type">E</span><<span class="type param">X</span>> { |
67 | <span class="constant">V</span>(<span class="type.param">X</span>) | 67 | <span class="constant">V</span>(<span class="type param">X</span>) |
68 | } | 68 | } |
69 | 69 | ||
70 | <span class="keyword">impl</span><<span class="type.param">X</span>> <span class="type">E</span><<span class="type.param">X</span>> { | 70 | <span class="keyword">impl</span><<span class="type param">X</span>> <span class="type">E</span><<span class="type param">X</span>> { |
71 | <span class="keyword">fn</span> <span class="function">new</span><<span class="type.param">T</span>>() -> <span class="type">E</span><<span class="type.param">T</span>> {} | 71 | <span class="keyword">fn</span> <span class="function">new</span><<span class="type param">T</span>>() -> <span class="type">E</span><<span class="type param">T</span>> {} |
72 | }</code></pre> \ No newline at end of file | 72 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 95f038f00..1f869867f 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -10,29 +10,29 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
10 | .parameter { color: #94BFF3; } | 10 | .parameter { color: #94BFF3; } |
11 | .text { color: #DCDCCC; } | 11 | .text { color: #DCDCCC; } |
12 | .type { color: #7CB8BB; } | 12 | .type { color: #7CB8BB; } |
13 | .type\.builtin { color: #8CD0D3; } | 13 | .type.builtin { color: #8CD0D3; } |
14 | .type\.param { color: #20999D; } | 14 | .type.param { color: #20999D; } |
15 | .attribute { color: #94BFF3; } | 15 | .attribute { color: #94BFF3; } |
16 | .literal { color: #BFEBBF; } | 16 | .literal { color: #BFEBBF; } |
17 | .literal\.numeric { color: #6A8759; } | 17 | .literal.numeric { color: #6A8759; } |
18 | .macro { color: #94BFF3; } | 18 | .macro { color: #94BFF3; } |
19 | .module { color: #AFD8AF; } | 19 | .module { color: #AFD8AF; } |
20 | .variable { color: #DCDCCC; } | 20 | .variable { color: #DCDCCC; } |
21 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } | 21 | .variable.mut { color: #DCDCCC; text-decoration: underline; } |
22 | 22 | ||
23 | .keyword { color: #F0DFAF; } | 23 | .keyword { color: #F0DFAF; } |
24 | .keyword\.unsafe { color: #DFAF8F; } | 24 | .keyword.unsafe { color: #DFAF8F; } |
25 | .keyword\.control { color: #F0DFAF; font-weight: bold; } | 25 | .keyword.control { color: #F0DFAF; font-weight: bold; } |
26 | </style> | 26 | </style> |
27 | <pre><code><span class="keyword">fn</span> <span class="function">main</span>() { | 27 | <pre><code><span class="keyword">fn</span> <span class="function">main</span>() { |
28 | <span class="keyword">let</span> <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>; | 28 | <span class="keyword">let</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>; |
29 | <span class="keyword">let</span> <span class="variable" data-binding-hash="4303609361109701698" style="color: hsl(242,75%,88%);">x</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string(); | 29 | <span class="keyword">let</span> <span class="variable" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string(); |
30 | <span class="keyword">let</span> <span class="variable" data-binding-hash="13865792086344377029" style="color: hsl(340,64%,86%);">y</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string(); | 30 | <span class="keyword">let</span> <span class="variable" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string(); |
31 | 31 | ||
32 | <span class="keyword">let</span> <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span> = <span class="string">"other color please!"</span>; | 32 | <span class="keyword">let</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string">"other color please!"</span>; |
33 | <span class="keyword">let</span> <span class="variable" data-binding-hash="12461245066629867975" style="color: hsl(132,91%,68%);">y</span> = <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span>.to_string(); | 33 | <span class="keyword">let</span> <span class="variable" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.to_string(); |
34 | } | 34 | } |
35 | 35 | ||
36 | <span class="keyword">fn</span> <span class="function">bar</span>() { | 36 | <span class="keyword">fn</span> <span class="function">bar</span>() { |
37 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>; | 37 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>; |
38 | }</code></pre> \ No newline at end of file | 38 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index d873f153e..5f11b091c 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -1,54 +1,30 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{HirFileId, InFile, Name, SourceAnalyzer, SourceBinder}; | 3 | mod tags; |
4 | use ra_db::SourceDatabase; | 4 | mod html; |
5 | use ra_ide_db::{defs::NameDefinition, RootDatabase}; | 5 | |
6 | use hir::{Name, Semantics}; | ||
7 | use ra_ide_db::{ | ||
8 | defs::{classify_name, NameDefinition}, | ||
9 | RootDatabase, | ||
10 | }; | ||
6 | use ra_prof::profile; | 11 | use ra_prof::profile; |
7 | use ra_syntax::{ | 12 | use ra_syntax::{ |
8 | ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, | 13 | ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, |
9 | WalkEvent, T, | 14 | TextRange, WalkEvent, T, |
10 | }; | 15 | }; |
11 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
12 | 17 | ||
13 | use crate::{ | 18 | use crate::{references::classify_name_ref, FileId}; |
14 | expand::descend_into_macros_with_analyzer, | ||
15 | references::{classify_name, classify_name_ref}, | ||
16 | FileId, | ||
17 | }; | ||
18 | 19 | ||
19 | pub mod tags { | 20 | pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; |
20 | pub(crate) const FIELD: &str = "field"; | 21 | |
21 | pub(crate) const FUNCTION: &str = "function"; | 22 | pub(crate) use html::highlight_as_html; |
22 | pub(crate) const MODULE: &str = "module"; | ||
23 | pub(crate) const CONSTANT: &str = "constant"; | ||
24 | pub(crate) const MACRO: &str = "macro"; | ||
25 | |||
26 | pub(crate) const VARIABLE: &str = "variable"; | ||
27 | pub(crate) const VARIABLE_MUT: &str = "variable.mut"; | ||
28 | |||
29 | pub(crate) const TYPE: &str = "type"; | ||
30 | pub(crate) const TYPE_BUILTIN: &str = "type.builtin"; | ||
31 | pub(crate) const TYPE_SELF: &str = "type.self"; | ||
32 | pub(crate) const TYPE_PARAM: &str = "type.param"; | ||
33 | pub(crate) const TYPE_LIFETIME: &str = "type.lifetime"; | ||
34 | |||
35 | pub(crate) const LITERAL_BYTE: &str = "literal.byte"; | ||
36 | pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric"; | ||
37 | pub(crate) const LITERAL_CHAR: &str = "literal.char"; | ||
38 | |||
39 | pub(crate) const LITERAL_COMMENT: &str = "comment"; | ||
40 | pub(crate) const LITERAL_STRING: &str = "string"; | ||
41 | pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute"; | ||
42 | |||
43 | pub(crate) const KEYWORD: &str = "keyword"; | ||
44 | pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe"; | ||
45 | pub(crate) const KEYWORD_CONTROL: &str = "keyword.control"; | ||
46 | } | ||
47 | 23 | ||
48 | #[derive(Debug)] | 24 | #[derive(Debug)] |
49 | pub struct HighlightedRange { | 25 | pub struct HighlightedRange { |
50 | pub range: TextRange, | 26 | pub range: TextRange, |
51 | pub tag: &'static str, | 27 | pub highlight: Highlight, |
52 | pub binding_hash: Option<u64>, | 28 | pub binding_hash: Option<u64>, |
53 | } | 29 | } |
54 | 30 | ||
@@ -67,54 +43,82 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { | |||
67 | } | 43 | } |
68 | } | 44 | } |
69 | 45 | ||
70 | pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { | 46 | pub(crate) fn highlight( |
47 | db: &RootDatabase, | ||
48 | file_id: FileId, | ||
49 | range: Option<TextRange>, | ||
50 | ) -> Vec<HighlightedRange> { | ||
71 | let _p = profile("highlight"); | 51 | let _p = profile("highlight"); |
72 | let parse = db.parse(file_id); | 52 | let sema = Semantics::new(db); |
73 | let root = parse.tree().syntax().clone(); | 53 | let root = sema.parse(file_id).syntax().clone(); |
74 | 54 | ||
75 | let mut sb = SourceBinder::new(db); | ||
76 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 55 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
77 | let mut res = Vec::new(); | 56 | let mut res = Vec::new(); |
78 | let analyzer = sb.analyze(InFile::new(file_id.into(), &root), None); | ||
79 | 57 | ||
80 | let mut in_macro_call = None; | 58 | let mut in_macro_call = None; |
81 | 59 | ||
60 | // Determine the root based on the given range. | ||
61 | let (root, highlight_range) = if let Some(range) = range { | ||
62 | let root = match root.covering_element(range) { | ||
63 | NodeOrToken::Node(node) => node, | ||
64 | NodeOrToken::Token(token) => token.parent(), | ||
65 | }; | ||
66 | (root, range) | ||
67 | } else { | ||
68 | (root.clone(), root.text_range()) | ||
69 | }; | ||
70 | |||
82 | for event in root.preorder_with_tokens() { | 71 | for event in root.preorder_with_tokens() { |
83 | match event { | 72 | match event { |
84 | WalkEvent::Enter(node) => match node.kind() { | 73 | WalkEvent::Enter(node) => { |
85 | MACRO_CALL => { | 74 | if node.text_range().intersection(&highlight_range).is_none() { |
86 | in_macro_call = Some(node.clone()); | 75 | continue; |
87 | if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { | ||
88 | res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None }); | ||
89 | } | ||
90 | } | 76 | } |
91 | _ if in_macro_call.is_some() => { | 77 | |
92 | if let Some(token) = node.as_token() { | 78 | match node.kind() { |
93 | if let Some((tag, binding_hash)) = highlight_token_tree( | 79 | MACRO_CALL => { |
94 | &mut sb, | 80 | in_macro_call = Some(node.clone()); |
95 | &analyzer, | 81 | if let Some(range) = highlight_macro(node) { |
96 | &mut bindings_shadow_count, | 82 | res.push(HighlightedRange { |
97 | InFile::new(file_id.into(), token.clone()), | 83 | range, |
98 | ) { | 84 | highlight: HighlightTag::Macro.into(), |
85 | binding_hash: None, | ||
86 | }); | ||
87 | } | ||
88 | } | ||
89 | _ if in_macro_call.is_some() => { | ||
90 | if let Some(token) = node.as_token() { | ||
91 | if let Some((highlight, binding_hash)) = highlight_token_tree( | ||
92 | &sema, | ||
93 | &mut bindings_shadow_count, | ||
94 | token.clone(), | ||
95 | ) { | ||
96 | res.push(HighlightedRange { | ||
97 | range: node.text_range(), | ||
98 | highlight, | ||
99 | binding_hash, | ||
100 | }); | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | _ => { | ||
105 | if let Some((highlight, binding_hash)) = | ||
106 | highlight_node(&sema, &mut bindings_shadow_count, node.clone()) | ||
107 | { | ||
99 | res.push(HighlightedRange { | 108 | res.push(HighlightedRange { |
100 | range: node.text_range(), | 109 | range: node.text_range(), |
101 | tag, | 110 | highlight, |
102 | binding_hash, | 111 | binding_hash, |
103 | }); | 112 | }); |
104 | } | 113 | } |
105 | } | 114 | } |
106 | } | 115 | } |
107 | _ => { | 116 | } |
108 | if let Some((tag, binding_hash)) = highlight_node( | ||
109 | &mut sb, | ||
110 | &mut bindings_shadow_count, | ||
111 | InFile::new(file_id.into(), node.clone()), | ||
112 | ) { | ||
113 | res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }); | ||
114 | } | ||
115 | } | ||
116 | }, | ||
117 | WalkEvent::Leave(node) => { | 117 | WalkEvent::Leave(node) => { |
118 | if node.text_range().intersection(&highlight_range).is_none() { | ||
119 | continue; | ||
120 | } | ||
121 | |||
118 | if let Some(m) = in_macro_call.as_ref() { | 122 | if let Some(m) = in_macro_call.as_ref() { |
119 | if *m == node { | 123 | if *m == node { |
120 | in_macro_call = None; | 124 | in_macro_call = None; |
@@ -127,8 +131,8 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
127 | res | 131 | res |
128 | } | 132 | } |
129 | 133 | ||
130 | fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { | 134 | fn highlight_macro(node: SyntaxElement) -> Option<TextRange> { |
131 | let macro_call = ast::MacroCall::cast(node.value.as_node()?.clone())?; | 135 | let macro_call = ast::MacroCall::cast(node.as_node()?.clone())?; |
132 | let path = macro_call.path()?; | 136 | let path = macro_call.path()?; |
133 | let name_ref = path.segment()?.name_ref()?; | 137 | let name_ref = path.segment()?.name_ref()?; |
134 | 138 | ||
@@ -145,56 +149,56 @@ fn highlight_macro(node: InFile<SyntaxElement>) -> Option<TextRange> { | |||
145 | } | 149 | } |
146 | 150 | ||
147 | fn highlight_token_tree( | 151 | fn highlight_token_tree( |
148 | sb: &mut SourceBinder<RootDatabase>, | 152 | sema: &Semantics<RootDatabase>, |
149 | analyzer: &SourceAnalyzer, | ||
150 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 153 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
151 | token: InFile<SyntaxToken>, | 154 | token: SyntaxToken, |
152 | ) -> Option<(&'static str, Option<u64>)> { | 155 | ) -> Option<(Highlight, Option<u64>)> { |
153 | if token.value.parent().kind() != TOKEN_TREE { | 156 | if token.parent().kind() != TOKEN_TREE { |
154 | return None; | 157 | return None; |
155 | } | 158 | } |
156 | let token = descend_into_macros_with_analyzer(sb.db, analyzer, token); | 159 | let token = sema.descend_into_macros(token.clone()); |
157 | let expanded = { | 160 | let expanded = { |
158 | let parent = token.value.parent(); | 161 | let parent = token.parent(); |
159 | // We only care Name and Name_ref | 162 | // We only care Name and Name_ref |
160 | match (token.value.kind(), parent.kind()) { | 163 | match (token.kind(), parent.kind()) { |
161 | (IDENT, NAME) | (IDENT, NAME_REF) => token.with_value(parent.into()), | 164 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), |
162 | _ => token.map(|it| it.into()), | 165 | _ => token.into(), |
163 | } | 166 | } |
164 | }; | 167 | }; |
165 | 168 | ||
166 | highlight_node(sb, bindings_shadow_count, expanded) | 169 | highlight_node(sema, bindings_shadow_count, expanded) |
167 | } | 170 | } |
168 | 171 | ||
169 | fn highlight_node( | 172 | fn highlight_node( |
170 | sb: &mut SourceBinder<RootDatabase>, | 173 | sema: &Semantics<RootDatabase>, |
171 | bindings_shadow_count: &mut FxHashMap<Name, u32>, | 174 | bindings_shadow_count: &mut FxHashMap<Name, u32>, |
172 | node: InFile<SyntaxElement>, | 175 | node: SyntaxElement, |
173 | ) -> Option<(&'static str, Option<u64>)> { | 176 | ) -> Option<(Highlight, Option<u64>)> { |
174 | let db = sb.db; | 177 | let db = sema.db; |
175 | let mut binding_hash = None; | 178 | let mut binding_hash = None; |
176 | let tag = match node.value.kind() { | 179 | let highlight: Highlight = match node.kind() { |
177 | FN_DEF => { | 180 | FN_DEF => { |
178 | bindings_shadow_count.clear(); | 181 | bindings_shadow_count.clear(); |
179 | return None; | 182 | return None; |
180 | } | 183 | } |
181 | COMMENT => tags::LITERAL_COMMENT, | 184 | COMMENT => HighlightTag::Comment.into(), |
182 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, | 185 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LiteralString.into(), |
183 | ATTR => tags::LITERAL_ATTRIBUTE, | 186 | ATTR => HighlightTag::Attribute.into(), |
184 | // Special-case field init shorthand | 187 | // Special-case field init shorthand |
185 | NAME_REF if node.value.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, | 188 | NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => { |
186 | NAME_REF if node.value.ancestors().any(|it| it.kind() == ATTR) => return None, | 189 | HighlightTag::Field.into() |
190 | } | ||
191 | NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None, | ||
187 | NAME_REF => { | 192 | NAME_REF => { |
188 | let name_ref = node.value.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); | 193 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); |
189 | let name_kind = classify_name_ref(sb, node.with_value(&name_ref)); | 194 | let name_kind = classify_name_ref(sema, &name_ref); |
190 | match name_kind { | 195 | match name_kind { |
191 | Some(name_kind) => { | 196 | Some(name_kind) => { |
192 | if let NameDefinition::Local(local) = &name_kind { | 197 | if let NameDefinition::Local(local) = &name_kind { |
193 | if let Some(name) = local.name(db) { | 198 | if let Some(name) = local.name(db) { |
194 | let shadow_count = | 199 | let shadow_count = |
195 | bindings_shadow_count.entry(name.clone()).or_default(); | 200 | bindings_shadow_count.entry(name.clone()).or_default(); |
196 | binding_hash = | 201 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
197 | Some(calc_binding_hash(node.file_id, &name, *shadow_count)) | ||
198 | } | 202 | } |
199 | }; | 203 | }; |
200 | 204 | ||
@@ -204,41 +208,45 @@ fn highlight_node( | |||
204 | } | 208 | } |
205 | } | 209 | } |
206 | NAME => { | 210 | NAME => { |
207 | let name = node.value.as_node().cloned().and_then(ast::Name::cast).unwrap(); | 211 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); |
208 | let name_kind = classify_name(sb, node.with_value(&name)); | 212 | let name_kind = classify_name(sema, &name); |
209 | 213 | ||
210 | if let Some(NameDefinition::Local(local)) = &name_kind { | 214 | if let Some(NameDefinition::Local(local)) = &name_kind { |
211 | if let Some(name) = local.name(db) { | 215 | if let Some(name) = local.name(db) { |
212 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | 216 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); |
213 | *shadow_count += 1; | 217 | *shadow_count += 1; |
214 | binding_hash = Some(calc_binding_hash(node.file_id, &name, *shadow_count)) | 218 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) |
215 | } | 219 | } |
216 | }; | 220 | }; |
217 | 221 | ||
218 | match name_kind { | 222 | match name_kind { |
219 | Some(name_kind) => highlight_name(db, name_kind), | 223 | Some(name_kind) => highlight_name(db, name_kind), |
220 | None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { | 224 | None => name.syntax().parent().map_or(HighlightTag::Function.into(), |x| { |
221 | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, | 225 | match x.kind() { |
222 | TYPE_PARAM => tags::TYPE_PARAM, | 226 | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => { |
223 | RECORD_FIELD_DEF => tags::FIELD, | 227 | HighlightTag::Type.into() |
224 | _ => tags::FUNCTION, | 228 | } |
229 | TYPE_PARAM => HighlightTag::TypeParam.into(), | ||
230 | RECORD_FIELD_DEF => HighlightTag::Field.into(), | ||
231 | _ => HighlightTag::Function.into(), | ||
232 | } | ||
225 | }), | 233 | }), |
226 | } | 234 | } |
227 | } | 235 | } |
228 | INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, | 236 | INT_NUMBER | FLOAT_NUMBER => HighlightTag::LiteralNumeric.into(), |
229 | BYTE => tags::LITERAL_BYTE, | 237 | BYTE => HighlightTag::LiteralByte.into(), |
230 | CHAR => tags::LITERAL_CHAR, | 238 | CHAR => HighlightTag::LiteralChar.into(), |
231 | LIFETIME => tags::TYPE_LIFETIME, | 239 | LIFETIME => HighlightTag::TypeLifetime.into(), |
232 | T![unsafe] => tags::KEYWORD_UNSAFE, | 240 | T![unsafe] => HighlightTag::Keyword | HighlightModifier::Unsafe, |
233 | k if is_control_keyword(k) => tags::KEYWORD_CONTROL, | 241 | k if is_control_keyword(k) => HighlightTag::Keyword | HighlightModifier::Control, |
234 | k if k.is_keyword() => tags::KEYWORD, | 242 | k if k.is_keyword() => HighlightTag::Keyword.into(), |
235 | 243 | ||
236 | _ => return None, | 244 | _ => return None, |
237 | }; | 245 | }; |
238 | 246 | ||
239 | return Some((tag, binding_hash)); | 247 | return Some((highlight, binding_hash)); |
240 | 248 | ||
241 | fn calc_binding_hash(file_id: HirFileId, name: &Name, shadow_count: u32) -> u64 { | 249 | fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { |
242 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { | 250 | fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { |
243 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; | 251 | use std::{collections::hash_map::DefaultHasher, hash::Hasher}; |
244 | 252 | ||
@@ -247,134 +255,48 @@ fn highlight_node( | |||
247 | hasher.finish() | 255 | hasher.finish() |
248 | } | 256 | } |
249 | 257 | ||
250 | hash((file_id, name, shadow_count)) | 258 | hash((name, shadow_count)) |
251 | } | 259 | } |
252 | } | 260 | } |
253 | 261 | ||
254 | pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { | 262 | fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight { |
255 | let parse = db.parse(file_id); | ||
256 | |||
257 | fn rainbowify(seed: u64) -> String { | ||
258 | use rand::prelude::*; | ||
259 | let mut rng = SmallRng::seed_from_u64(seed); | ||
260 | format!( | ||
261 | "hsl({h},{s}%,{l}%)", | ||
262 | h = rng.gen_range::<u16, _, _>(0, 361), | ||
263 | s = rng.gen_range::<u16, _, _>(42, 99), | ||
264 | l = rng.gen_range::<u16, _, _>(40, 91), | ||
265 | ) | ||
266 | } | ||
267 | |||
268 | let mut ranges = highlight(db, file_id); | ||
269 | ranges.sort_by_key(|it| it.range.start()); | ||
270 | // quick non-optimal heuristic to intersect token ranges and highlighted ranges | ||
271 | let mut frontier = 0; | ||
272 | let mut could_intersect: Vec<&HighlightedRange> = Vec::new(); | ||
273 | |||
274 | let mut buf = String::new(); | ||
275 | buf.push_str(&STYLE); | ||
276 | buf.push_str("<pre><code>"); | ||
277 | let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token()); | ||
278 | for token in tokens { | ||
279 | could_intersect.retain(|it| token.text_range().start() <= it.range.end()); | ||
280 | while let Some(r) = ranges.get(frontier) { | ||
281 | if r.range.start() <= token.text_range().end() { | ||
282 | could_intersect.push(r); | ||
283 | frontier += 1; | ||
284 | } else { | ||
285 | break; | ||
286 | } | ||
287 | } | ||
288 | let text = html_escape(&token.text()); | ||
289 | let ranges = could_intersect | ||
290 | .iter() | ||
291 | .filter(|it| token.text_range().is_subrange(&it.range)) | ||
292 | .collect::<Vec<_>>(); | ||
293 | if ranges.is_empty() { | ||
294 | buf.push_str(&text); | ||
295 | } else { | ||
296 | let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" "); | ||
297 | let binding_hash = ranges.first().and_then(|x| x.binding_hash); | ||
298 | let color = match (rainbow, binding_hash) { | ||
299 | (true, Some(hash)) => format!( | ||
300 | " data-binding-hash=\"{}\" style=\"color: {};\"", | ||
301 | hash, | ||
302 | rainbowify(hash) | ||
303 | ), | ||
304 | _ => "".into(), | ||
305 | }; | ||
306 | buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", classes, color, text)); | ||
307 | } | ||
308 | } | ||
309 | buf.push_str("</code></pre>"); | ||
310 | buf | ||
311 | } | ||
312 | |||
313 | fn highlight_name(db: &RootDatabase, def: NameDefinition) -> &'static str { | ||
314 | match def { | 263 | match def { |
315 | NameDefinition::Macro(_) => tags::MACRO, | 264 | NameDefinition::Macro(_) => HighlightTag::Macro, |
316 | NameDefinition::StructField(_) => tags::FIELD, | 265 | NameDefinition::StructField(_) => HighlightTag::Field, |
317 | NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => tags::MODULE, | 266 | NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::Module, |
318 | NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => tags::FUNCTION, | 267 | NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::Function, |
319 | NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => tags::TYPE, | 268 | NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::Type, |
320 | NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, | 269 | NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::Constant, |
321 | NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => tags::CONSTANT, | 270 | NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::Constant, |
322 | NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => tags::CONSTANT, | 271 | NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::Constant, |
323 | NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => tags::TYPE, | 272 | NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::Type, |
324 | NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, | 273 | NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::Type, |
325 | NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, | 274 | NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { |
326 | NameDefinition::SelfType(_) => tags::TYPE_SELF, | 275 | return HighlightTag::Type | HighlightModifier::Builtin |
327 | NameDefinition::TypeParam(_) => tags::TYPE_PARAM, | 276 | } |
277 | NameDefinition::SelfType(_) => HighlightTag::TypeSelf, | ||
278 | NameDefinition::TypeParam(_) => HighlightTag::TypeParam, | ||
328 | NameDefinition::Local(local) => { | 279 | NameDefinition::Local(local) => { |
280 | let mut h = Highlight::new(HighlightTag::Variable); | ||
329 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | 281 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { |
330 | tags::VARIABLE_MUT | 282 | h |= HighlightModifier::Mutable; |
331 | } else { | ||
332 | tags::VARIABLE | ||
333 | } | 283 | } |
284 | return h; | ||
334 | } | 285 | } |
335 | } | 286 | } |
287 | .into() | ||
336 | } | 288 | } |
337 | 289 | ||
338 | //FIXME: like, real html escaping | ||
339 | fn html_escape(text: &str) -> String { | ||
340 | text.replace("<", "<").replace(">", ">") | ||
341 | } | ||
342 | |||
343 | const STYLE: &str = " | ||
344 | <style> | ||
345 | body { margin: 0; } | ||
346 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | ||
347 | |||
348 | .comment { color: #7F9F7F; } | ||
349 | .string { color: #CC9393; } | ||
350 | .field { color: #94BFF3; } | ||
351 | .function { color: #93E0E3; } | ||
352 | .parameter { color: #94BFF3; } | ||
353 | .text { color: #DCDCCC; } | ||
354 | .type { color: #7CB8BB; } | ||
355 | .type\\.builtin { color: #8CD0D3; } | ||
356 | .type\\.param { color: #20999D; } | ||
357 | .attribute { color: #94BFF3; } | ||
358 | .literal { color: #BFEBBF; } | ||
359 | .literal\\.numeric { color: #6A8759; } | ||
360 | .macro { color: #94BFF3; } | ||
361 | .module { color: #AFD8AF; } | ||
362 | .variable { color: #DCDCCC; } | ||
363 | .variable\\.mut { color: #DCDCCC; text-decoration: underline; } | ||
364 | |||
365 | .keyword { color: #F0DFAF; } | ||
366 | .keyword\\.unsafe { color: #DFAF8F; } | ||
367 | .keyword\\.control { color: #F0DFAF; font-weight: bold; } | ||
368 | </style> | ||
369 | "; | ||
370 | |||
371 | #[cfg(test)] | 290 | #[cfg(test)] |
372 | mod tests { | 291 | mod tests { |
373 | use std::fs; | 292 | use std::fs; |
374 | 293 | ||
375 | use test_utils::{assert_eq_text, project_dir, read_text}; | 294 | use test_utils::{assert_eq_text, project_dir, read_text}; |
376 | 295 | ||
377 | use crate::mock_analysis::{single_file, MockAnalysis}; | 296 | use crate::{ |
297 | mock_analysis::{single_file, MockAnalysis}, | ||
298 | FileRange, TextRange, | ||
299 | }; | ||
378 | 300 | ||
379 | #[test] | 301 | #[test] |
380 | fn test_highlighting() { | 302 | fn test_highlighting() { |
@@ -475,4 +397,26 @@ fn bar() { | |||
475 | let _ = host.analysis().highlight(file_id).unwrap(); | 397 | let _ = host.analysis().highlight(file_id).unwrap(); |
476 | // eprintln!("elapsed: {:?}", t.elapsed()); | 398 | // eprintln!("elapsed: {:?}", t.elapsed()); |
477 | } | 399 | } |
400 | |||
401 | #[test] | ||
402 | fn test_ranges() { | ||
403 | let (analysis, file_id) = single_file( | ||
404 | r#" | ||
405 | #[derive(Clone, Debug)] | ||
406 | struct Foo { | ||
407 | pub x: i32, | ||
408 | pub y: i32, | ||
409 | }"#, | ||
410 | ); | ||
411 | |||
412 | // The "x" | ||
413 | let highlights = &analysis | ||
414 | .highlight_range(FileRange { | ||
415 | file_id, | ||
416 | range: TextRange::offset_len(82.into(), 1.into()), | ||
417 | }) | ||
418 | .unwrap(); | ||
419 | |||
420 | assert_eq!(&highlights[0].highlight.to_string(), "field"); | ||
421 | } | ||
478 | } | 422 | } |
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs new file mode 100644 index 000000000..210d9a57b --- /dev/null +++ b/crates/ra_ide/src/syntax_highlighting/html.rs | |||
@@ -0,0 +1,104 @@ | |||
1 | //! Renders a bit of code as HTML. | ||
2 | |||
3 | use ra_db::SourceDatabase; | ||
4 | use ra_syntax::AstNode; | ||
5 | |||
6 | use crate::{FileId, HighlightedRange, RootDatabase}; | ||
7 | |||
8 | use super::highlight; | ||
9 | |||
10 | pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { | ||
11 | let parse = db.parse(file_id); | ||
12 | |||
13 | fn rainbowify(seed: u64) -> String { | ||
14 | use rand::prelude::*; | ||
15 | let mut rng = SmallRng::seed_from_u64(seed); | ||
16 | format!( | ||
17 | "hsl({h},{s}%,{l}%)", | ||
18 | h = rng.gen_range::<u16, _, _>(0, 361), | ||
19 | s = rng.gen_range::<u16, _, _>(42, 99), | ||
20 | l = rng.gen_range::<u16, _, _>(40, 91), | ||
21 | ) | ||
22 | } | ||
23 | |||
24 | let mut ranges = highlight(db, file_id, None); | ||
25 | ranges.sort_by_key(|it| it.range.start()); | ||
26 | // quick non-optimal heuristic to intersect token ranges and highlighted ranges | ||
27 | let mut frontier = 0; | ||
28 | let mut could_intersect: Vec<&HighlightedRange> = Vec::new(); | ||
29 | |||
30 | let mut buf = String::new(); | ||
31 | buf.push_str(&STYLE); | ||
32 | buf.push_str("<pre><code>"); | ||
33 | let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token()); | ||
34 | for token in tokens { | ||
35 | could_intersect.retain(|it| token.text_range().start() <= it.range.end()); | ||
36 | while let Some(r) = ranges.get(frontier) { | ||
37 | if r.range.start() <= token.text_range().end() { | ||
38 | could_intersect.push(r); | ||
39 | frontier += 1; | ||
40 | } else { | ||
41 | break; | ||
42 | } | ||
43 | } | ||
44 | let text = html_escape(&token.text()); | ||
45 | let ranges = could_intersect | ||
46 | .iter() | ||
47 | .filter(|it| token.text_range().is_subrange(&it.range)) | ||
48 | .collect::<Vec<_>>(); | ||
49 | if ranges.is_empty() { | ||
50 | buf.push_str(&text); | ||
51 | } else { | ||
52 | let classes = ranges | ||
53 | .iter() | ||
54 | .map(|it| it.highlight.to_string().replace('.', " ")) | ||
55 | .collect::<Vec<_>>() | ||
56 | .join(" "); | ||
57 | let binding_hash = ranges.first().and_then(|x| x.binding_hash); | ||
58 | let color = match (rainbow, binding_hash) { | ||
59 | (true, Some(hash)) => format!( | ||
60 | " data-binding-hash=\"{}\" style=\"color: {};\"", | ||
61 | hash, | ||
62 | rainbowify(hash) | ||
63 | ), | ||
64 | _ => "".into(), | ||
65 | }; | ||
66 | buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", classes, color, text)); | ||
67 | } | ||
68 | } | ||
69 | buf.push_str("</code></pre>"); | ||
70 | buf | ||
71 | } | ||
72 | |||
73 | //FIXME: like, real html escaping | ||
74 | fn html_escape(text: &str) -> String { | ||
75 | text.replace("<", "<").replace(">", ">") | ||
76 | } | ||
77 | |||
78 | const STYLE: &str = " | ||
79 | <style> | ||
80 | body { margin: 0; } | ||
81 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | ||
82 | |||
83 | .comment { color: #7F9F7F; } | ||
84 | .string { color: #CC9393; } | ||
85 | .field { color: #94BFF3; } | ||
86 | .function { color: #93E0E3; } | ||
87 | .parameter { color: #94BFF3; } | ||
88 | .text { color: #DCDCCC; } | ||
89 | .type { color: #7CB8BB; } | ||
90 | .type.builtin { color: #8CD0D3; } | ||
91 | .type.param { color: #20999D; } | ||
92 | .attribute { color: #94BFF3; } | ||
93 | .literal { color: #BFEBBF; } | ||
94 | .literal.numeric { color: #6A8759; } | ||
95 | .macro { color: #94BFF3; } | ||
96 | .module { color: #AFD8AF; } | ||
97 | .variable { color: #DCDCCC; } | ||
98 | .variable.mut { color: #DCDCCC; text-decoration: underline; } | ||
99 | |||
100 | .keyword { color: #F0DFAF; } | ||
101 | .keyword.unsafe { color: #DFAF8F; } | ||
102 | .keyword.control { color: #F0DFAF; font-weight: bold; } | ||
103 | </style> | ||
104 | "; | ||
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs new file mode 100644 index 000000000..383c74c98 --- /dev/null +++ b/crates/ra_ide/src/syntax_highlighting/tags.rs | |||
@@ -0,0 +1,163 @@ | |||
1 | //! Defines token tags we use for syntax highlighting. | ||
2 | //! A tag is not unlike a CSS class. | ||
3 | |||
4 | use std::{fmt, ops}; | ||
5 | |||
6 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
7 | pub struct Highlight { | ||
8 | pub tag: HighlightTag, | ||
9 | pub modifiers: HighlightModifiers, | ||
10 | } | ||
11 | |||
12 | #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
13 | pub struct HighlightModifiers(u32); | ||
14 | |||
15 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
16 | pub enum HighlightTag { | ||
17 | Field, | ||
18 | Function, | ||
19 | Module, | ||
20 | Constant, | ||
21 | Macro, | ||
22 | Variable, | ||
23 | |||
24 | Type, | ||
25 | TypeSelf, | ||
26 | TypeParam, | ||
27 | TypeLifetime, | ||
28 | |||
29 | LiteralByte, | ||
30 | LiteralNumeric, | ||
31 | LiteralChar, | ||
32 | |||
33 | Comment, | ||
34 | LiteralString, | ||
35 | Attribute, | ||
36 | |||
37 | Keyword, | ||
38 | } | ||
39 | |||
40 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
41 | #[repr(u8)] | ||
42 | pub enum HighlightModifier { | ||
43 | Mutable = 0, | ||
44 | Unsafe, | ||
45 | /// Used with keywords like `if` and `break`. | ||
46 | Control, | ||
47 | Builtin, | ||
48 | } | ||
49 | |||
50 | impl HighlightTag { | ||
51 | fn as_str(self) -> &'static str { | ||
52 | match self { | ||
53 | HighlightTag::Field => "field", | ||
54 | HighlightTag::Function => "function", | ||
55 | HighlightTag::Module => "module", | ||
56 | HighlightTag::Constant => "constant", | ||
57 | HighlightTag::Macro => "macro", | ||
58 | HighlightTag::Variable => "variable", | ||
59 | HighlightTag::Type => "type", | ||
60 | HighlightTag::TypeSelf => "type.self", | ||
61 | HighlightTag::TypeParam => "type.param", | ||
62 | HighlightTag::TypeLifetime => "type.lifetime", | ||
63 | HighlightTag::LiteralByte => "literal.byte", | ||
64 | HighlightTag::LiteralNumeric => "literal.numeric", | ||
65 | HighlightTag::LiteralChar => "literal.char", | ||
66 | HighlightTag::Comment => "comment", | ||
67 | HighlightTag::LiteralString => "string", | ||
68 | HighlightTag::Attribute => "attribute", | ||
69 | HighlightTag::Keyword => "keyword", | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl fmt::Display for HighlightTag { | ||
75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
76 | fmt::Display::fmt(self.as_str(), f) | ||
77 | } | ||
78 | } | ||
79 | |||
80 | impl HighlightModifier { | ||
81 | const ALL: &'static [HighlightModifier] = &[ | ||
82 | HighlightModifier::Mutable, | ||
83 | HighlightModifier::Unsafe, | ||
84 | HighlightModifier::Control, | ||
85 | HighlightModifier::Builtin, | ||
86 | ]; | ||
87 | |||
88 | fn as_str(self) -> &'static str { | ||
89 | match self { | ||
90 | HighlightModifier::Mutable => "mutable", | ||
91 | HighlightModifier::Unsafe => "unsafe", | ||
92 | HighlightModifier::Control => "control", | ||
93 | HighlightModifier::Builtin => "builtin", | ||
94 | } | ||
95 | } | ||
96 | |||
97 | fn mask(self) -> u32 { | ||
98 | 1 << (self as u32) | ||
99 | } | ||
100 | } | ||
101 | |||
102 | impl fmt::Display for HighlightModifier { | ||
103 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
104 | fmt::Display::fmt(self.as_str(), f) | ||
105 | } | ||
106 | } | ||
107 | |||
108 | impl fmt::Display for Highlight { | ||
109 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
110 | write!(f, "{}", self.tag)?; | ||
111 | for modifier in self.modifiers.iter() { | ||
112 | write!(f, ".{}", modifier)? | ||
113 | } | ||
114 | Ok(()) | ||
115 | } | ||
116 | } | ||
117 | |||
118 | impl From<HighlightTag> for Highlight { | ||
119 | fn from(tag: HighlightTag) -> Highlight { | ||
120 | Highlight::new(tag) | ||
121 | } | ||
122 | } | ||
123 | |||
124 | impl Highlight { | ||
125 | pub(crate) fn new(tag: HighlightTag) -> Highlight { | ||
126 | Highlight { tag, modifiers: HighlightModifiers::default() } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | impl ops::BitOr<HighlightModifier> for HighlightTag { | ||
131 | type Output = Highlight; | ||
132 | |||
133 | fn bitor(self, rhs: HighlightModifier) -> Highlight { | ||
134 | Highlight::new(self) | rhs | ||
135 | } | ||
136 | } | ||
137 | |||
138 | impl ops::BitOrAssign<HighlightModifier> for HighlightModifiers { | ||
139 | fn bitor_assign(&mut self, rhs: HighlightModifier) { | ||
140 | self.0 |= rhs.mask(); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | impl ops::BitOrAssign<HighlightModifier> for Highlight { | ||
145 | fn bitor_assign(&mut self, rhs: HighlightModifier) { | ||
146 | self.modifiers |= rhs; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | impl ops::BitOr<HighlightModifier> for Highlight { | ||
151 | type Output = Highlight; | ||
152 | |||
153 | fn bitor(mut self, rhs: HighlightModifier) -> Highlight { | ||
154 | self |= rhs; | ||
155 | self | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl HighlightModifiers { | ||
160 | pub fn iter(self) -> impl Iterator<Item = HighlightModifier> { | ||
161 | HighlightModifier::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask()) | ||
162 | } | ||
163 | } | ||
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index ee409e34e..7b285d280 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml | |||
@@ -11,30 +11,17 @@ doctest = false | |||
11 | wasm = [] | 11 | wasm = [] |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | either = "1.5.3" | ||
15 | format-buf = "1.0.0" | ||
16 | indexmap = "1.3.2" | ||
17 | itertools = "0.8.2" | ||
18 | join_to_string = "0.1.3" | ||
19 | log = "0.4.8" | 14 | log = "0.4.8" |
20 | rayon = "1.3.0" | 15 | rayon = "1.3.0" |
21 | fst = { version = "0.3.5", default-features = false } | 16 | fst = { version = "0.3.5", default-features = false } |
22 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
23 | unicase = "2.6.0" | ||
24 | superslice = "1.0.0" | 18 | superslice = "1.0.0" |
25 | once_cell = "1.3.1" | ||
26 | 19 | ||
27 | ra_syntax = { path = "../ra_syntax" } | 20 | ra_syntax = { path = "../ra_syntax" } |
28 | ra_text_edit = { path = "../ra_text_edit" } | 21 | ra_text_edit = { path = "../ra_text_edit" } |
29 | ra_db = { path = "../ra_db" } | 22 | ra_db = { path = "../ra_db" } |
30 | ra_cfg = { path = "../ra_cfg" } | ||
31 | ra_fmt = { path = "../ra_fmt" } | ||
32 | ra_prof = { path = "../ra_prof" } | 23 | ra_prof = { path = "../ra_prof" } |
33 | test_utils = { path = "../test_utils" } | ||
34 | 24 | ||
35 | # ra_ide should depend only on the top-level `hir` package. if you need | 25 | # ra_ide should depend only on the top-level `hir` package. if you need |
36 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 26 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
37 | hir = { path = "../ra_hir", package = "ra_hir" } | 27 | hir = { path = "../ra_hir", package = "ra_hir" } |
38 | |||
39 | [dev-dependencies] | ||
40 | insta = "0.13.1" | ||
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 04c214624..e10e72f71 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -6,8 +6,8 @@ | |||
6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). | 6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). |
7 | 7 | ||
8 | use hir::{ | 8 | use hir::{ |
9 | Adt, FieldSource, HasSource, ImplBlock, InFile, Local, MacroDef, Module, ModuleDef, | 9 | Adt, FieldSource, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, Semantics, |
10 | SourceBinder, StructField, TypeParam, | 10 | StructField, TypeParam, |
11 | }; | 11 | }; |
12 | use ra_prof::profile; | 12 | use ra_prof::profile; |
13 | use ra_syntax::{ | 13 | use ra_syntax::{ |
@@ -68,78 +68,62 @@ impl NameDefinition { | |||
68 | } | 68 | } |
69 | } | 69 | } |
70 | 70 | ||
71 | pub fn classify_name( | 71 | pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameDefinition> { |
72 | sb: &mut SourceBinder<RootDatabase>, | ||
73 | name: InFile<&ast::Name>, | ||
74 | ) -> Option<NameDefinition> { | ||
75 | let _p = profile("classify_name"); | 72 | let _p = profile("classify_name"); |
76 | let parent = name.value.syntax().parent()?; | 73 | let parent = name.syntax().parent()?; |
77 | 74 | ||
78 | match_ast! { | 75 | match_ast! { |
79 | match parent { | 76 | match parent { |
80 | ast::BindPat(it) => { | 77 | ast::BindPat(it) => { |
81 | let src = name.with_value(it); | 78 | let local = sema.to_def(&it)?; |
82 | let local = sb.to_def(src)?; | ||
83 | Some(NameDefinition::Local(local)) | 79 | Some(NameDefinition::Local(local)) |
84 | }, | 80 | }, |
85 | ast::RecordFieldDef(it) => { | 81 | ast::RecordFieldDef(it) => { |
86 | let src = name.with_value(it); | 82 | let field: hir::StructField = sema.to_def(&it)?; |
87 | let field: hir::StructField = sb.to_def(src)?; | ||
88 | Some(from_struct_field(field)) | 83 | Some(from_struct_field(field)) |
89 | }, | 84 | }, |
90 | ast::Module(it) => { | 85 | ast::Module(it) => { |
91 | let def = sb.to_def(name.with_value(it))?; | 86 | let def = sema.to_def(&it)?; |
92 | Some(from_module_def(def.into())) | 87 | Some(from_module_def(def.into())) |
93 | }, | 88 | }, |
94 | ast::StructDef(it) => { | 89 | ast::StructDef(it) => { |
95 | let src = name.with_value(it); | 90 | let def: hir::Struct = sema.to_def(&it)?; |
96 | let def: hir::Struct = sb.to_def(src)?; | ||
97 | Some(from_module_def(def.into())) | 91 | Some(from_module_def(def.into())) |
98 | }, | 92 | }, |
99 | ast::EnumDef(it) => { | 93 | ast::EnumDef(it) => { |
100 | let src = name.with_value(it); | 94 | let def: hir::Enum = sema.to_def(&it)?; |
101 | let def: hir::Enum = sb.to_def(src)?; | ||
102 | Some(from_module_def(def.into())) | 95 | Some(from_module_def(def.into())) |
103 | }, | 96 | }, |
104 | ast::TraitDef(it) => { | 97 | ast::TraitDef(it) => { |
105 | let src = name.with_value(it); | 98 | let def: hir::Trait = sema.to_def(&it)?; |
106 | let def: hir::Trait = sb.to_def(src)?; | ||
107 | Some(from_module_def(def.into())) | 99 | Some(from_module_def(def.into())) |
108 | }, | 100 | }, |
109 | ast::StaticDef(it) => { | 101 | ast::StaticDef(it) => { |
110 | let src = name.with_value(it); | 102 | let def: hir::Static = sema.to_def(&it)?; |
111 | let def: hir::Static = sb.to_def(src)?; | ||
112 | Some(from_module_def(def.into())) | 103 | Some(from_module_def(def.into())) |
113 | }, | 104 | }, |
114 | ast::EnumVariant(it) => { | 105 | ast::EnumVariant(it) => { |
115 | let src = name.with_value(it); | 106 | let def: hir::EnumVariant = sema.to_def(&it)?; |
116 | let def: hir::EnumVariant = sb.to_def(src)?; | ||
117 | Some(from_module_def(def.into())) | 107 | Some(from_module_def(def.into())) |
118 | }, | 108 | }, |
119 | ast::FnDef(it) => { | 109 | ast::FnDef(it) => { |
120 | let src = name.with_value(it); | 110 | let def: hir::Function = sema.to_def(&it)?; |
121 | let def: hir::Function = sb.to_def(src)?; | ||
122 | Some(from_module_def(def.into())) | 111 | Some(from_module_def(def.into())) |
123 | }, | 112 | }, |
124 | ast::ConstDef(it) => { | 113 | ast::ConstDef(it) => { |
125 | let src = name.with_value(it); | 114 | let def: hir::Const = sema.to_def(&it)?; |
126 | let def: hir::Const = sb.to_def(src)?; | ||
127 | Some(from_module_def(def.into())) | 115 | Some(from_module_def(def.into())) |
128 | }, | 116 | }, |
129 | ast::TypeAliasDef(it) => { | 117 | ast::TypeAliasDef(it) => { |
130 | let src = name.with_value(it); | 118 | let def: hir::TypeAlias = sema.to_def(&it)?; |
131 | let def: hir::TypeAlias = sb.to_def(src)?; | ||
132 | Some(from_module_def(def.into())) | 119 | Some(from_module_def(def.into())) |
133 | }, | 120 | }, |
134 | ast::MacroCall(it) => { | 121 | ast::MacroCall(it) => { |
135 | let src = name.with_value(it); | 122 | let def = sema.to_def(&it)?; |
136 | let def = sb.to_def(src.clone())?; | ||
137 | |||
138 | Some(NameDefinition::Macro(def)) | 123 | Some(NameDefinition::Macro(def)) |
139 | }, | 124 | }, |
140 | ast::TypeParam(it) => { | 125 | ast::TypeParam(it) => { |
141 | let src = name.with_value(it); | 126 | let def = sema.to_def(&it)?; |
142 | let def = sb.to_def(src)?; | ||
143 | Some(NameDefinition::TypeParam(def)) | 127 | Some(NameDefinition::TypeParam(def)) |
144 | }, | 128 | }, |
145 | _ => None, | 129 | _ => None, |
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index b8dd358a9..e590d2a5c 100644 --- a/crates/ra_ide_db/src/imports_locator.rs +++ b/crates/ra_ide_db/src/imports_locator.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the ra_assists module. | 1 | //! This module contains an import search funcionality that is provided to the ra_assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. |
3 | 3 | ||
4 | use hir::{db::HirDatabase, ModuleDef, SourceBinder}; | 4 | use hir::{ModuleDef, Semantics}; |
5 | use ra_prof::profile; | 5 | use ra_prof::profile; |
6 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; | 6 | use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; |
7 | 7 | ||
@@ -12,17 +12,17 @@ use crate::{ | |||
12 | }; | 12 | }; |
13 | 13 | ||
14 | pub struct ImportsLocator<'a> { | 14 | pub struct ImportsLocator<'a> { |
15 | source_binder: SourceBinder<'a, RootDatabase>, | 15 | sema: Semantics<'a, RootDatabase>, |
16 | } | 16 | } |
17 | 17 | ||
18 | impl<'a> ImportsLocator<'a> { | 18 | impl<'a> ImportsLocator<'a> { |
19 | pub fn new(db: &'a RootDatabase) -> Self { | 19 | pub fn new(db: &'a RootDatabase) -> Self { |
20 | Self { source_binder: SourceBinder::new(db) } | 20 | Self { sema: Semantics::new(db) } |
21 | } | 21 | } |
22 | 22 | ||
23 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { | 23 | pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { |
24 | let _p = profile("search_for_imports"); | 24 | let _p = profile("search_for_imports"); |
25 | let db = self.source_binder.db; | 25 | let db = self.sema.db; |
26 | 26 | ||
27 | let project_results = { | 27 | let project_results = { |
28 | let mut query = Query::new(name_to_import.to_string()); | 28 | let mut query = Query::new(name_to_import.to_string()); |
@@ -41,7 +41,7 @@ impl<'a> ImportsLocator<'a> { | |||
41 | project_results | 41 | project_results |
42 | .into_iter() | 42 | .into_iter() |
43 | .chain(lib_results.into_iter()) | 43 | .chain(lib_results.into_iter()) |
44 | .filter_map(|import_candidate| self.get_name_definition(db, &import_candidate)) | 44 | .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) |
45 | .filter_map(|name_definition_to_import| match name_definition_to_import { | 45 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
46 | NameDefinition::ModuleDef(module_def) => Some(module_def), | 46 | NameDefinition::ModuleDef(module_def) => Some(module_def), |
47 | _ => None, | 47 | _ => None, |
@@ -49,22 +49,16 @@ impl<'a> ImportsLocator<'a> { | |||
49 | .collect() | 49 | .collect() |
50 | } | 50 | } |
51 | 51 | ||
52 | fn get_name_definition( | 52 | fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<NameDefinition> { |
53 | &mut self, | ||
54 | db: &impl HirDatabase, | ||
55 | import_candidate: &FileSymbol, | ||
56 | ) -> Option<NameDefinition> { | ||
57 | let _p = profile("get_name_definition"); | 53 | let _p = profile("get_name_definition"); |
58 | let file_id = import_candidate.file_id.into(); | 54 | let file_id = import_candidate.file_id; |
59 | let candidate_node = import_candidate.ptr.to_node(&db.parse_or_expand(file_id)?); | 55 | |
56 | let candidate_node = import_candidate.ptr.to_node(self.sema.parse(file_id).syntax()); | ||
60 | let candidate_name_node = if candidate_node.kind() != NAME { | 57 | let candidate_name_node = if candidate_node.kind() != NAME { |
61 | candidate_node.children().find(|it| it.kind() == NAME)? | 58 | candidate_node.children().find(|it| it.kind() == NAME)? |
62 | } else { | 59 | } else { |
63 | candidate_node | 60 | candidate_node |
64 | }; | 61 | }; |
65 | classify_name( | 62 | classify_name(&self.sema, &ast::Name::cast(candidate_name_node)?) |
66 | &mut self.source_binder, | ||
67 | hir::InFile { file_id, value: &ast::Name::cast(candidate_name_node)? }, | ||
68 | ) | ||
69 | } | 63 | } |
70 | } | 64 | } |
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml index 823745795..d15b08992 100644 --- a/crates/ra_prof/Cargo.toml +++ b/crates/ra_prof/Cargo.toml | |||
@@ -10,7 +10,6 @@ doctest = false | |||
10 | 10 | ||
11 | [dependencies] | 11 | [dependencies] |
12 | once_cell = "1.3.1" | 12 | once_cell = "1.3.1" |
13 | itertools = "0.8.2" | ||
14 | backtrace = { version = "0.3.44", optional = true } | 13 | backtrace = { version = "0.3.44", optional = true } |
15 | 14 | ||
16 | [target.'cfg(not(target_env = "msvc"))'.dependencies] | 15 | [target.'cfg(not(target_env = "msvc"))'.dependencies] |
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index 7891628dc..8efc6b368 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.8.2" | 14 | itertools = "0.8.2" |
15 | rowan = "0.9.0" | 15 | rowan = "0.9.1" |
16 | rustc_lexer = "0.1.0" | 16 | rustc_lexer = "0.1.0" |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 21fca99a6..ebf59288a 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -4,10 +4,11 @@ use std::ops::RangeInclusive; | |||
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use ra_text_edit::TextEditBuilder; | 6 | use ra_text_edit::TextEditBuilder; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::{FxHashMap, FxHashSet}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, | 10 | AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, |
11 | TextRange, TextUnit, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | /// Returns ancestors of the node at the offset, sorted by length. This should | 14 | /// Returns ancestors of the node at the offset, sorted by length. This should |
@@ -37,6 +38,17 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) -> | |||
37 | ancestors_at_offset(syntax, offset).find_map(N::cast) | 38 | ancestors_at_offset(syntax, offset).find_map(N::cast) |
38 | } | 39 | } |
39 | 40 | ||
41 | /// Skip to next non `trivia` token | ||
42 | pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> { | ||
43 | while token.kind().is_trivia() { | ||
44 | token = match direction { | ||
45 | Direction::Next => token.next_token()?, | ||
46 | Direction::Prev => token.prev_token()?, | ||
47 | } | ||
48 | } | ||
49 | Some(token) | ||
50 | } | ||
51 | |||
40 | /// Finds the first sibling in the given direction which is not `trivia` | 52 | /// Finds the first sibling in the given direction which is not `trivia` |
41 | pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> { | 53 | pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> { |
42 | return match element { | 54 | return match element { |
@@ -56,6 +68,11 @@ pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxEleme | |||
56 | root.covering_element(range) | 68 | root.covering_element(range) |
57 | } | 69 | } |
58 | 70 | ||
71 | pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> { | ||
72 | let u_ancestors = u.ancestors().collect::<FxHashSet<SyntaxNode>>(); | ||
73 | v.ancestors().find(|it| u_ancestors.contains(it)) | ||
74 | } | ||
75 | |||
59 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 76 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
60 | pub enum InsertPosition<T> { | 77 | pub enum InsertPosition<T> { |
61 | First, | 78 | First, |
diff --git a/crates/ra_text_edit/Cargo.toml b/crates/ra_text_edit/Cargo.toml index a32149299..cae28389d 100644 --- a/crates/ra_text_edit/Cargo.toml +++ b/crates/ra_text_edit/Cargo.toml | |||
@@ -9,7 +9,5 @@ publish = false | |||
9 | doctest = false | 9 | doctest = false |
10 | 10 | ||
11 | [dependencies] | 11 | [dependencies] |
12 | text_unit = "0.1.9" | 12 | text_unit = "0.1.10" |
13 | 13 | ||
14 | [dev-dependencies] | ||
15 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index c5d6e3831..d00545121 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -15,13 +15,12 @@ path = "./src/bin/main.rs" | |||
15 | [dependencies] | 15 | [dependencies] |
16 | anyhow = "1.0.26" | 16 | anyhow = "1.0.26" |
17 | crossbeam-channel = "0.4.0" | 17 | crossbeam-channel = "0.4.0" |
18 | either = "1.5.3" | ||
19 | env_logger = { version = "0.7.1", default-features = false } | 18 | env_logger = { version = "0.7.1", default-features = false } |
20 | globset = "0.4.4" | 19 | globset = "0.4.4" |
21 | itertools = "0.8.2" | 20 | itertools = "0.8.2" |
22 | jod-thread = "0.1.0" | 21 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 22 | log = "0.4.8" |
24 | lsp-types = { version = "0.70.1", features = ["proposed"] } | 23 | lsp-types = { version = "0.71.0", features = ["proposed"] } |
25 | parking_lot = "0.10.0" | 24 | parking_lot = "0.10.0" |
26 | pico-args = "0.3.1" | 25 | pico-args = "0.3.1" |
27 | rand = { version = "0.7.3", features = ["small_rng"] } | 26 | rand = { version = "0.7.3", features = ["small_rng"] } |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index c9fd645f1..759bceb32 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -1,12 +1,15 @@ | |||
1 | //! Advertizes the capabilities of the LSP Server. | 1 | //! Advertizes the capabilities of the LSP Server. |
2 | 2 | ||
3 | use crate::semantic_tokens; | ||
4 | |||
3 | use lsp_types::{ | 5 | use lsp_types::{ |
4 | CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions, | 6 | CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions, |
5 | CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, | 7 | CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, |
6 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, | 8 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, |
7 | SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, | 9 | SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend, |
8 | TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, | 10 | SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, |
9 | TypeDefinitionProviderCapability, WorkDoneProgressOptions, | 11 | TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, |
12 | WorkDoneProgressOptions, | ||
10 | }; | 13 | }; |
11 | 14 | ||
12 | pub fn server_capabilities() -> ServerCapabilities { | 15 | pub fn server_capabilities() -> ServerCapabilities { |
@@ -57,7 +60,19 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
57 | execute_command_provider: None, | 60 | execute_command_provider: None, |
58 | workspace: None, | 61 | workspace: None, |
59 | call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), | 62 | call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), |
60 | semantic_tokens_provider: None, | 63 | semantic_tokens_provider: Some( |
64 | SemanticTokensOptions { | ||
65 | legend: SemanticTokensLegend { | ||
66 | token_types: semantic_tokens::SUPPORTED_TYPES.iter().cloned().collect(), | ||
67 | token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.iter().cloned().collect(), | ||
68 | }, | ||
69 | |||
70 | document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), | ||
71 | range_provider: Some(true), | ||
72 | work_done_progress_options: Default::default(), | ||
73 | } | ||
74 | .into(), | ||
75 | ), | ||
61 | experimental: Default::default(), | 76 | experimental: Default::default(), |
62 | } | 77 | } |
63 | } | 78 | } |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 90ef74056..b012f5dd5 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs | |||
@@ -4,19 +4,27 @@ | |||
4 | use lsp_types::{ | 4 | use lsp_types::{ |
5 | self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, | 5 | self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, |
6 | Location, LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp, | 6 | Location, LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp, |
7 | SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, | 7 | SemanticTokenModifier, SemanticTokenType, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, |
8 | TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, | 8 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, |
9 | WorkspaceEdit, | ||
9 | }; | 10 | }; |
10 | use ra_ide::{ | 11 | use ra_ide::{ |
11 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, | 12 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, |
12 | FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, | 13 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, |
13 | NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, | 14 | InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, ReferenceAccess, Severity, |
15 | SourceChange, SourceFileEdit, | ||
14 | }; | 16 | }; |
15 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 17 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
16 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 18 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
17 | use ra_vfs::LineEndings; | 19 | use ra_vfs::LineEndings; |
18 | 20 | ||
19 | use crate::{req, world::WorldSnapshot, Result}; | 21 | use crate::{ |
22 | req, | ||
23 | semantic_tokens::{self, ModifierSet, BUILTIN, CONSTANT, CONTROL, MUTABLE, UNSAFE}, | ||
24 | world::WorldSnapshot, | ||
25 | Result, | ||
26 | }; | ||
27 | use semantic_tokens::ATTRIBUTE; | ||
20 | 28 | ||
21 | pub trait Conv { | 29 | pub trait Conv { |
22 | type Output; | 30 | type Output; |
@@ -302,6 +310,55 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold { | |||
302 | } | 310 | } |
303 | } | 311 | } |
304 | 312 | ||
313 | impl Conv for Highlight { | ||
314 | type Output = (u32, u32); | ||
315 | |||
316 | fn conv(self) -> Self::Output { | ||
317 | let mut mods = ModifierSet::default(); | ||
318 | let type_ = match self.tag { | ||
319 | HighlightTag::Field => SemanticTokenType::MEMBER, | ||
320 | HighlightTag::Function => SemanticTokenType::FUNCTION, | ||
321 | HighlightTag::Module => SemanticTokenType::NAMESPACE, | ||
322 | HighlightTag::Constant => { | ||
323 | mods |= SemanticTokenModifier::STATIC; | ||
324 | mods |= SemanticTokenModifier::READONLY; | ||
325 | CONSTANT | ||
326 | } | ||
327 | HighlightTag::Macro => SemanticTokenType::MACRO, | ||
328 | HighlightTag::Variable => SemanticTokenType::VARIABLE, | ||
329 | HighlightTag::Type => SemanticTokenType::TYPE, | ||
330 | HighlightTag::TypeSelf => { | ||
331 | mods |= SemanticTokenModifier::REFERENCE; | ||
332 | SemanticTokenType::TYPE | ||
333 | } | ||
334 | HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER, | ||
335 | HighlightTag::TypeLifetime => { | ||
336 | mods |= SemanticTokenModifier::REFERENCE; | ||
337 | SemanticTokenType::LABEL | ||
338 | } | ||
339 | HighlightTag::LiteralByte => SemanticTokenType::NUMBER, | ||
340 | HighlightTag::LiteralNumeric => SemanticTokenType::NUMBER, | ||
341 | HighlightTag::LiteralChar => SemanticTokenType::NUMBER, | ||
342 | HighlightTag::Comment => SemanticTokenType::COMMENT, | ||
343 | HighlightTag::LiteralString => SemanticTokenType::STRING, | ||
344 | HighlightTag::Attribute => ATTRIBUTE, | ||
345 | HighlightTag::Keyword => SemanticTokenType::KEYWORD, | ||
346 | }; | ||
347 | |||
348 | for modifier in self.modifiers.iter() { | ||
349 | let modifier = match modifier { | ||
350 | HighlightModifier::Mutable => MUTABLE, | ||
351 | HighlightModifier::Unsafe => UNSAFE, | ||
352 | HighlightModifier::Control => CONTROL, | ||
353 | HighlightModifier::Builtin => BUILTIN, | ||
354 | }; | ||
355 | mods |= modifier; | ||
356 | } | ||
357 | |||
358 | (semantic_tokens::type_index(type_), mods.0) | ||
359 | } | ||
360 | } | ||
361 | |||
305 | impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> { | 362 | impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> { |
306 | type Output = Option<T::Output>; | 363 | type Output = Option<T::Output>; |
307 | 364 | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 0dae30e46..a0f968823 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -36,6 +36,7 @@ pub mod req; | |||
36 | mod config; | 36 | mod config; |
37 | mod world; | 37 | mod world; |
38 | mod diagnostics; | 38 | mod diagnostics; |
39 | mod semantic_tokens; | ||
39 | 40 | ||
40 | use serde::de::DeserializeOwned; | 41 | use serde::de::DeserializeOwned; |
41 | 42 | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 98306986b..2b25f5443 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -527,6 +527,8 @@ fn on_request( | |||
527 | .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)? | 527 | .on::<req::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)? |
528 | .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)? | 528 | .on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)? |
529 | .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)? | 529 | .on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)? |
530 | .on::<req::SemanticTokensRequest>(handlers::handle_semantic_tokens)? | ||
531 | .on::<req::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)? | ||
530 | .on::<req::Ssr>(handlers::handle_ssr)? | 532 | .on::<req::Ssr>(handlers::handle_ssr)? |
531 | .finish(); | 533 | .finish(); |
532 | Ok(()) | 534 | Ok(()) |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index bb7bab372..9ed53169c 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -16,8 +16,9 @@ use lsp_types::{ | |||
16 | CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem, | 16 | CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem, |
17 | Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, | 17 | Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, |
18 | FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, | 18 | FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, |
19 | PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier, | 19 | PrepareRenameResponse, Range, RenameParams, SemanticTokens, SemanticTokensParams, |
20 | TextEdit, WorkspaceEdit, | 20 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, |
21 | TextDocumentIdentifier, TextEdit, WorkspaceEdit, | ||
21 | }; | 22 | }; |
22 | use ra_ide::{ | 23 | use ra_ide::{ |
23 | AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, | 24 | AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, |
@@ -38,6 +39,7 @@ use crate::{ | |||
38 | diagnostics::DiagnosticTask, | 39 | diagnostics::DiagnosticTask, |
39 | from_json, | 40 | from_json, |
40 | req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, | 41 | req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind}, |
42 | semantic_tokens::SemanticTokensBuilder, | ||
41 | world::WorldSnapshot, | 43 | world::WorldSnapshot, |
42 | LspError, Result, | 44 | LspError, Result, |
43 | }; | 45 | }; |
@@ -952,7 +954,7 @@ fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> | |||
952 | .into_iter() | 954 | .into_iter() |
953 | .map(|h| Decoration { | 955 | .map(|h| Decoration { |
954 | range: h.range.conv_with(&line_index), | 956 | range: h.range.conv_with(&line_index), |
955 | tag: h.tag, | 957 | tag: h.highlight.to_string(), |
956 | binding_hash: h.binding_hash.map(|x| x.to_string()), | 958 | binding_hash: h.binding_hash.map(|x| x.to_string()), |
957 | }) | 959 | }) |
958 | .collect(); | 960 | .collect(); |
@@ -1068,3 +1070,45 @@ pub fn handle_call_hierarchy_outgoing( | |||
1068 | 1070 | ||
1069 | Ok(Some(res)) | 1071 | Ok(Some(res)) |
1070 | } | 1072 | } |
1073 | |||
1074 | pub fn handle_semantic_tokens( | ||
1075 | world: WorldSnapshot, | ||
1076 | params: SemanticTokensParams, | ||
1077 | ) -> Result<Option<SemanticTokensResult>> { | ||
1078 | let _p = profile("handle_semantic_tokens"); | ||
1079 | |||
1080 | let file_id = params.text_document.try_conv_with(&world)?; | ||
1081 | let line_index = world.analysis().file_line_index(file_id)?; | ||
1082 | |||
1083 | let mut builder = SemanticTokensBuilder::default(); | ||
1084 | |||
1085 | for highlight_range in world.analysis().highlight(file_id)?.into_iter() { | ||
1086 | let (token_type, token_modifiers) = highlight_range.highlight.conv(); | ||
1087 | builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); | ||
1088 | } | ||
1089 | |||
1090 | let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; | ||
1091 | |||
1092 | Ok(Some(tokens.into())) | ||
1093 | } | ||
1094 | |||
1095 | pub fn handle_semantic_tokens_range( | ||
1096 | world: WorldSnapshot, | ||
1097 | params: SemanticTokensRangeParams, | ||
1098 | ) -> Result<Option<SemanticTokensRangeResult>> { | ||
1099 | let _p = profile("handle_semantic_tokens_range"); | ||
1100 | |||
1101 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | ||
1102 | let line_index = world.analysis().file_line_index(frange.file_id)?; | ||
1103 | |||
1104 | let mut builder = SemanticTokensBuilder::default(); | ||
1105 | |||
1106 | for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { | ||
1107 | let (token_type, token_modifiers) = highlight_range.highlight.conv(); | ||
1108 | builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); | ||
1109 | } | ||
1110 | |||
1111 | let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; | ||
1112 | |||
1113 | Ok(Some(tokens.into())) | ||
1114 | } | ||
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs index 7ff7f60b3..fd6aef597 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/req.rs | |||
@@ -12,9 +12,10 @@ pub use lsp_types::{ | |||
12 | DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, | 12 | DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, |
13 | PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, | 13 | PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, |
14 | PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange, | 14 | PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange, |
15 | SelectionRangeParams, ServerCapabilities, ShowMessageParams, SignatureHelp, SymbolKind, | 15 | SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams, |
16 | TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkDoneProgressParams, WorkspaceEdit, | 16 | SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams, |
17 | WorkspaceSymbolParams, | 17 | SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, TextEdit, |
18 | WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, | ||
18 | }; | 19 | }; |
19 | 20 | ||
20 | pub enum AnalyzerStatus {} | 21 | pub enum AnalyzerStatus {} |
@@ -111,7 +112,7 @@ pub struct PublishDecorationsParams { | |||
111 | #[serde(rename_all = "camelCase")] | 112 | #[serde(rename_all = "camelCase")] |
112 | pub struct Decoration { | 113 | pub struct Decoration { |
113 | pub range: Range, | 114 | pub range: Range, |
114 | pub tag: &'static str, | 115 | pub tag: String, |
115 | pub binding_hash: Option<String>, | 116 | pub binding_hash: Option<String>, |
116 | } | 117 | } |
117 | 118 | ||
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs new file mode 100644 index 000000000..d8362409d --- /dev/null +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -0,0 +1,114 @@ | |||
1 | //! Semantic Tokens helpers | ||
2 | |||
3 | use std::ops; | ||
4 | |||
5 | use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; | ||
6 | |||
7 | pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); | ||
8 | pub(crate) const CONSTANT: SemanticTokenType = SemanticTokenType::new("constant"); | ||
9 | |||
10 | pub(crate) const MUTABLE: SemanticTokenModifier = SemanticTokenModifier::new("mutable"); | ||
11 | pub(crate) const UNSAFE: SemanticTokenModifier = SemanticTokenModifier::new("unsafe"); | ||
12 | pub(crate) const CONTROL: SemanticTokenModifier = SemanticTokenModifier::new("control"); | ||
13 | pub(crate) const BUILTIN: SemanticTokenModifier = SemanticTokenModifier::new("builtin"); | ||
14 | |||
15 | pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ | ||
16 | SemanticTokenType::COMMENT, | ||
17 | SemanticTokenType::KEYWORD, | ||
18 | SemanticTokenType::STRING, | ||
19 | SemanticTokenType::NUMBER, | ||
20 | SemanticTokenType::REGEXP, | ||
21 | SemanticTokenType::OPERATOR, | ||
22 | SemanticTokenType::NAMESPACE, | ||
23 | SemanticTokenType::TYPE, | ||
24 | SemanticTokenType::STRUCT, | ||
25 | SemanticTokenType::CLASS, | ||
26 | SemanticTokenType::INTERFACE, | ||
27 | SemanticTokenType::ENUM, | ||
28 | SemanticTokenType::TYPE_PARAMETER, | ||
29 | SemanticTokenType::FUNCTION, | ||
30 | SemanticTokenType::MEMBER, | ||
31 | SemanticTokenType::PROPERTY, | ||
32 | SemanticTokenType::MACRO, | ||
33 | SemanticTokenType::VARIABLE, | ||
34 | SemanticTokenType::PARAMETER, | ||
35 | SemanticTokenType::LABEL, | ||
36 | ATTRIBUTE, | ||
37 | CONSTANT, | ||
38 | ]; | ||
39 | |||
40 | pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ | ||
41 | SemanticTokenModifier::DOCUMENTATION, | ||
42 | SemanticTokenModifier::DECLARATION, | ||
43 | SemanticTokenModifier::DEFINITION, | ||
44 | SemanticTokenModifier::REFERENCE, | ||
45 | SemanticTokenModifier::STATIC, | ||
46 | SemanticTokenModifier::ABSTRACT, | ||
47 | SemanticTokenModifier::DEPRECATED, | ||
48 | SemanticTokenModifier::ASYNC, | ||
49 | SemanticTokenModifier::VOLATILE, | ||
50 | SemanticTokenModifier::READONLY, | ||
51 | MUTABLE, | ||
52 | UNSAFE, | ||
53 | CONTROL, | ||
54 | BUILTIN, | ||
55 | ]; | ||
56 | |||
57 | #[derive(Default)] | ||
58 | pub(crate) struct ModifierSet(pub(crate) u32); | ||
59 | |||
60 | impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet { | ||
61 | fn bitor_assign(&mut self, rhs: SemanticTokenModifier) { | ||
62 | let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap(); | ||
63 | self.0 |= 1 << idx; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | /// Tokens are encoded relative to each other. | ||
68 | /// | ||
69 | /// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 | ||
70 | #[derive(Default)] | ||
71 | pub(crate) struct SemanticTokensBuilder { | ||
72 | prev_line: u32, | ||
73 | prev_char: u32, | ||
74 | data: Vec<SemanticToken>, | ||
75 | } | ||
76 | |||
77 | impl SemanticTokensBuilder { | ||
78 | /// Push a new token onto the builder | ||
79 | pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) { | ||
80 | let mut push_line = range.start.line as u32; | ||
81 | let mut push_char = range.start.character as u32; | ||
82 | |||
83 | if !self.data.is_empty() { | ||
84 | push_line -= self.prev_line; | ||
85 | if push_line == 0 { | ||
86 | push_char -= self.prev_char; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | // A token cannot be multiline | ||
91 | let token_len = range.end.character - range.start.character; | ||
92 | |||
93 | let token = SemanticToken { | ||
94 | delta_line: push_line, | ||
95 | delta_start: push_char, | ||
96 | length: token_len as u32, | ||
97 | token_type: token_index, | ||
98 | token_modifiers_bitset: modifier_bitset, | ||
99 | }; | ||
100 | |||
101 | self.data.push(token); | ||
102 | |||
103 | self.prev_line = range.start.line as u32; | ||
104 | self.prev_char = range.start.character as u32; | ||
105 | } | ||
106 | |||
107 | pub fn build(self) -> Vec<SemanticToken> { | ||
108 | self.data | ||
109 | } | ||
110 | } | ||
111 | |||
112 | pub fn type_index(type_: SemanticTokenType) -> u32 { | ||
113 | SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32 | ||
114 | } | ||
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 971592b73..6a7c6d6f9 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml | |||
@@ -9,5 +9,5 @@ doctest = false | |||
9 | 9 | ||
10 | [dependencies] | 10 | [dependencies] |
11 | difference = "2.0.0" | 11 | difference = "2.0.0" |
12 | text_unit = "0.1.9" | 12 | text_unit = "0.1.10" |
13 | serde_json = "1.0.48" | 13 | serde_json = "1.0.48" |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index e6e8d7110..69deddcb5 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -83,6 +83,7 @@ fn try_extract_range(text: &str) -> Option<(TextRange, String)> { | |||
83 | Some((TextRange::from_to(start, end), text)) | 83 | Some((TextRange::from_to(start, end), text)) |
84 | } | 84 | } |
85 | 85 | ||
86 | #[derive(Clone, Copy)] | ||
86 | pub enum RangeOrOffset { | 87 | pub enum RangeOrOffset { |
87 | Range(TextRange), | 88 | Range(TextRange), |
88 | Offset(TextUnit), | 89 | Offset(TextUnit), |
diff --git a/editors/code/.eslintrc.js b/editors/code/.eslintrc.js index 16f18ab2c..c6bf410f4 100644 --- a/editors/code/.eslintrc.js +++ b/editors/code/.eslintrc.js | |||
@@ -32,6 +32,7 @@ module.exports = { | |||
32 | "@typescript-eslint/semi": [ | 32 | "@typescript-eslint/semi": [ |
33 | "error", | 33 | "error", |
34 | "always" | 34 | "always" |
35 | ] | 35 | ], |
36 | "@typescript-eslint/no-unnecessary-type-assertion": "error" | ||
36 | } | 37 | } |
37 | }; | 38 | }; |
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 76ef2aae4..6901363fc 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -112,12 +112,6 @@ | |||
112 | "@types/node": "*" | 112 | "@types/node": "*" |
113 | } | 113 | } |
114 | }, | 114 | }, |
115 | "@types/throttle-debounce": { | ||
116 | "version": "2.1.0", | ||
117 | "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz", | ||
118 | "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==", | ||
119 | "dev": true | ||
120 | }, | ||
121 | "@types/vscode": { | 115 | "@types/vscode": { |
122 | "version": "1.42.0", | 116 | "version": "1.42.0", |
123 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.42.0.tgz", | 117 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.42.0.tgz", |
@@ -1517,11 +1511,6 @@ | |||
1517 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", | 1511 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", |
1518 | "dev": true | 1512 | "dev": true |
1519 | }, | 1513 | }, |
1520 | "throttle-debounce": { | ||
1521 | "version": "2.1.0", | ||
1522 | "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.1.0.tgz", | ||
1523 | "integrity": "sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg==" | ||
1524 | }, | ||
1525 | "through": { | 1514 | "through": { |
1526 | "version": "2.3.8", | 1515 | "version": "2.3.8", |
1527 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", | 1516 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", |
@@ -1575,9 +1564,9 @@ | |||
1575 | } | 1564 | } |
1576 | }, | 1565 | }, |
1577 | "typescript": { | 1566 | "typescript": { |
1578 | "version": "3.7.5", | 1567 | "version": "3.8.2", |
1579 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", | 1568 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", |
1580 | "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", | 1569 | "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==", |
1581 | "dev": true | 1570 | "dev": true |
1582 | }, | 1571 | }, |
1583 | "typescript-formatter": { | 1572 | "typescript-formatter": { |
diff --git a/editors/code/package.json b/editors/code/package.json index 9ef6c6983..ad1101603 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -18,6 +18,7 @@ | |||
18 | "engines": { | 18 | "engines": { |
19 | "vscode": "^1.42.0" | 19 | "vscode": "^1.42.0" |
20 | }, | 20 | }, |
21 | "enableProposedApi": true, | ||
21 | "scripts": { | 22 | "scripts": { |
22 | "vscode:prepublish": "tsc && rollup -c", | 23 | "vscode:prepublish": "tsc && rollup -c", |
23 | "package": "vsce package -o rust-analyzer.vsix", | 24 | "package": "vsce package -o rust-analyzer.vsix", |
@@ -27,7 +28,6 @@ | |||
27 | "dependencies": { | 28 | "dependencies": { |
28 | "jsonc-parser": "^2.1.0", | 29 | "jsonc-parser": "^2.1.0", |
29 | "node-fetch": "^2.6.0", | 30 | "node-fetch": "^2.6.0", |
30 | "throttle-debounce": "^2.1.0", | ||
31 | "vscode-languageclient": "^6.1.1" | 31 | "vscode-languageclient": "^6.1.1" |
32 | }, | 32 | }, |
33 | "devDependencies": { | 33 | "devDependencies": { |
@@ -35,14 +35,13 @@ | |||
35 | "@rollup/plugin-node-resolve": "^7.1.1", | 35 | "@rollup/plugin-node-resolve": "^7.1.1", |
36 | "@types/node": "^12.12.27", | 36 | "@types/node": "^12.12.27", |
37 | "@types/node-fetch": "^2.5.4", | 37 | "@types/node-fetch": "^2.5.4", |
38 | "@types/throttle-debounce": "^2.1.0", | ||
39 | "@types/vscode": "^1.42.0", | 38 | "@types/vscode": "^1.42.0", |
40 | "@typescript-eslint/eslint-plugin": "^2.20.0", | 39 | "@typescript-eslint/eslint-plugin": "^2.20.0", |
41 | "@typescript-eslint/parser": "^2.20.0", | 40 | "@typescript-eslint/parser": "^2.20.0", |
42 | "eslint": "^6.8.0", | 41 | "eslint": "^6.8.0", |
43 | "rollup": "^1.31.1", | 42 | "rollup": "^1.31.1", |
44 | "tslib": "^1.10.0", | 43 | "tslib": "^1.10.0", |
45 | "typescript": "^3.7.5", | 44 | "typescript": "^3.8.2", |
46 | "typescript-formatter": "^7.2.2", | 45 | "typescript-formatter": "^7.2.2", |
47 | "vsce": "^1.73.0" | 46 | "vsce": "^1.73.0" |
48 | }, | 47 | }, |
@@ -169,6 +168,11 @@ | |||
169 | "type": "object", | 168 | "type": "object", |
170 | "title": "Rust Analyzer", | 169 | "title": "Rust Analyzer", |
171 | "properties": { | 170 | "properties": { |
171 | "rust-analyzer.highlighting.semanticTokens": { | ||
172 | "type": "boolean", | ||
173 | "default": false, | ||
174 | "description": "Use proposed semantic tokens API for syntax highlighting" | ||
175 | }, | ||
172 | "rust-analyzer.highlightingOn": { | 176 | "rust-analyzer.highlightingOn": { |
173 | "type": "boolean", | 177 | "type": "boolean", |
174 | "default": false, | 178 | "default": false, |
@@ -375,6 +379,48 @@ | |||
375 | "highContrast": "#BEBEBE" | 379 | "highContrast": "#BEBEBE" |
376 | } | 380 | } |
377 | } | 381 | } |
382 | ], | ||
383 | "semanticTokenTypes": [ | ||
384 | { | ||
385 | "id": "attribute" | ||
386 | }, | ||
387 | { | ||
388 | "id": "constant" | ||
389 | } | ||
390 | ], | ||
391 | "semanticTokenModifiers": [ | ||
392 | { | ||
393 | "id": "mutable" | ||
394 | }, | ||
395 | { | ||
396 | "id": "unsafe" | ||
397 | }, | ||
398 | { | ||
399 | "id": "control" | ||
400 | }, | ||
401 | { | ||
402 | "id": "builtin" | ||
403 | } | ||
404 | ], | ||
405 | "semanticTokenStyleDefaults": [ | ||
406 | { | ||
407 | "selector": "*.mutable", | ||
408 | "light": { | ||
409 | "fontStyle": "underline" | ||
410 | }, | ||
411 | "dark": { | ||
412 | "fontStyle": "underline" | ||
413 | }, | ||
414 | "highContrast": { | ||
415 | "fontStyle": "underline" | ||
416 | } | ||
417 | }, | ||
418 | { | ||
419 | "selector": "constant", | ||
420 | "scope": [ | ||
421 | "entity.name.constant" | ||
422 | ] | ||
423 | } | ||
378 | ] | 424 | ] |
379 | } | 425 | } |
380 | } | 426 | } |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index aaf2ef40e..44bd04c21 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -3,6 +3,7 @@ import * as vscode from 'vscode'; | |||
3 | 3 | ||
4 | import { Config } from './config'; | 4 | import { Config } from './config'; |
5 | import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; | 5 | import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; |
6 | import { SemanticTokensFeature } from 'vscode-languageclient/lib/semanticTokens.proposed'; | ||
6 | 7 | ||
7 | export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> { | 8 | export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> { |
8 | // '.' Is the fallback if no folder is open | 9 | // '.' Is the fallback if no folder is open |
@@ -26,7 +27,7 @@ export async function createClient(config: Config, serverPath: string): Promise< | |||
26 | const clientOptions: lc.LanguageClientOptions = { | 27 | const clientOptions: lc.LanguageClientOptions = { |
27 | documentSelector: [{ scheme: 'file', language: 'rust' }], | 28 | documentSelector: [{ scheme: 'file', language: 'rust' }], |
28 | initializationOptions: { | 29 | initializationOptions: { |
29 | publishDecorations: true, | 30 | publishDecorations: !config.highlightingSemanticTokens, |
30 | lruCapacity: config.lruCapacity, | 31 | lruCapacity: config.lruCapacity, |
31 | maxInlayHintLength: config.maxInlayHintLength, | 32 | maxInlayHintLength: config.maxInlayHintLength, |
32 | cargoWatchEnable: cargoWatchOpts.enable, | 33 | cargoWatchEnable: cargoWatchOpts.enable, |
@@ -83,5 +84,10 @@ export async function createClient(config: Config, serverPath: string): Promise< | |||
83 | // Here we want to just enable CallHierarchyFeature since it is available on stable. | 84 | // Here we want to just enable CallHierarchyFeature since it is available on stable. |
84 | // Note that while the CallHierarchyFeature is stable the LSP protocol is not. | 85 | // Note that while the CallHierarchyFeature is stable the LSP protocol is not. |
85 | res.registerFeature(new CallHierarchyFeature(res)); | 86 | res.registerFeature(new CallHierarchyFeature(res)); |
87 | |||
88 | if (config.highlightingSemanticTokens) { | ||
89 | res.registerFeature(new SemanticTokensFeature(res)); | ||
90 | } | ||
91 | |||
86 | return res; | 92 | return res; |
87 | } | 93 | } |
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts index 6631e8db7..1c6ea399b 100644 --- a/editors/code/src/commands/analyzer_status.ts +++ b/editors/code/src/commands/analyzer_status.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | 2 | ||
3 | import * as ra from '../rust-analyzer-api'; | ||
3 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd } from '../ctx'; |
4 | 5 | ||
5 | // Shows status of rust-analyzer (for debugging) | 6 | // Shows status of rust-analyzer (for debugging) |
@@ -50,10 +51,7 @@ class TextDocumentContentProvider | |||
50 | const client = this.ctx.client; | 51 | const client = this.ctx.client; |
51 | if (!editor || !client) return ''; | 52 | if (!editor || !client) return ''; |
52 | 53 | ||
53 | return client.sendRequest<string>( | 54 | return client.sendRequest(ra.analyzerStatus, null); |
54 | 'rust-analyzer/analyzerStatus', | ||
55 | null, | ||
56 | ); | ||
57 | } | 55 | } |
58 | 56 | ||
59 | get onDidChange(): vscode.Event<vscode.Uri> { | 57 | get onDidChange(): vscode.Event<vscode.Uri> { |
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts index edec9bbc1..23f2ef1d5 100644 --- a/editors/code/src/commands/expand_macro.ts +++ b/editors/code/src/commands/expand_macro.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd } from '../ctx'; |
5 | 5 | ||
@@ -26,12 +26,7 @@ export function expandMacro(ctx: Ctx): Cmd { | |||
26 | }; | 26 | }; |
27 | } | 27 | } |
28 | 28 | ||
29 | interface ExpandedMacro { | 29 | function codeFormat(expanded: ra.ExpandedMacro): string { |
30 | name: string; | ||
31 | expansion: string; | ||
32 | } | ||
33 | |||
34 | function codeFormat(expanded: ExpandedMacro): string { | ||
35 | let result = `// Recursive expansion of ${expanded.name}! macro\n`; | 30 | let result = `// Recursive expansion of ${expanded.name}! macro\n`; |
36 | result += '// ' + '='.repeat(result.length - 3); | 31 | result += '// ' + '='.repeat(result.length - 3); |
37 | result += '\n\n'; | 32 | result += '\n\n'; |
@@ -54,14 +49,11 @@ class TextDocumentContentProvider | |||
54 | if (!editor || !client) return ''; | 49 | if (!editor || !client) return ''; |
55 | 50 | ||
56 | const position = editor.selection.active; | 51 | const position = editor.selection.active; |
57 | const request: lc.TextDocumentPositionParams = { | 52 | |
53 | const expanded = await client.sendRequest(ra.expandMacro, { | ||
58 | textDocument: { uri: editor.document.uri.toString() }, | 54 | textDocument: { uri: editor.document.uri.toString() }, |
59 | position, | 55 | position, |
60 | }; | 56 | }); |
61 | const expanded = await client.sendRequest<ExpandedMacro>( | ||
62 | 'rust-analyzer/expandMacro', | ||
63 | request, | ||
64 | ); | ||
65 | 57 | ||
66 | if (expanded == null) return 'Not available'; | 58 | if (expanded == null) return 'Not available'; |
67 | 59 | ||
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 839245f48..bdb7fc3b0 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | import * as ra from '../rust-analyzer-api'; | ||
3 | 4 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 5 | import { Ctx, Cmd } from '../ctx'; |
5 | import * as sourceChange from '../source_change'; | 6 | import * as sourceChange from '../source_change'; |
@@ -16,9 +17,7 @@ export * from './ssr'; | |||
16 | export * from './server_version'; | 17 | export * from './server_version'; |
17 | 18 | ||
18 | export function collectGarbage(ctx: Ctx): Cmd { | 19 | export function collectGarbage(ctx: Ctx): Cmd { |
19 | return async () => { | 20 | return async () => ctx.client.sendRequest(ra.collectGarbage, null); |
20 | await ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null); | ||
21 | }; | ||
22 | } | 21 | } |
23 | 22 | ||
24 | export function showReferences(ctx: Ctx): Cmd { | 23 | export function showReferences(ctx: Ctx): Cmd { |
@@ -36,13 +35,13 @@ export function showReferences(ctx: Ctx): Cmd { | |||
36 | } | 35 | } |
37 | 36 | ||
38 | export function applySourceChange(ctx: Ctx): Cmd { | 37 | export function applySourceChange(ctx: Ctx): Cmd { |
39 | return async (change: sourceChange.SourceChange) => { | 38 | return async (change: ra.SourceChange) => { |
40 | await sourceChange.applySourceChange(ctx, change); | 39 | await sourceChange.applySourceChange(ctx, change); |
41 | }; | 40 | }; |
42 | } | 41 | } |
43 | 42 | ||
44 | export function selectAndApplySourceChange(ctx: Ctx): Cmd { | 43 | export function selectAndApplySourceChange(ctx: Ctx): Cmd { |
45 | return async (changes: sourceChange.SourceChange[]) => { | 44 | return async (changes: ra.SourceChange[]) => { |
46 | if (changes.length === 1) { | 45 | if (changes.length === 1) { |
47 | await sourceChange.applySourceChange(ctx, changes[0]); | 46 | await sourceChange.applySourceChange(ctx, changes[0]); |
48 | } else if (changes.length > 0) { | 47 | } else if (changes.length > 0) { |
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts index 7b08c3255..de0614653 100644 --- a/editors/code/src/commands/join_lines.ts +++ b/editors/code/src/commands/join_lines.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as lc from 'vscode-languageclient'; | 1 | import * as ra from '../rust-analyzer-api'; |
2 | 2 | ||
3 | import { Ctx, Cmd } from '../ctx'; | 3 | import { Ctx, Cmd } from '../ctx'; |
4 | import { applySourceChange, SourceChange } from '../source_change'; | 4 | import { applySourceChange } from '../source_change'; |
5 | 5 | ||
6 | export function joinLines(ctx: Ctx): Cmd { | 6 | export function joinLines(ctx: Ctx): Cmd { |
7 | return async () => { | 7 | return async () => { |
@@ -9,19 +9,10 @@ export function joinLines(ctx: Ctx): Cmd { | |||
9 | const client = ctx.client; | 9 | const client = ctx.client; |
10 | if (!editor || !client) return; | 10 | if (!editor || !client) return; |
11 | 11 | ||
12 | const request: JoinLinesParams = { | 12 | const change = await client.sendRequest(ra.joinLines, { |
13 | range: client.code2ProtocolConverter.asRange(editor.selection), | 13 | range: client.code2ProtocolConverter.asRange(editor.selection), |
14 | textDocument: { uri: editor.document.uri.toString() }, | 14 | textDocument: { uri: editor.document.uri.toString() }, |
15 | }; | 15 | }); |
16 | const change = await client.sendRequest<SourceChange>( | ||
17 | 'rust-analyzer/joinLines', | ||
18 | request, | ||
19 | ); | ||
20 | await applySourceChange(ctx, change); | 16 | await applySourceChange(ctx, change); |
21 | }; | 17 | }; |
22 | } | 18 | } |
23 | |||
24 | interface JoinLinesParams { | ||
25 | textDocument: lc.TextDocumentIdentifier; | ||
26 | range: lc.Range; | ||
27 | } | ||
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts index 7c58bb7e7..a60776e2d 100644 --- a/editors/code/src/commands/matching_brace.ts +++ b/editors/code/src/commands/matching_brace.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd } from '../ctx'; |
5 | 5 | ||
@@ -9,16 +9,12 @@ export function matchingBrace(ctx: Ctx): Cmd { | |||
9 | const client = ctx.client; | 9 | const client = ctx.client; |
10 | if (!editor || !client) return; | 10 | if (!editor || !client) return; |
11 | 11 | ||
12 | const request: FindMatchingBraceParams = { | 12 | const response = await client.sendRequest(ra.findMatchingBrace, { |
13 | textDocument: { uri: editor.document.uri.toString() }, | 13 | textDocument: { uri: editor.document.uri.toString() }, |
14 | offsets: editor.selections.map(s => | 14 | offsets: editor.selections.map(s => |
15 | client.code2ProtocolConverter.asPosition(s.active), | 15 | client.code2ProtocolConverter.asPosition(s.active), |
16 | ), | 16 | ), |
17 | }; | 17 | }); |
18 | const response = await client.sendRequest<lc.Position[]>( | ||
19 | 'rust-analyzer/findMatchingBrace', | ||
20 | request, | ||
21 | ); | ||
22 | editor.selections = editor.selections.map((sel, idx) => { | 18 | editor.selections = editor.selections.map((sel, idx) => { |
23 | const active = client.protocol2CodeConverter.asPosition( | 19 | const active = client.protocol2CodeConverter.asPosition( |
24 | response[idx], | 20 | response[idx], |
@@ -29,8 +25,3 @@ export function matchingBrace(ctx: Ctx): Cmd { | |||
29 | editor.revealRange(editor.selection); | 25 | editor.revealRange(editor.selection); |
30 | }; | 26 | }; |
31 | } | 27 | } |
32 | |||
33 | interface FindMatchingBraceParams { | ||
34 | textDocument: lc.TextDocumentIdentifier; | ||
35 | offsets: lc.Position[]; | ||
36 | } | ||
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts index 25eaebcbe..285849db7 100644 --- a/editors/code/src/commands/on_enter.ts +++ b/editors/code/src/commands/on_enter.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { applySourceChange, SourceChange } from '../source_change'; | 4 | import { applySourceChange } from '../source_change'; |
5 | import { Cmd, Ctx } from '../ctx'; | 5 | import { Cmd, Ctx } from '../ctx'; |
6 | 6 | ||
7 | async function handleKeypress(ctx: Ctx) { | 7 | async function handleKeypress(ctx: Ctx) { |
@@ -10,16 +10,15 @@ async function handleKeypress(ctx: Ctx) { | |||
10 | 10 | ||
11 | if (!editor || !client) return false; | 11 | if (!editor || !client) return false; |
12 | 12 | ||
13 | const request: lc.TextDocumentPositionParams = { | 13 | const change = await client.sendRequest(ra.onEnter, { |
14 | textDocument: { uri: editor.document.uri.toString() }, | 14 | textDocument: { uri: editor.document.uri.toString() }, |
15 | position: client.code2ProtocolConverter.asPosition( | 15 | position: client.code2ProtocolConverter.asPosition( |
16 | editor.selection.active, | 16 | editor.selection.active, |
17 | ), | 17 | ), |
18 | }; | 18 | }).catch(_error => { |
19 | const change = await client.sendRequest<undefined | SourceChange>( | 19 | // client.logFailedRequest(OnEnterRequest.type, error); |
20 | 'rust-analyzer/onEnter', | 20 | return null; |
21 | request, | 21 | }); |
22 | ); | ||
23 | if (!change) return false; | 22 | if (!change) return false; |
24 | 23 | ||
25 | await applySourceChange(ctx, change); | 24 | await applySourceChange(ctx, change); |
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts index bf40b4021..8f78ddd71 100644 --- a/editors/code/src/commands/parent_module.ts +++ b/editors/code/src/commands/parent_module.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd } from '../ctx'; |
5 | 5 | ||
@@ -9,16 +9,12 @@ export function parentModule(ctx: Ctx): Cmd { | |||
9 | const client = ctx.client; | 9 | const client = ctx.client; |
10 | if (!editor || !client) return; | 10 | if (!editor || !client) return; |
11 | 11 | ||
12 | const request: lc.TextDocumentPositionParams = { | 12 | const response = await client.sendRequest(ra.parentModule, { |
13 | textDocument: { uri: editor.document.uri.toString() }, | 13 | textDocument: { uri: editor.document.uri.toString() }, |
14 | position: client.code2ProtocolConverter.asPosition( | 14 | position: client.code2ProtocolConverter.asPosition( |
15 | editor.selection.active, | 15 | editor.selection.active, |
16 | ), | 16 | ), |
17 | }; | 17 | }); |
18 | const response = await client.sendRequest<lc.Location[]>( | ||
19 | 'rust-analyzer/parentModule', | ||
20 | request, | ||
21 | ); | ||
22 | const loc = response[0]; | 18 | const loc = response[0]; |
23 | if (loc == null) return; | 19 | if (loc == null) return; |
24 | 20 | ||
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 7919997ce..06b513466 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | import * as ra from '../rust-analyzer-api'; | ||
3 | 4 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 5 | import { Ctx, Cmd } from '../ctx'; |
5 | 6 | ||
@@ -14,16 +15,13 @@ export function run(ctx: Ctx): Cmd { | |||
14 | const textDocument: lc.TextDocumentIdentifier = { | 15 | const textDocument: lc.TextDocumentIdentifier = { |
15 | uri: editor.document.uri.toString(), | 16 | uri: editor.document.uri.toString(), |
16 | }; | 17 | }; |
17 | const params: RunnablesParams = { | 18 | |
19 | const runnables = await client.sendRequest(ra.runnables, { | ||
18 | textDocument, | 20 | textDocument, |
19 | position: client.code2ProtocolConverter.asPosition( | 21 | position: client.code2ProtocolConverter.asPosition( |
20 | editor.selection.active, | 22 | editor.selection.active, |
21 | ), | 23 | ), |
22 | }; | 24 | }); |
23 | const runnables = await client.sendRequest<Runnable[]>( | ||
24 | 'rust-analyzer/runnables', | ||
25 | params, | ||
26 | ); | ||
27 | const items: RunnableQuickPick[] = []; | 25 | const items: RunnableQuickPick[] = []; |
28 | if (prevRunnable) { | 26 | if (prevRunnable) { |
29 | items.push(prevRunnable); | 27 | items.push(prevRunnable); |
@@ -48,7 +46,7 @@ export function run(ctx: Ctx): Cmd { | |||
48 | } | 46 | } |
49 | 47 | ||
50 | export function runSingle(ctx: Ctx): Cmd { | 48 | export function runSingle(ctx: Ctx): Cmd { |
51 | return async (runnable: Runnable) => { | 49 | return async (runnable: ra.Runnable) => { |
52 | const editor = ctx.activeRustEditor; | 50 | const editor = ctx.activeRustEditor; |
53 | if (!editor) return; | 51 | if (!editor) return; |
54 | 52 | ||
@@ -64,26 +62,13 @@ export function runSingle(ctx: Ctx): Cmd { | |||
64 | }; | 62 | }; |
65 | } | 63 | } |
66 | 64 | ||
67 | interface RunnablesParams { | ||
68 | textDocument: lc.TextDocumentIdentifier; | ||
69 | position?: lc.Position; | ||
70 | } | ||
71 | |||
72 | interface Runnable { | ||
73 | label: string; | ||
74 | bin: string; | ||
75 | args: string[]; | ||
76 | env: { [index: string]: string }; | ||
77 | cwd?: string; | ||
78 | } | ||
79 | |||
80 | class RunnableQuickPick implements vscode.QuickPickItem { | 65 | class RunnableQuickPick implements vscode.QuickPickItem { |
81 | public label: string; | 66 | public label: string; |
82 | public description?: string | undefined; | 67 | public description?: string | undefined; |
83 | public detail?: string | undefined; | 68 | public detail?: string | undefined; |
84 | public picked?: boolean | undefined; | 69 | public picked?: boolean | undefined; |
85 | 70 | ||
86 | constructor(public runnable: Runnable) { | 71 | constructor(public runnable: ra.Runnable) { |
87 | this.label = runnable.label; | 72 | this.label = runnable.label; |
88 | } | 73 | } |
89 | } | 74 | } |
@@ -96,7 +81,7 @@ interface CargoTaskDefinition extends vscode.TaskDefinition { | |||
96 | env?: { [key: string]: string }; | 81 | env?: { [key: string]: string }; |
97 | } | 82 | } |
98 | 83 | ||
99 | function createTask(spec: Runnable): vscode.Task { | 84 | function createTask(spec: ra.Runnable): vscode.Task { |
100 | const TASK_SOURCE = 'Rust'; | 85 | const TASK_SOURCE = 'Rust'; |
101 | const definition: CargoTaskDefinition = { | 86 | const definition: CargoTaskDefinition = { |
102 | type: 'cargo', | 87 | type: 'cargo', |
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts index 9b814612a..eee48c693 100644 --- a/editors/code/src/commands/ssr.ts +++ b/editors/code/src/commands/ssr.ts | |||
@@ -1,6 +1,8 @@ | |||
1 | import { Ctx, Cmd } from '../ctx'; | ||
2 | import { applySourceChange, SourceChange } from '../source_change'; | ||
3 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as ra from "../rust-analyzer-api"; | ||
3 | |||
4 | import { Ctx, Cmd } from '../ctx'; | ||
5 | import { applySourceChange } from '../source_change'; | ||
4 | 6 | ||
5 | export function ssr(ctx: Ctx): Cmd { | 7 | export function ssr(ctx: Ctx): Cmd { |
6 | return async () => { | 8 | return async () => { |
@@ -21,16 +23,8 @@ export function ssr(ctx: Ctx): Cmd { | |||
21 | 23 | ||
22 | if (!request) return; | 24 | if (!request) return; |
23 | 25 | ||
24 | const ssrRequest: SsrRequest = { arg: request }; | 26 | const change = await client.sendRequest(ra.ssr, { arg: request }); |
25 | const change = await client.sendRequest<SourceChange>( | ||
26 | 'rust-analyzer/ssr', | ||
27 | ssrRequest, | ||
28 | ); | ||
29 | 27 | ||
30 | await applySourceChange(ctx, change); | 28 | await applySourceChange(ctx, change); |
31 | }; | 29 | }; |
32 | } | 30 | } |
33 | |||
34 | interface SsrRequest { | ||
35 | arg: string; | ||
36 | } | ||
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts index 2887c96c8..7218bfb90 100644 --- a/editors/code/src/commands/syntax_tree.ts +++ b/editors/code/src/commands/syntax_tree.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd } from '../ctx'; |
5 | 5 | ||
@@ -61,13 +61,8 @@ function afterLs(f: () => void) { | |||
61 | setTimeout(f, 10); | 61 | setTimeout(f, 10); |
62 | } | 62 | } |
63 | 63 | ||
64 | interface SyntaxTreeParams { | ||
65 | textDocument: lc.TextDocumentIdentifier; | ||
66 | range?: lc.Range; | ||
67 | } | ||
68 | 64 | ||
69 | class TextDocumentContentProvider | 65 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { |
70 | implements vscode.TextDocumentContentProvider { | ||
71 | uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); | 66 | uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); |
72 | eventEmitter = new vscode.EventEmitter<vscode.Uri>(); | 67 | eventEmitter = new vscode.EventEmitter<vscode.Uri>(); |
73 | 68 | ||
@@ -79,23 +74,15 @@ class TextDocumentContentProvider | |||
79 | const client = this.ctx.client; | 74 | const client = this.ctx.client; |
80 | if (!editor || !client) return ''; | 75 | if (!editor || !client) return ''; |
81 | 76 | ||
82 | let range: lc.Range | undefined; | ||
83 | |||
84 | // When the range based query is enabled we take the range of the selection | 77 | // When the range based query is enabled we take the range of the selection |
85 | if (uri.query === 'range=true') { | 78 | const range = uri.query === 'range=true' && !editor.selection.isEmpty |
86 | range = editor.selection.isEmpty | 79 | ? client.code2ProtocolConverter.asRange(editor.selection) |
87 | ? undefined | 80 | : null; |
88 | : client.code2ProtocolConverter.asRange(editor.selection); | ||
89 | } | ||
90 | 81 | ||
91 | const request: SyntaxTreeParams = { | 82 | return client.sendRequest(ra.syntaxTree, { |
92 | textDocument: { uri: editor.document.uri.toString() }, | 83 | textDocument: { uri: editor.document.uri.toString() }, |
93 | range, | 84 | range, |
94 | }; | 85 | }); |
95 | return client.sendRequest<string>( | ||
96 | 'rust-analyzer/syntaxTree', | ||
97 | request, | ||
98 | ); | ||
99 | } | 86 | } |
100 | 87 | ||
101 | get onDidChange(): vscode.Event<vscode.Uri> { | 88 | get onDidChange(): vscode.Event<vscode.Uri> { |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 47e8cd45d..bf915102c 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -22,6 +22,7 @@ export class Config { | |||
22 | private static readonly requiresReloadOpts = [ | 22 | private static readonly requiresReloadOpts = [ |
23 | "cargoFeatures", | 23 | "cargoFeatures", |
24 | "cargo-watch", | 24 | "cargo-watch", |
25 | "highlighting.semanticTokens" | ||
25 | ] | 26 | ] |
26 | .map(opt => `${Config.rootSection}.${opt}`); | 27 | .map(opt => `${Config.rootSection}.${opt}`); |
27 | 28 | ||
@@ -143,6 +144,7 @@ export class Config { | |||
143 | // We don't do runtime config validation here for simplicity. More on stackoverflow: | 144 | // We don't do runtime config validation here for simplicity. More on stackoverflow: |
144 | // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension | 145 | // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension |
145 | 146 | ||
147 | get highlightingSemanticTokens() { return this.cfg.get("highlighting.semanticTokens") as boolean; } | ||
146 | get highlightingOn() { return this.cfg.get("highlightingOn") as boolean; } | 148 | get highlightingOn() { return this.cfg.get("highlightingOn") as boolean; } |
147 | get rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; } | 149 | get rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; } |
148 | get lruCapacity() { return this.cfg.get("lruCapacity") as null | number; } | 150 | get lruCapacity() { return this.cfg.get("lruCapacity") as null | number; } |
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index 77b4a1a68..3e0cbdc56 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from './rust-analyzer-api'; |
3 | 3 | ||
4 | import { ColorTheme, TextMateRuleSettings } from './color_theme'; | 4 | import { ColorTheme, TextMateRuleSettings } from './color_theme'; |
5 | 5 | ||
@@ -8,29 +8,25 @@ import { sendRequestWithRetry } from './util'; | |||
8 | 8 | ||
9 | export function activateHighlighting(ctx: Ctx) { | 9 | export function activateHighlighting(ctx: Ctx) { |
10 | const highlighter = new Highlighter(ctx); | 10 | const highlighter = new Highlighter(ctx); |
11 | const client = ctx.client; | ||
12 | if (client != null) { | ||
13 | client.onNotification( | ||
14 | 'rust-analyzer/publishDecorations', | ||
15 | (params: PublishDecorationsParams) => { | ||
16 | if (!ctx.config.highlightingOn) return; | ||
17 | |||
18 | const targetEditor = vscode.window.visibleTextEditors.find( | ||
19 | editor => { | ||
20 | const unescapedUri = unescape( | ||
21 | editor.document.uri.toString(), | ||
22 | ); | ||
23 | // Unescaped URI looks like: | ||
24 | // file:///c:/Workspace/ra-test/src/main.rs | ||
25 | return unescapedUri === params.uri; | ||
26 | }, | ||
27 | ); | ||
28 | if (!targetEditor) return; | ||
29 | 11 | ||
30 | highlighter.setHighlights(targetEditor, params.decorations); | 12 | ctx.client.onNotification(ra.publishDecorations, params => { |
13 | if (!ctx.config.highlightingOn) return; | ||
14 | |||
15 | const targetEditor = vscode.window.visibleTextEditors.find( | ||
16 | editor => { | ||
17 | const unescapedUri = unescape( | ||
18 | editor.document.uri.toString(), | ||
19 | ); | ||
20 | // Unescaped URI looks like: | ||
21 | // file:///c:/Workspace/ra-test/src/main.rs | ||
22 | return unescapedUri === params.uri; | ||
31 | }, | 23 | }, |
32 | ); | 24 | ); |
33 | } | 25 | if (!targetEditor) return; |
26 | |||
27 | highlighter.setHighlights(targetEditor, params.decorations); | ||
28 | }); | ||
29 | |||
34 | 30 | ||
35 | vscode.workspace.onDidChangeConfiguration( | 31 | vscode.workspace.onDidChangeConfiguration( |
36 | _ => highlighter.removeHighlights(), | 32 | _ => highlighter.removeHighlights(), |
@@ -45,13 +41,10 @@ export function activateHighlighting(ctx: Ctx) { | |||
45 | const client = ctx.client; | 41 | const client = ctx.client; |
46 | if (!client) return; | 42 | if (!client) return; |
47 | 43 | ||
48 | const params: lc.TextDocumentIdentifier = { | 44 | const decorations = await sendRequestWithRetry( |
49 | uri: editor.document.uri.toString(), | ||
50 | }; | ||
51 | const decorations = await sendRequestWithRetry<Decoration[]>( | ||
52 | client, | 45 | client, |
53 | 'rust-analyzer/decorationsRequest', | 46 | ra.decorationsRequest, |
54 | params, | 47 | { uri: editor.document.uri.toString() }, |
55 | ); | 48 | ); |
56 | highlighter.setHighlights(editor, decorations); | 49 | highlighter.setHighlights(editor, decorations); |
57 | }, | 50 | }, |
@@ -60,17 +53,6 @@ export function activateHighlighting(ctx: Ctx) { | |||
60 | ); | 53 | ); |
61 | } | 54 | } |
62 | 55 | ||
63 | interface PublishDecorationsParams { | ||
64 | uri: string; | ||
65 | decorations: Decoration[]; | ||
66 | } | ||
67 | |||
68 | interface Decoration { | ||
69 | range: lc.Range; | ||
70 | tag: string; | ||
71 | bindingHash?: string; | ||
72 | } | ||
73 | |||
74 | // Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76 | 56 | // Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76 |
75 | function fancify(seed: string, shade: 'light' | 'dark') { | 57 | function fancify(seed: string, shade: 'light' | 'dark') { |
76 | const random = randomU32Numbers(hashString(seed)); | 58 | const random = randomU32Numbers(hashString(seed)); |
@@ -108,7 +90,7 @@ class Highlighter { | |||
108 | this.decorations = null; | 90 | this.decorations = null; |
109 | } | 91 | } |
110 | 92 | ||
111 | public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) { | 93 | public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) { |
112 | const client = this.ctx.client; | 94 | const client = this.ctx.client; |
113 | if (!client) return; | 95 | if (!client) return; |
114 | // Initialize decorations if necessary | 96 | // Initialize decorations if necessary |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 5f9229efb..5951cf1b4 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as ra from './rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx } from './ctx'; | 4 | import { Ctx } from './ctx'; |
5 | import { log, sendRequestWithRetry } from './util'; | 5 | import { log, sendRequestWithRetry } from './util'; |
@@ -39,16 +39,6 @@ export function activateInlayHints(ctx: Ctx) { | |||
39 | void hintsUpdater.setEnabled(ctx.config.displayInlayHints); | 39 | void hintsUpdater.setEnabled(ctx.config.displayInlayHints); |
40 | } | 40 | } |
41 | 41 | ||
42 | interface InlayHintsParams { | ||
43 | textDocument: lc.TextDocumentIdentifier; | ||
44 | } | ||
45 | |||
46 | interface InlayHint { | ||
47 | range: vscode.Range; | ||
48 | kind: "TypeHint" | "ParameterHint"; | ||
49 | label: string; | ||
50 | } | ||
51 | |||
52 | const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ | 42 | const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ |
53 | after: { | 43 | after: { |
54 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | 44 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), |
@@ -107,9 +97,9 @@ class HintsUpdater { | |||
107 | if (newHints == null) return; | 97 | if (newHints == null) return; |
108 | 98 | ||
109 | const newTypeDecorations = newHints | 99 | const newTypeDecorations = newHints |
110 | .filter(hint => hint.kind === 'TypeHint') | 100 | .filter(hint => hint.kind === ra.InlayKind.TypeHint) |
111 | .map(hint => ({ | 101 | .map(hint => ({ |
112 | range: hint.range, | 102 | range: this.ctx.client.protocol2CodeConverter.asRange(hint.range), |
113 | renderOptions: { | 103 | renderOptions: { |
114 | after: { | 104 | after: { |
115 | contentText: `: ${hint.label}`, | 105 | contentText: `: ${hint.label}`, |
@@ -119,9 +109,9 @@ class HintsUpdater { | |||
119 | this.setTypeDecorations(editor, newTypeDecorations); | 109 | this.setTypeDecorations(editor, newTypeDecorations); |
120 | 110 | ||
121 | const newParameterDecorations = newHints | 111 | const newParameterDecorations = newHints |
122 | .filter(hint => hint.kind === 'ParameterHint') | 112 | .filter(hint => hint.kind === ra.InlayKind.ParameterHint) |
123 | .map(hint => ({ | 113 | .map(hint => ({ |
124 | range: hint.range, | 114 | range: this.ctx.client.protocol2CodeConverter.asRange(hint.range), |
125 | renderOptions: { | 115 | renderOptions: { |
126 | before: { | 116 | before: { |
127 | contentText: `${hint.label}: `, | 117 | contentText: `${hint.label}: `, |
@@ -151,20 +141,15 @@ class HintsUpdater { | |||
151 | ); | 141 | ); |
152 | } | 142 | } |
153 | 143 | ||
154 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { | 144 | private async queryHints(documentUri: string): Promise<ra.InlayHint[] | null> { |
155 | this.pending.get(documentUri)?.cancel(); | 145 | this.pending.get(documentUri)?.cancel(); |
156 | 146 | ||
157 | const tokenSource = new vscode.CancellationTokenSource(); | 147 | const tokenSource = new vscode.CancellationTokenSource(); |
158 | this.pending.set(documentUri, tokenSource); | 148 | this.pending.set(documentUri, tokenSource); |
159 | 149 | ||
160 | const request: InlayHintsParams = { textDocument: { uri: documentUri } }; | 150 | const request = { textDocument: { uri: documentUri } }; |
161 | 151 | ||
162 | return sendRequestWithRetry<InlayHint[]>( | 152 | return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token) |
163 | this.ctx.client, | ||
164 | 'rust-analyzer/inlayHints', | ||
165 | request, | ||
166 | tokenSource.token | ||
167 | ) | ||
168 | .catch(_ => null) | 153 | .catch(_ => null) |
169 | .finally(() => { | 154 | .finally(() => { |
170 | if (!tokenSource.token.isCancellationRequested) { | 155 | if (!tokenSource.token.isCancellationRequested) { |
diff --git a/editors/code/src/installation/download_artifact.ts b/editors/code/src/installation/download_artifact.ts index 356723aba..8ed99bf0a 100644 --- a/editors/code/src/installation/download_artifact.ts +++ b/editors/code/src/installation/download_artifact.ts | |||
@@ -5,7 +5,6 @@ import { strict as assert } from "assert"; | |||
5 | 5 | ||
6 | import { ArtifactReleaseInfo } from "./interfaces"; | 6 | import { ArtifactReleaseInfo } from "./interfaces"; |
7 | import { downloadFile } from "./download_file"; | 7 | import { downloadFile } from "./download_file"; |
8 | import { throttle } from "throttle-debounce"; | ||
9 | 8 | ||
10 | /** | 9 | /** |
11 | * Downloads artifact from given `downloadUrl`. | 10 | * Downloads artifact from given `downloadUrl`. |
@@ -38,19 +37,15 @@ export async function downloadArtifact( | |||
38 | async (progress, _cancellationToken) => { | 37 | async (progress, _cancellationToken) => { |
39 | let lastPrecentage = 0; | 38 | let lastPrecentage = 0; |
40 | const filePermissions = 0o755; // (rwx, r_x, r_x) | 39 | const filePermissions = 0o755; // (rwx, r_x, r_x) |
41 | await downloadFile(downloadUrl, installationPath, filePermissions, throttle( | 40 | await downloadFile(downloadUrl, installationPath, filePermissions, (readBytes, totalBytes) => { |
42 | 200, | 41 | const newPercentage = (readBytes / totalBytes) * 100; |
43 | /* noTrailing: */ true, | 42 | progress.report({ |
44 | (readBytes, totalBytes) => { | 43 | message: newPercentage.toFixed(0) + "%", |
45 | const newPercentage = (readBytes / totalBytes) * 100; | 44 | increment: newPercentage - lastPrecentage |
46 | progress.report({ | 45 | }); |
47 | message: newPercentage.toFixed(0) + "%", | ||
48 | increment: newPercentage - lastPrecentage | ||
49 | }); | ||
50 | 46 | ||
51 | lastPrecentage = newPercentage; | 47 | lastPrecentage = newPercentage; |
52 | }) | 48 | }); |
53 | ); | ||
54 | } | 49 | } |
55 | ); | 50 | ); |
56 | } | 51 | } |
diff --git a/editors/code/src/installation/fetch_artifact_release_info.ts b/editors/code/src/installation/fetch_artifact_release_info.ts index 1b6fc8d48..b1b5a3485 100644 --- a/editors/code/src/installation/fetch_artifact_release_info.ts +++ b/editors/code/src/installation/fetch_artifact_release_info.ts | |||
@@ -4,41 +4,61 @@ import { log } from "../util"; | |||
4 | 4 | ||
5 | const GITHUB_API_ENDPOINT_URL = "https://api.github.com"; | 5 | const GITHUB_API_ENDPOINT_URL = "https://api.github.com"; |
6 | 6 | ||
7 | |||
8 | /** | 7 | /** |
9 | * Fetches the release with `releaseTag` (or just latest release when not specified) | 8 | * Fetches the release with `releaseTag` from GitHub `repo` and |
10 | * from GitHub `repo` and returns metadata about `artifactFileName` shipped with | 9 | * returns metadata about `artifactFileName` shipped with |
11 | * this release or `null` if no such artifact was published. | 10 | * this release. |
11 | * | ||
12 | * @throws Error upon network failure or if no such repository, release, or artifact exists. | ||
12 | */ | 13 | */ |
13 | export async function fetchArtifactReleaseInfo( | 14 | export async function fetchArtifactReleaseInfo( |
14 | repo: GithubRepo, artifactFileName: string, releaseTag?: string | 15 | repo: GithubRepo, |
15 | ): Promise<null | ArtifactReleaseInfo> { | 16 | artifactFileName: string, |
17 | releaseTag: string | ||
18 | ): Promise<ArtifactReleaseInfo> { | ||
16 | 19 | ||
17 | const repoOwner = encodeURIComponent(repo.owner); | 20 | const repoOwner = encodeURIComponent(repo.owner); |
18 | const repoName = encodeURIComponent(repo.name); | 21 | const repoName = encodeURIComponent(repo.name); |
19 | 22 | ||
20 | const apiEndpointPath = releaseTag | 23 | const apiEndpointPath = `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}`; |
21 | ? `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}` | ||
22 | : `/repos/${repoOwner}/${repoName}/releases/latest`; | ||
23 | 24 | ||
24 | const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath; | 25 | const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath; |
25 | 26 | ||
26 | // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`) | ||
27 | |||
28 | log.debug("Issuing request for released artifacts metadata to", requestUrl); | 27 | log.debug("Issuing request for released artifacts metadata to", requestUrl); |
29 | 28 | ||
30 | // FIXME: handle non-ok response | 29 | const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }); |
31 | const response: GithubRelease = await fetch(requestUrl, { | ||
32 | headers: { Accept: "application/vnd.github.v3+json" } | ||
33 | }) | ||
34 | .then(res => res.json()); | ||
35 | 30 | ||
36 | const artifact = response.assets.find(artifact => artifact.name === artifactFileName); | 31 | if (!response.ok) { |
32 | log.error("Error fetching artifact release info", { | ||
33 | requestUrl, | ||
34 | releaseTag, | ||
35 | artifactFileName, | ||
36 | response: { | ||
37 | headers: response.headers, | ||
38 | status: response.status, | ||
39 | body: await response.text(), | ||
40 | } | ||
41 | }); | ||
42 | |||
43 | throw new Error( | ||
44 | `Got response ${response.status} when trying to fetch ` + | ||
45 | `"${artifactFileName}" artifact release info for ${releaseTag} release` | ||
46 | ); | ||
47 | } | ||
37 | 48 | ||
38 | if (!artifact) return null; | 49 | // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`) |
50 | const release: GithubRelease = await response.json(); | ||
51 | |||
52 | const artifact = release.assets.find(artifact => artifact.name === artifactFileName); | ||
53 | |||
54 | if (!artifact) { | ||
55 | throw new Error( | ||
56 | `Artifact ${artifactFileName} was not found in ${release.name} release!` | ||
57 | ); | ||
58 | } | ||
39 | 59 | ||
40 | return { | 60 | return { |
41 | releaseName: response.name, | 61 | releaseName: release.name, |
42 | downloadUrl: artifact.browser_download_url | 62 | downloadUrl: artifact.browser_download_url |
43 | }; | 63 | }; |
44 | 64 | ||
diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts index 685abfdc6..cb5e56844 100644 --- a/editors/code/src/installation/server.ts +++ b/editors/code/src/installation/server.ts | |||
@@ -63,7 +63,7 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n | |||
63 | 63 | ||
64 | async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> { | 64 | async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> { |
65 | try { | 65 | try { |
66 | const releaseInfo = (await fetchArtifactReleaseInfo(source.repo, source.file, source.version))!; | 66 | const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.version); |
67 | 67 | ||
68 | await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); | 68 | await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); |
69 | await setServerVersion(source.storage, releaseInfo.releaseName); | 69 | await setServerVersion(source.storage, releaseInfo.releaseName); |
@@ -88,9 +88,12 @@ async function downloadServer(source: BinarySource.GithubRelease): Promise<boole | |||
88 | return false; | 88 | return false; |
89 | } | 89 | } |
90 | 90 | ||
91 | if (!isBinaryAvailable(path.join(source.dir, source.file))) assert(false, | 91 | const binaryPath = path.join(source.dir, source.file); |
92 | |||
93 | assert(isBinaryAvailable(binaryPath), | ||
92 | `Downloaded language server binary is not functional.` + | 94 | `Downloaded language server binary is not functional.` + |
93 | `Downloaded from: ${JSON.stringify(source, null, 4)}` | 95 | `Downloaded from GitHub repo ${source.repo.owner}/${source.repo.name} ` + |
96 | `to ${binaryPath}` | ||
94 | ); | 97 | ); |
95 | 98 | ||
96 | vscode.window.showInformationMessage( | 99 | vscode.window.showInformationMessage( |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 7b3bb6302..ecf53cf77 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -12,6 +12,26 @@ import { log } from './util'; | |||
12 | let ctx: Ctx | undefined; | 12 | let ctx: Ctx | undefined; |
13 | 13 | ||
14 | export async function activate(context: vscode.ExtensionContext) { | 14 | export async function activate(context: vscode.ExtensionContext) { |
15 | // Register a "dumb" onEnter command for the case where server fails to | ||
16 | // start. | ||
17 | // | ||
18 | // FIXME: refactor command registration code such that commands are | ||
19 | // **always** registered, even if the server does not start. Use API like | ||
20 | // this perhaps? | ||
21 | // | ||
22 | // ```TypeScript | ||
23 | // registerCommand( | ||
24 | // factory: (Ctx) => ((Ctx) => any), | ||
25 | // fallback: () => any = () => vscode.window.showErrorMessage( | ||
26 | // "rust-analyzer is not available" | ||
27 | // ), | ||
28 | // ) | ||
29 | const defaultOnEnter = vscode.commands.registerCommand( | ||
30 | 'rust-analyzer.onEnter', | ||
31 | () => vscode.commands.executeCommand('default:type', { text: '\n' }), | ||
32 | ); | ||
33 | context.subscriptions.push(defaultOnEnter); | ||
34 | |||
15 | const config = new Config(context); | 35 | const config = new Config(context); |
16 | 36 | ||
17 | const serverPath = await ensureServerBinary(config.serverSource); | 37 | const serverPath = await ensureServerBinary(config.serverSource); |
@@ -54,7 +74,10 @@ export async function activate(context: vscode.ExtensionContext) { | |||
54 | ctx.registerCommand('syntaxTree', commands.syntaxTree); | 74 | ctx.registerCommand('syntaxTree', commands.syntaxTree); |
55 | ctx.registerCommand('expandMacro', commands.expandMacro); | 75 | ctx.registerCommand('expandMacro', commands.expandMacro); |
56 | ctx.registerCommand('run', commands.run); | 76 | ctx.registerCommand('run', commands.run); |
77 | |||
78 | defaultOnEnter.dispose(); | ||
57 | ctx.registerCommand('onEnter', commands.onEnter); | 79 | ctx.registerCommand('onEnter', commands.onEnter); |
80 | |||
58 | ctx.registerCommand('ssr', commands.ssr); | 81 | ctx.registerCommand('ssr', commands.ssr); |
59 | ctx.registerCommand('serverVersion', commands.serverVersion); | 82 | ctx.registerCommand('serverVersion', commands.serverVersion); |
60 | 83 | ||
@@ -66,7 +89,9 @@ export async function activate(context: vscode.ExtensionContext) { | |||
66 | 89 | ||
67 | activateStatusDisplay(ctx); | 90 | activateStatusDisplay(ctx); |
68 | 91 | ||
69 | activateHighlighting(ctx); | 92 | if (!ctx.config.highlightingSemanticTokens) { |
93 | activateHighlighting(ctx); | ||
94 | } | ||
70 | activateInlayHints(ctx); | 95 | activateInlayHints(ctx); |
71 | } | 96 | } |
72 | 97 | ||
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts new file mode 100644 index 000000000..c5a010e94 --- /dev/null +++ b/editors/code/src/rust-analyzer-api.ts | |||
@@ -0,0 +1,117 @@ | |||
1 | /** | ||
2 | * This file mirrors `crates/rust-analyzer/src/req.rs` declarations. | ||
3 | */ | ||
4 | |||
5 | import * as lc from "vscode-languageclient"; | ||
6 | |||
7 | type Option<T> = null | T; | ||
8 | type Vec<T> = T[]; | ||
9 | type FxHashMap<K extends PropertyKey, V> = Record<K, V>; | ||
10 | |||
11 | function request<TParams, TResult>(method: string) { | ||
12 | return new lc.RequestType<TParams, TResult, unknown>(`rust-analyzer/${method}`); | ||
13 | } | ||
14 | function notification<TParam>(method: string) { | ||
15 | return new lc.NotificationType<TParam>(method); | ||
16 | } | ||
17 | |||
18 | |||
19 | export const analyzerStatus = request<null, string>("analyzerStatus"); | ||
20 | |||
21 | |||
22 | export const collectGarbage = request<null, null>("collectGarbage"); | ||
23 | |||
24 | |||
25 | export interface SyntaxTreeParams { | ||
26 | textDocument: lc.TextDocumentIdentifier; | ||
27 | range: Option<lc.Range>; | ||
28 | } | ||
29 | export const syntaxTree = request<SyntaxTreeParams, string>("syntaxTree"); | ||
30 | |||
31 | |||
32 | export interface ExpandMacroParams { | ||
33 | textDocument: lc.TextDocumentIdentifier; | ||
34 | position: Option<lc.Position>; | ||
35 | } | ||
36 | export interface ExpandedMacro { | ||
37 | name: string; | ||
38 | expansion: string; | ||
39 | } | ||
40 | export const expandMacro = request<ExpandMacroParams, Option<ExpandedMacro>>("expandMacro"); | ||
41 | |||
42 | |||
43 | export interface FindMatchingBraceParams { | ||
44 | textDocument: lc.TextDocumentIdentifier; | ||
45 | offsets: Vec<lc.Position>; | ||
46 | } | ||
47 | export const findMatchingBrace = request<FindMatchingBraceParams, Vec<lc.Position>>("findMatchingBrace"); | ||
48 | |||
49 | |||
50 | export interface PublishDecorationsParams { | ||
51 | uri: string; | ||
52 | decorations: Vec<Decoration>; | ||
53 | } | ||
54 | export interface Decoration { | ||
55 | range: lc.Range; | ||
56 | tag: string; | ||
57 | bindingHash: Option<string>; | ||
58 | } | ||
59 | export const decorationsRequest = request<lc.TextDocumentIdentifier, Vec<Decoration>>("decorationsRequest"); | ||
60 | |||
61 | |||
62 | export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Location>>("parentModule"); | ||
63 | |||
64 | |||
65 | export interface JoinLinesParams { | ||
66 | textDocument: lc.TextDocumentIdentifier; | ||
67 | range: lc.Range; | ||
68 | } | ||
69 | export const joinLines = request<JoinLinesParams, SourceChange>("joinLines"); | ||
70 | |||
71 | |||
72 | export const onEnter = request<lc.TextDocumentPositionParams, Option<SourceChange>>("onEnter"); | ||
73 | |||
74 | export interface RunnablesParams { | ||
75 | textDocument: lc.TextDocumentIdentifier; | ||
76 | position: Option<lc.Position>; | ||
77 | } | ||
78 | export interface Runnable { | ||
79 | range: lc.Range; | ||
80 | label: string; | ||
81 | bin: string; | ||
82 | args: Vec<string>; | ||
83 | env: FxHashMap<string, string>; | ||
84 | cwd: Option<string>; | ||
85 | } | ||
86 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); | ||
87 | |||
88 | |||
89 | export const enum InlayKind { | ||
90 | TypeHint = "TypeHint", | ||
91 | ParameterHint = "ParameterHint", | ||
92 | } | ||
93 | export interface InlayHint { | ||
94 | range: lc.Range; | ||
95 | kind: InlayKind; | ||
96 | label: string; | ||
97 | } | ||
98 | export interface InlayHintsParams { | ||
99 | textDocument: lc.TextDocumentIdentifier; | ||
100 | } | ||
101 | export const inlayHints = request<InlayHintsParams, Vec<InlayHint>>("inlayHints"); | ||
102 | |||
103 | |||
104 | export interface SsrParams { | ||
105 | arg: string; | ||
106 | } | ||
107 | export const ssr = request<SsrParams, SourceChange>("ssr"); | ||
108 | |||
109 | |||
110 | export const publishDecorations = notification<PublishDecorationsParams>("publishDecorations"); | ||
111 | |||
112 | |||
113 | export interface SourceChange { | ||
114 | label: string; | ||
115 | workspaceEdit: lc.WorkspaceEdit; | ||
116 | cursorPosition: Option<lc.TextDocumentPositionParams>; | ||
117 | } | ||
diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts index a336269ba..399a150c6 100644 --- a/editors/code/src/source_change.ts +++ b/editors/code/src/source_change.ts | |||
@@ -1,15 +1,10 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | import * as ra from './rust-analyzer-api'; | ||
3 | 4 | ||
4 | import { Ctx } from './ctx'; | 5 | import { Ctx } from './ctx'; |
5 | 6 | ||
6 | export interface SourceChange { | 7 | export async function applySourceChange(ctx: Ctx, change: ra.SourceChange) { |
7 | label: string; | ||
8 | workspaceEdit: lc.WorkspaceEdit; | ||
9 | cursorPosition?: lc.TextDocumentPositionParams; | ||
10 | } | ||
11 | |||
12 | export async function applySourceChange(ctx: Ctx, change: SourceChange) { | ||
13 | const client = ctx.client; | 8 | const client = ctx.client; |
14 | if (!client) return; | 9 | if (!client) return; |
15 | 10 | ||
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 2f18f85a3..68c2a94d0 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -20,21 +20,21 @@ export const log = { | |||
20 | } | 20 | } |
21 | }; | 21 | }; |
22 | 22 | ||
23 | export async function sendRequestWithRetry<R>( | 23 | export async function sendRequestWithRetry<TParam, TRet>( |
24 | client: lc.LanguageClient, | 24 | client: lc.LanguageClient, |
25 | method: string, | 25 | reqType: lc.RequestType<TParam, TRet, unknown>, |
26 | param: unknown, | 26 | param: TParam, |
27 | token?: vscode.CancellationToken, | 27 | token?: vscode.CancellationToken, |
28 | ): Promise<R> { | 28 | ): Promise<TRet> { |
29 | for (const delay of [2, 4, 6, 8, 10, null]) { | 29 | for (const delay of [2, 4, 6, 8, 10, null]) { |
30 | try { | 30 | try { |
31 | return await (token | 31 | return await (token |
32 | ? client.sendRequest(method, param, token) | 32 | ? client.sendRequest(reqType, param, token) |
33 | : client.sendRequest(method, param) | 33 | : client.sendRequest(reqType, param) |
34 | ); | 34 | ); |
35 | } catch (error) { | 35 | } catch (error) { |
36 | if (delay === null) { | 36 | if (delay === null) { |
37 | log.error("LSP request timed out", { method, param, error }); | 37 | log.error("LSP request timed out", { method: reqType.method, param, error }); |
38 | throw error; | 38 | throw error; |
39 | } | 39 | } |
40 | 40 | ||
@@ -43,7 +43,7 @@ export async function sendRequestWithRetry<R>( | |||
43 | } | 43 | } |
44 | 44 | ||
45 | if (error.code !== lc.ErrorCodes.ContentModified) { | 45 | if (error.code !== lc.ErrorCodes.ContentModified) { |
46 | log.error("LSP request failed", { method, param, error }); | 46 | log.error("LSP request failed", { method: reqType.method, param, error }); |
47 | throw error; | 47 | throw error; |
48 | } | 48 | } |
49 | 49 | ||