aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/code/.vscodeignore1
-rw-r--r--editors/code/package-lock.json97
-rw-r--r--editors/code/package.json128
-rw-r--r--editors/code/rust.tmGrammar.json681
-rw-r--r--editors/code/src/cargo.ts100
-rw-r--r--editors/code/src/client.ts65
-rw-r--r--editors/code/src/color_theme.ts129
-rw-r--r--editors/code/src/commands/index.ts56
-rw-r--r--editors/code/src/commands/join_lines.ts12
-rw-r--r--editors/code/src/commands/on_enter.ts5
-rw-r--r--editors/code/src/commands/runnables.ts197
-rw-r--r--editors/code/src/commands/ssr.ts2
-rw-r--r--editors/code/src/commands/syntax_tree.ts4
-rw-r--r--editors/code/src/config.ts20
-rw-r--r--editors/code/src/debug.ts124
-rw-r--r--editors/code/src/inlay_hints.ts14
-rw-r--r--editors/code/src/main.ts11
-rw-r--r--editors/code/src/rust-analyzer-api.ts6
-rw-r--r--editors/code/src/util.ts11
19 files changed, 1315 insertions, 348 deletions
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index ac411f8e2..257b744bf 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -3,5 +3,6 @@
3!package.json 3!package.json
4!package-lock.json 4!package-lock.json
5!ra_syntax_tree.tmGrammar.json 5!ra_syntax_tree.tmGrammar.json
6!rust.tmGrammar.json
6!icon.png 7!icon.png
7!README.md 8!README.md
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 55ba5351d..06178990f 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -88,15 +88,15 @@
88 "dev": true 88 "dev": true
89 }, 89 },
90 "@types/node": { 90 "@types/node": {
91 "version": "12.12.37", 91 "version": "12.12.39",
92 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.37.tgz", 92 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.39.tgz",
93 "integrity": "sha512-4mXKoDptrXAwZErQHrLzpe0FN/0Wmf5JRniSVIdwUrtDf9wnmEV1teCNLBo/TwuXhkK/bVegoEn/wmb+x0AuPg==", 93 "integrity": "sha512-pADGfwnDkr6zagDwEiCVE4yQrv7XDkoeVa4OfA9Ju/zRTk6YNDLGtQbkdL4/56mCQQCs4AhNrBIag6jrp7ZuOg==",
94 "dev": true 94 "dev": true
95 }, 95 },
96 "@types/node-fetch": { 96 "@types/node-fetch": {
97 "version": "2.5.6", 97 "version": "2.5.7",
98 "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.6.tgz", 98 "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
99 "integrity": "sha512-2w0NTwMWF1d3NJMK0Uiq2UNN8htVCyOWOD0jIPjPgC5Ph/YP4dVhs9YxxcMcuLuwAslz0dVEcZQUaqkLs3IzOQ==", 99 "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
100 "dev": true, 100 "dev": true,
101 "requires": { 101 "requires": {
102 "@types/node": "*", 102 "@types/node": "*",
@@ -113,31 +113,31 @@
113 } 113 }
114 }, 114 },
115 "@types/vscode": { 115 "@types/vscode": {
116 "version": "1.44.0", 116 "version": "1.45.0",
117 "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.44.0.tgz", 117 "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.45.0.tgz",
118 "integrity": "sha512-WJZtZlinE3meRdH+I7wTsIhpz/GLhqEQwmPGeh4s1irWLwMzCeTV8WZ+pgPTwrDXoafVUWwo1LiZ9HJVHFlJSQ==", 118 "integrity": "sha512-b0Gyir7sPBCqiKLygAhn/AYVfzWD+SMPkWltBrIuPEyTOxSU1wVApWY/FcxYO2EWTRacoubTl4+gvZf86RkecA==",
119 "dev": true 119 "dev": true
120 }, 120 },
121 "@typescript-eslint/eslint-plugin": { 121 "@typescript-eslint/eslint-plugin": {
122 "version": "2.29.0", 122 "version": "2.33.0",
123 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.29.0.tgz", 123 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.33.0.tgz",
124 "integrity": "sha512-X/YAY7azKirENm4QRpT7OVmzok02cSkqeIcLmdz6gXUQG4Hk0Fi9oBAynSAyNXeGdMRuZvjBa0c1Lu0dn/u6VA==", 124 "integrity": "sha512-QV6P32Btu1sCI/kTqjTNI/8OpCYyvlGjW5vD8MpTIg+HGE5S88HtT1G+880M4bXlvXj/NjsJJG0aGcVh0DdbeQ==",
125 "dev": true, 125 "dev": true,
126 "requires": { 126 "requires": {
127 "@typescript-eslint/experimental-utils": "2.29.0", 127 "@typescript-eslint/experimental-utils": "2.33.0",
128 "functional-red-black-tree": "^1.0.1", 128 "functional-red-black-tree": "^1.0.1",
129 "regexpp": "^3.0.0", 129 "regexpp": "^3.0.0",
130 "tsutils": "^3.17.1" 130 "tsutils": "^3.17.1"
131 } 131 }
132 }, 132 },
133 "@typescript-eslint/experimental-utils": { 133 "@typescript-eslint/experimental-utils": {
134 "version": "2.29.0", 134 "version": "2.33.0",
135 "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz", 135 "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.33.0.tgz",
136 "integrity": "sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw==", 136 "integrity": "sha512-qzPM2AuxtMrRq78LwyZa8Qn6gcY8obkIrBs1ehqmQADwkYzTE1Pb4y2W+U3rE/iFkSWcWHG2LS6MJfj6SmHApg==",
137 "dev": true, 137 "dev": true,
138 "requires": { 138 "requires": {
139 "@types/json-schema": "^7.0.3", 139 "@types/json-schema": "^7.0.3",
140 "@typescript-eslint/typescript-estree": "2.29.0", 140 "@typescript-eslint/typescript-estree": "2.33.0",
141 "eslint-scope": "^5.0.0", 141 "eslint-scope": "^5.0.0",
142 "eslint-utils": "^2.0.0" 142 "eslint-utils": "^2.0.0"
143 }, 143 },
@@ -154,21 +154,21 @@
154 } 154 }
155 }, 155 },
156 "@typescript-eslint/parser": { 156 "@typescript-eslint/parser": {
157 "version": "2.29.0", 157 "version": "2.33.0",
158 "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.29.0.tgz", 158 "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.33.0.tgz",
159 "integrity": "sha512-H78M+jcu5Tf6m/5N8iiFblUUv+HJDguMSdFfzwa6vSg9lKR8Mk9BsgeSjO8l2EshKnJKcbv0e8IDDOvSNjl0EA==", 159 "integrity": "sha512-AUtmwUUhJoH6yrtxZMHbRUEMsC2G6z5NSxg9KsROOGqNXasM71I8P2NihtumlWTUCRld70vqIZ6Pm4E5PAziEA==",
160 "dev": true, 160 "dev": true,
161 "requires": { 161 "requires": {
162 "@types/eslint-visitor-keys": "^1.0.0", 162 "@types/eslint-visitor-keys": "^1.0.0",
163 "@typescript-eslint/experimental-utils": "2.29.0", 163 "@typescript-eslint/experimental-utils": "2.33.0",
164 "@typescript-eslint/typescript-estree": "2.29.0", 164 "@typescript-eslint/typescript-estree": "2.33.0",
165 "eslint-visitor-keys": "^1.1.0" 165 "eslint-visitor-keys": "^1.1.0"
166 } 166 }
167 }, 167 },
168 "@typescript-eslint/typescript-estree": { 168 "@typescript-eslint/typescript-estree": {
169 "version": "2.29.0", 169 "version": "2.33.0",
170 "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz", 170 "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.33.0.tgz",
171 "integrity": "sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA==", 171 "integrity": "sha512-d8rY6/yUxb0+mEwTShCQF2zYQdLlqihukNfG9IUlLYz5y1CH6G/9XYbrxQLq3Z14RNvkCC6oe+OcFlyUpwUbkg==",
172 "dev": true, 172 "dev": true,
173 "requires": { 173 "requires": {
174 "debug": "^4.1.1", 174 "debug": "^4.1.1",
@@ -176,8 +176,16 @@
176 "glob": "^7.1.6", 176 "glob": "^7.1.6",
177 "is-glob": "^4.0.1", 177 "is-glob": "^4.0.1",
178 "lodash": "^4.17.15", 178 "lodash": "^4.17.15",
179 "semver": "^6.3.0", 179 "semver": "^7.3.2",
180 "tsutils": "^3.17.1" 180 "tsutils": "^3.17.1"
181 },
182 "dependencies": {
183 "semver": {
184 "version": "7.3.2",
185 "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
186 "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
187 "dev": true
188 }
181 } 189 }
182 }, 190 },
183 "acorn": { 191 "acorn": {
@@ -1066,11 +1074,6 @@
1066 "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1074 "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
1067 "dev": true 1075 "dev": true
1068 }, 1076 },
1069 "jsonc-parser": {
1070 "version": "2.2.1",
1071 "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz",
1072 "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w=="
1073 },
1074 "leven": { 1077 "leven": {
1075 "version": "3.1.0", 1078 "version": "3.1.0",
1076 "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", 1079 "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -1165,18 +1168,18 @@
1165 "dev": true 1168 "dev": true
1166 }, 1169 },
1167 "mime-db": { 1170 "mime-db": {
1168 "version": "1.43.0", 1171 "version": "1.44.0",
1169 "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", 1172 "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
1170 "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", 1173 "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
1171 "dev": true 1174 "dev": true
1172 }, 1175 },
1173 "mime-types": { 1176 "mime-types": {
1174 "version": "2.1.26", 1177 "version": "2.1.27",
1175 "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", 1178 "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
1176 "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", 1179 "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
1177 "dev": true, 1180 "dev": true,
1178 "requires": { 1181 "requires": {
1179 "mime-db": "1.43.0" 1182 "mime-db": "1.44.0"
1180 } 1183 }
1181 }, 1184 },
1182 "mimic-fn": { 1185 "mimic-fn": {
@@ -1457,9 +1460,9 @@
1457 } 1460 }
1458 }, 1461 },
1459 "rollup": { 1462 "rollup": {
1460 "version": "2.7.1", 1463 "version": "2.10.0",
1461 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.7.1.tgz", 1464 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.0.tgz",
1462 "integrity": "sha512-c1FCjY8HK1nAq0bTZHaR72ZknpP7p0EjxbcVc6BcmtOosurK//P5jtwxX+f/4fgtbrjczqf0uvR+EdtxpriE8g==", 1465 "integrity": "sha512-7BmpEfUN9P6esJzWIn3DmR//90mW6YwYB1t3y48LpF8ITpYtL8s1kEirMKqUu44dVH/6a/rs0EuwYVL3FuRDoA==",
1463 "dev": true, 1466 "dev": true,
1464 "requires": { 1467 "requires": {
1465 "fsevents": "~2.1.2" 1468 "fsevents": "~2.1.2"
@@ -1689,9 +1692,9 @@
1689 } 1692 }
1690 }, 1693 },
1691 "tslib": { 1694 "tslib": {
1692 "version": "1.11.1", 1695 "version": "1.12.0",
1693 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", 1696 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.12.0.tgz",
1694 "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", 1697 "integrity": "sha512-5rxCQkP0kytf4H1T4xz1imjxaUUPMvc5aWp0rJ/VMIN7ClRiH1FwFvBt8wOeMasp/epeUnmSW6CixSIePtiLqA==",
1695 "dev": true 1698 "dev": true
1696 }, 1699 },
1697 "tsutils": { 1700 "tsutils": {
@@ -1735,9 +1738,9 @@
1735 } 1738 }
1736 }, 1739 },
1737 "typescript": { 1740 "typescript": {
1738 "version": "3.8.3", 1741 "version": "3.9.2",
1739 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", 1742 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz",
1740 "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", 1743 "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==",
1741 "dev": true 1744 "dev": true
1742 }, 1745 },
1743 "typescript-formatter": { 1746 "typescript-formatter": {
diff --git a/editors/code/package.json b/editors/code/package.json
index d30673791..78f647baa 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -33,22 +33,21 @@
33 "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix" 33 "fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix"
34 }, 34 },
35 "dependencies": { 35 "dependencies": {
36 "jsonc-parser": "^2.2.1",
37 "node-fetch": "^2.6.0", 36 "node-fetch": "^2.6.0",
38 "vscode-languageclient": "7.0.0-next.1" 37 "vscode-languageclient": "7.0.0-next.1"
39 }, 38 },
40 "devDependencies": { 39 "devDependencies": {
41 "@rollup/plugin-commonjs": "^11.1.0", 40 "@rollup/plugin-commonjs": "^11.1.0",
42 "@rollup/plugin-node-resolve": "^7.1.3", 41 "@rollup/plugin-node-resolve": "^7.1.3",
43 "@types/node": "^12.12.37", 42 "@types/node": "^12.12.39",
44 "@types/node-fetch": "^2.5.6", 43 "@types/node-fetch": "^2.5.7",
45 "@types/vscode": "^1.44.0", 44 "@types/vscode": "^1.44.0",
46 "@typescript-eslint/eslint-plugin": "^2.29.0", 45 "@typescript-eslint/eslint-plugin": "^2.33.0",
47 "@typescript-eslint/parser": "^2.29.0", 46 "@typescript-eslint/parser": "^2.33.0",
48 "eslint": "^6.8.0", 47 "eslint": "^6.8.0",
49 "rollup": "^2.7.1", 48 "rollup": "^2.10.0",
50 "tslib": "^1.11.1", 49 "tslib": "^1.12.0",
51 "typescript": "^3.8.3", 50 "typescript": "^3.9.2",
52 "typescript-formatter": "^7.2.2", 51 "typescript-formatter": "^7.2.2",
53 "vsce": "^1.75.0" 52 "vsce": "^1.75.0"
54 }, 53 },
@@ -122,6 +121,16 @@
122 "category": "Rust Analyzer" 121 "category": "Rust Analyzer"
123 }, 122 },
124 { 123 {
124 "command": "rust-analyzer.debug",
125 "title": "Debug",
126 "category": "Rust Analyzer"
127 },
128 {
129 "command": "rust-analyzer.newDebugConfig",
130 "title": "Generate launch configuration",
131 "category": "Rust Analyzer"
132 },
133 {
125 "command": "rust-analyzer.analyzerStatus", 134 "command": "rust-analyzer.analyzerStatus",
126 "title": "Status", 135 "title": "Status",
127 "category": "Rust Analyzer" 136 "category": "Rust Analyzer"
@@ -205,11 +214,6 @@
205 "default": [], 214 "default": [],
206 "description": "Paths to exclude from analysis." 215 "description": "Paths to exclude from analysis."
207 }, 216 },
208 "rust-analyzer.notifications.workspaceLoaded": {
209 "type": "boolean",
210 "default": true,
211 "markdownDescription": "Whether to show `workspace loaded` message."
212 },
213 "rust-analyzer.notifications.cargoTomlNotFound": { 217 "rust-analyzer.notifications.cargoTomlNotFound": {
214 "type": "boolean", 218 "type": "boolean",
215 "default": true, 219 "default": true,
@@ -238,6 +242,14 @@
238 "default": false, 242 "default": false,
239 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs" 243 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
240 }, 244 },
245 "rust-analyzer.cargo.target": {
246 "type": [
247 "null",
248 "string"
249 ],
250 "default": null,
251 "description": "Specify the compilation target"
252 },
241 "rust-analyzer.rustfmt.extraArgs": { 253 "rust-analyzer.rustfmt.extraArgs": {
242 "type": "array", 254 "type": "array",
243 "items": { 255 "items": {
@@ -286,27 +298,37 @@
286 "minItems": 1 298 "minItems": 1
287 }, 299 },
288 "default": null, 300 "default": null,
289 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message=format=json` or similar option." 301 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option."
290 }, 302 },
291 "rust-analyzer.checkOnSave.allTargets": { 303 "rust-analyzer.checkOnSave.allTargets": {
292 "type": "boolean", 304 "type": "boolean",
293 "default": true, 305 "default": true,
294 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)" 306 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
295 }, 307 },
308 "rust-analyzer.checkOnSave.allFeatures": {
309 "type": "boolean",
310 "default": true,
311 "markdownDescription": "Check with all features (will be passed as `--all-features`)"
312 },
313 "rust-analyzer.inlayHints.enable": {
314 "type": "boolean",
315 "default": true,
316 "description": "Whether to show inlay hints"
317 },
296 "rust-analyzer.inlayHints.typeHints": { 318 "rust-analyzer.inlayHints.typeHints": {
297 "type": "boolean", 319 "type": "boolean",
298 "default": true, 320 "default": true,
299 "description": "Whether to show inlay type hints" 321 "description": "Whether to show inlay type hints for variables."
300 }, 322 },
301 "rust-analyzer.inlayHints.chainingHints": { 323 "rust-analyzer.inlayHints.chainingHints": {
302 "type": "boolean", 324 "type": "boolean",
303 "default": true, 325 "default": true,
304 "description": "Whether to show inlay type hints for method chains" 326 "description": "Whether to show inlay type hints for method chains."
305 }, 327 },
306 "rust-analyzer.inlayHints.parameterHints": { 328 "rust-analyzer.inlayHints.parameterHints": {
307 "type": "boolean", 329 "type": "boolean",
308 "default": true, 330 "default": true,
309 "description": "Whether to show function parameter name inlay hints at the call site" 331 "description": "Whether to show function parameter name inlay hints at the call site."
310 }, 332 },
311 "rust-analyzer.inlayHints.maxLength": { 333 "rust-analyzer.inlayHints.maxLength": {
312 "type": [ 334 "type": [
@@ -398,7 +420,7 @@
398 "ms-vscode.cpptools" 420 "ms-vscode.cpptools"
399 ], 421 ],
400 "default": "auto", 422 "default": "auto",
401 "description": "Preffered debug engine.", 423 "description": "Preferred debug engine.",
402 "markdownEnumDescriptions": [ 424 "markdownEnumDescriptions": [
403 "First try to use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), if it's not installed try to use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools).", 425 "First try to use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), if it's not installed try to use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools).",
404 "Use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)", 426 "Use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)",
@@ -411,6 +433,36 @@
411 "default": { 433 "default": {
412 "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust" 434 "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
413 } 435 }
436 },
437 "rust-analyzer.debug.openDebugPane": {
438 "description": "Whether to open up the Debug Pane on debugging start.",
439 "type": "boolean",
440 "default": false
441 },
442 "rust-analyzer.debug.engineSettings": {
443 "type": "object",
444 "default": {},
445 "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }"
446 },
447 "rust-analyzer.lens.enable": {
448 "description": "Whether to show CodeLens in Rust files.",
449 "type": "boolean",
450 "default": true
451 },
452 "rust-analyzer.lens.run": {
453 "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
454 "type": "boolean",
455 "default": true
456 },
457 "rust-analyzer.lens.debug": {
458 "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
459 "type": "boolean",
460 "default": true
461 },
462 "rust-analyzer.lens.implementations": {
463 "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
464 "type": "boolean",
465 "default": true
414 } 466 }
415 } 467 }
416 }, 468 },
@@ -457,6 +509,11 @@
457 ], 509 ],
458 "grammars": [ 510 "grammars": [
459 { 511 {
512 "language": "rust",
513 "scopeName": "source.rust",
514 "path": "rust.tmGrammar.json"
515 },
516 {
460 "language": "ra_syntax_tree", 517 "language": "ra_syntax_tree",
461 "scopeName": "source.ra_syntax_tree", 518 "scopeName": "source.ra_syntax_tree",
462 "path": "ra_syntax_tree.tmGrammar.json" 519 "path": "ra_syntax_tree.tmGrammar.json"
@@ -539,6 +596,10 @@
539 { 596 {
540 "id": "unresolvedReference", 597 "id": "unresolvedReference",
541 "description": "Style for names which can not be resolved due to compilation errors" 598 "description": "Style for names which can not be resolved due to compilation errors"
599 },
600 {
601 "id": "formatSpecifier",
602 "description": "Style for {} placeholders in format strings"
542 } 603 }
543 ], 604 ],
544 "semanticTokenModifiers": [ 605 "semanticTokenModifiers": [
@@ -563,26 +624,41 @@
563 { 624 {
564 "language": "rust", 625 "language": "rust",
565 "scopes": { 626 "scopes": {
627 "macro": [
628 "entity.name.function.macro.rust"
629 ],
566 "attribute": [ 630 "attribute": [
567 "meta.attribute" 631 "meta.attribute.rust"
568 ], 632 ],
569 "builtinType": [ 633 "builtinType": [
570 "support.type.primitive" 634 "support.type.primitive.rust"
571 ], 635 ],
572 "lifetime": [ 636 "lifetime": [
573 "entity.name.lifetime.rust" 637 "storage.modifier.lifetime.rust"
574 ], 638 ],
575 "typeAlias": [ 639 "typeAlias": [
576 "entity.name.typeAlias" 640 "entity.name.type.typeAlias.rust"
577 ], 641 ],
578 "union": [ 642 "union": [
579 "entity.name.union" 643 "entity.name.type.union.rust"
644 ],
645 "struct": [
646 "entity.name.type.struct.rust"
647 ],
648 "keyword": [
649 "keyword.other.rust"
580 ], 650 ],
581 "keyword.unsafe": [ 651 "keyword.controlFlow": [
582 "keyword.other.unsafe" 652 "keyword.control.rust"
583 ], 653 ],
584 "variable.constant": [ 654 "variable.constant": [
585 "entity.name.constant" 655 "variable.other.constant.rust"
656 ],
657 "formatSpecifier": [
658 "punctuation.section.embedded.rust"
659 ],
660 "*.mutable": [
661 "markup.underline"
586 ] 662 ]
587 } 663 }
588 } 664 }
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
new file mode 100644
index 000000000..aa0811326
--- /dev/null
+++ b/editors/code/rust.tmGrammar.json
@@ -0,0 +1,681 @@
1{
2 "name": "Rust",
3 "scopeName": "source.rust",
4 "patterns": [
5 {
6 "comment": "Implementation",
7 "begin": "\\b(impl)\\b",
8 "end": "\\{",
9 "beginCaptures": {
10 "1": {
11 "name": "storage.type.rust"
12 }
13 },
14 "patterns": [
15 {
16 "include": "#block_comment"
17 },
18 {
19 "include": "#line_comment"
20 },
21 {
22 "include": "#sigils"
23 },
24 {
25 "include": "#mut"
26 },
27 {
28 "include": "#dyn"
29 },
30 {
31 "include": "#ref_lifetime"
32 },
33 {
34 "include": "#core_types"
35 },
36 {
37 "include": "#core_marker"
38 },
39 {
40 "include": "#core_traits"
41 },
42 {
43 "include": "#std_types"
44 },
45 {
46 "include": "#std_traits"
47 },
48 {
49 "include": "#type_params"
50 },
51 {
52 "include": "#where"
53 },
54 {
55 "name": "storage.type.rust",
56 "match": "\\bfor\\b"
57 },
58 {
59 "include": "#type"
60 }
61 ]
62 },
63 {
64 "include": "#block_doc_comment"
65 },
66 {
67 "include": "#block_comment"
68 },
69 {
70 "include": "#line_doc_comment"
71 },
72 {
73 "include": "#line_comment"
74 },
75 {
76 "comment": "Attribute",
77 "name": "meta.attribute.rust",
78 "begin": "#\\!?\\[",
79 "end": "\\]",
80 "patterns": [
81 {
82 "include": "#string_literal"
83 },
84 {
85 "include": "#block_doc_comment"
86 },
87 {
88 "include": "#block_comment"
89 },
90 {
91 "include": "#line_doc_comment"
92 },
93 {
94 "include": "#line_comment"
95 }
96 ]
97 },
98 {
99 "comment": "Single-quote string literal (character)",
100 "name": "string.quoted.single.rust",
101 "match": "b?'([^'\\\\]|\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.))'"
102 },
103 {
104 "include": "#string_literal"
105 },
106 {
107 "include": "#raw_string_literal"
108 },
109 {
110 "comment": "Floating point literal (fraction)",
111 "name": "constant.numeric.float.rust",
112 "match": "\\b[0-9][0-9_]*\\.[0-9][0-9_]*([eE][+-]?[0-9_]+)?(f32|f64)?\\b"
113 },
114 {
115 "comment": "Floating point literal (exponent)",
116 "name": "constant.numeric.float.rust",
117 "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?[eE][+-]?[0-9_]+(f32|f64)?\\b"
118 },
119 {
120 "comment": "Floating point literal (typed)",
121 "name": "constant.numeric.float.rust",
122 "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?([eE][+-]?[0-9_]+)?(f32|f64)\\b"
123 },
124 {
125 "comment": "Integer literal (decimal)",
126 "name": "constant.numeric.integer.decimal.rust",
127 "match": "\\b[0-9][0-9_]*([ui](8|16|32|64|128|s|size))?\\b"
128 },
129 {
130 "comment": "Integer literal (hexadecimal)",
131 "name": "constant.numeric.integer.hexadecimal.rust",
132 "match": "\\b0x[a-fA-F0-9_]+([ui](8|16|32|64|128|s|size))?\\b"
133 },
134 {
135 "comment": "Integer literal (octal)",
136 "name": "constant.numeric.integer.octal.rust",
137 "match": "\\b0o[0-7_]+([ui](8|16|32|64|128|s|size))?\\b"
138 },
139 {
140 "comment": "Integer literal (binary)",
141 "name": "constant.numeric.integer.binary.rust",
142 "match": "\\b0b[01_]+([ui](8|16|32|64|128|s|size))?\\b"
143 },
144 {
145 "comment": "Static storage modifier",
146 "name": "storage.modifier.static.rust",
147 "match": "\\bstatic\\b"
148 },
149 {
150 "comment": "Boolean constant",
151 "name": "constant.language.boolean.rust",
152 "match": "\\b(true|false)\\b"
153 },
154 {
155 "comment": "Control keyword",
156 "name": "keyword.control.rust",
157 "match": "\\b(async|await|break|continue|else|if|in|for|loop|match|return|try|while)\\b"
158 },
159 {
160 "comment": "Keyword",
161 "name": "keyword.other.rust",
162 "match": "\\b(crate|extern|mod|let|ref|use|super|move|as)\\b"
163 },
164 {
165 "comment": "Reserved keyword",
166 "name": "invalid.deprecated.rust",
167 "match": "\\b(abstract|alignof|become|do|final|macro|offsetof|override|priv|proc|pure|sizeof|typeof|virtual|yield)\\b"
168 },
169 {
170 "include": "#unsafe"
171 },
172 {
173 "include": "#sigils"
174 },
175 {
176 "include": "#self"
177 },
178 {
179 "include": "#mut"
180 },
181 {
182 "include": "#dyn"
183 },
184 {
185 "include": "#impl"
186 },
187 {
188 "include": "#box"
189 },
190 {
191 "include": "#lifetime"
192 },
193 {
194 "include": "#ref_lifetime"
195 },
196 {
197 "include": "#const"
198 },
199 {
200 "include": "#pub"
201 },
202 {
203 "comment": "Miscellaneous operator",
204 "name": "keyword.operator.misc.rust",
205 "match": "(=>|::)"
206 },
207 {
208 "comment": "Comparison operator",
209 "name": "keyword.operator.comparison.rust",
210 "match": "(&&|\\|\\||==|!=)"
211 },
212 {
213 "comment": "Assignment operator",
214 "name": "keyword.operator.assignment.rust",
215 "match": "(\\+=|-=|/=|\\*=|%=|\\^=|&=|\\|=|<<=|>>=|=)"
216 },
217 {
218 "comment": "Arithmetic operator",
219 "name": "keyword.operator.arithmetic.rust",
220 "match": "(!|\\+|-|/|\\*|%|\\^|&|\\||<<|>>)"
221 },
222 {
223 "comment": "Comparison operator (second group because of regex precedence)",
224 "name": "keyword.operator.comparison.rust",
225 "match": "(<=|>=|<|>)"
226 },
227 {
228 "include": "#core_types"
229 },
230 {
231 "include": "#core_vars"
232 },
233 {
234 "include": "#core_marker"
235 },
236 {
237 "include": "#core_traits"
238 },
239 {
240 "include": "#std_types"
241 },
242 {
243 "include": "#std_traits"
244 },
245 {
246 "comment": "Built-in macro",
247 "name": "support.function.builtin.rust",
248 "match": "\\b(macro_rules|compile_error|format_args|env|option_env|concat_idents|concat|line|column|file|stringify|include|include_str|include_bytes|module_path|cfg)!"
249 },
250 {
251 "comment": "Core macro",
252 "name": "support.function.core.rust",
253 "match": "\\b(panic|assert|assert_eq|assert_ne|debug_assert|debug_assert_eq|debug_assert_ne|try|write|writeln|unreachable|unimplemented)!"
254 },
255 {
256 "comment": "Standard library macro",
257 "name": "support.function.std.rust",
258 "match": "\\b(format|print|println|eprint|eprintln|select|vec)!"
259 },
260 {
261 "comment": "Logging macro",
262 "name": "support.function.log.rust",
263 "match": "\\b(log|error|warn|info|debug|trace|log_enabled)!"
264 },
265 {
266 "comment": "Invokation of a macro",
267 "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]",
268 "captures": {
269 "1": {
270 "name": "entity.name.function.macro.rust"
271 }
272 }
273 },
274 {
275 "comment": "Function call",
276 "match": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*\\(",
277 "captures": {
278 "1": {
279 "name": "entity.name.function.rust"
280 }
281 }
282 },
283 {
284 "comment": "Function call with type parameters",
285 "begin": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*(::)(?=\\s*<.*>\\s*\\()",
286 "end": "\\(",
287 "captures": {
288 "1": {
289 "name": "entity.name.function.rust"
290 },
291 "2": {
292 "name": "keyword.operator.misc.rust"
293 }
294 },
295 "patterns": [
296 {
297 "include": "#type_params"
298 }
299 ]
300 },
301 {
302 "comment": "Function definition",
303 "begin": "\\b(fn)\\s+([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)",
304 "end": "[\\{;]",
305 "beginCaptures": {
306 "1": {
307 "name": "keyword.other.fn.rust"
308 },
309 "2": {
310 "name": "entity.name.function.rust"
311 }
312 },
313 "patterns": [
314 {
315 "include": "#block_comment"
316 },
317 {
318 "include": "#line_comment"
319 },
320 {
321 "include": "#sigils"
322 },
323 {
324 "include": "#self"
325 },
326 {
327 "include": "#mut"
328 },
329 {
330 "include": "#dyn"
331 },
332 {
333 "include": "#impl"
334 },
335 {
336 "include": "#ref_lifetime"
337 },
338 {
339 "include": "#core_types"
340 },
341 {
342 "include": "#core_marker"
343 },
344 {
345 "include": "#core_traits"
346 },
347 {
348 "include": "#std_types"
349 },
350 {
351 "include": "#std_traits"
352 },
353 {
354 "include": "#type_params"
355 },
356 {
357 "include": "#const"
358 },
359 {
360 "include": "#where"
361 },
362 {
363 "include": "#unsafe"
364 },
365 {
366 "comment": "Function arguments",
367 "match": "\bfn\b",
368 "name": "keyword.other.fn.rust"
369 }
370 ]
371 },
372 {
373 "comment": "Type declaration",
374 "begin": "\\b(enum|struct|trait|union)\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
375 "end": "[\\{\\(;]",
376 "beginCaptures": {
377 "1": {
378 "name": "storage.type.rust"
379 },
380 "2": {
381 "name": "entity.name.type.rust"
382 }
383 },
384 "patterns": [
385 {
386 "include": "#block_comment"
387 },
388 {
389 "include": "#line_comment"
390 },
391 {
392 "include": "#core_traits"
393 },
394 {
395 "include": "#std_traits"
396 },
397 {
398 "include": "#type_params"
399 },
400 {
401 "include": "#core_types"
402 },
403 {
404 "include": "#pub"
405 },
406 {
407 "include": "#where"
408 }
409 ]
410 },
411 {
412 "comment": "Type alias",
413 "begin": "\\b(type)\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
414 "end": ";",
415 "beginCaptures": {
416 "1": {
417 "name": "storage.type.rust"
418 },
419 "2": {
420 "name": "entity.name.type.rust"
421 }
422 },
423 "patterns": [
424 {
425 "include": "#block_comment"
426 },
427 {
428 "include": "#line_comment"
429 },
430 {
431 "include": "#sigils"
432 },
433 {
434 "include": "#mut"
435 },
436 {
437 "include": "#dyn"
438 },
439 {
440 "include": "#impl"
441 },
442 {
443 "include": "#lifetime"
444 },
445 {
446 "include": "#ref_lifetime"
447 },
448 {
449 "include": "#core_types"
450 },
451 {
452 "include": "#core_marker"
453 },
454 {
455 "include": "#core_traits"
456 },
457 {
458 "include": "#std_types"
459 },
460 {
461 "include": "#std_traits"
462 },
463 {
464 "include": "#type_params"
465 }
466 ]
467 }
468 ],
469 "repository": {
470 "block_doc_comment": {
471 "comment": "Block documentation comment",
472 "name": "comment.block.documentation.rust",
473 "begin": "/\\*[\\*!](?![\\*/])",
474 "end": "\\*/",
475 "patterns": [
476 {
477 "include": "#block_doc_comment"
478 },
479 {
480 "include": "#block_comment"
481 }
482 ]
483 },
484 "block_comment": {
485 "comment": "Block comment",
486 "name": "comment.block.rust",
487 "begin": "/\\*",
488 "end": "\\*/",
489 "patterns": [
490 {
491 "include": "#block_doc_comment"
492 },
493 {
494 "include": "#block_comment"
495 }
496 ]
497 },
498 "line_doc_comment": {
499 "comment": "Single-line documentation comment",
500 "name": "comment.line.documentation.rust",
501 "begin": "//[!/](?=[^/])",
502 "end": "$"
503 },
504 "line_comment": {
505 "comment": "Single-line comment",
506 "name": "comment.line.double-slash.rust",
507 "begin": "//",
508 "end": "$"
509 },
510 "escaped_character": {
511 "name": "constant.character.escape.rust",
512 "match": "\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)"
513 },
514 "string_literal": {
515 "comment": "Double-quote string literal",
516 "name": "string.quoted.double.rust",
517 "begin": "b?\"",
518 "end": "\"",
519 "patterns": [
520 {
521 "include": "#escaped_character"
522 }
523 ]
524 },
525 "raw_string_literal": {
526 "comment": "Raw double-quote string literal",
527 "name": "string.quoted.double.raw.rust",
528 "begin": "b?r(#*)\"",
529 "end": "\"\\1"
530 },
531 "sigils": {
532 "comment": "Sigil",
533 "name": "keyword.operator.sigil.rust",
534 "match": "[&*](?=[a-zA-Z0-9_\\(\\[\\|\\\"]+)"
535 },
536 "self": {
537 "comment": "Self variable",
538 "name": "variable.language.rust",
539 "match": "\\bself\\b"
540 },
541 "mut": {
542 "comment": "Mutable storage modifier",
543 "name": "storage.modifier.mut.rust",
544 "match": "\\bmut\\b"
545 },
546 "dyn": {
547 "comment": "Dynamic modifier",
548 "name": "storage.modifier.dyn.rust",
549 "match": "\\bdyn\\b"
550 },
551 "impl": {
552 "comment": "Existential type modifier",
553 "name": "storage.modifier.impl.rust",
554 "match": "\\bimpl\\b"
555 },
556 "box": {
557 "comment": "Box storage modifier",
558 "name": "storage.modifier.box.rust",
559 "match": "\\bbox\\b"
560 },
561 "const": {
562 "comment": "Const storage modifier",
563 "name": "storage.modifier.const.rust",
564 "match": "\\bconst\\b"
565 },
566 "pub": {
567 "comment": "Visibility modifier",
568 "name": "storage.modifier.visibility.rust",
569 "match": "\\bpub\\b"
570 },
571 "unsafe": {
572 "comment": "Unsafe code keyword",
573 "name": "keyword.other.unsafe.rust",
574 "match": "\\bunsafe\\b"
575 },
576 "where": {
577 "comment": "Generic where clause",
578 "name": "keyword.other.where.rust",
579 "match": "\\bwhere\\b"
580 },
581 "lifetime": {
582 "comment": "Named lifetime",
583 "name": "storage.modifier.lifetime.rust",
584 "match": "'([a-zA-Z_][a-zA-Z0-9_]*)\\b"
585 },
586 "ref_lifetime": {
587 "comment": "Reference with named lifetime",
588 "match": "(&)('[a-zA-Z_][a-zA-Z0-9_]*)\\b",
589 "captures": {
590 "1": {
591 "name": "keyword.operator.sigil.rust"
592 },
593 "2": {
594 "name": "storage.modifier.lifetime.rust"
595 }
596 }
597 },
598 "core_types": {
599 "comment": "Built-in/core type",
600 "name": "support.type.primitive.rust",
601 "match": "\\b(bool|char|usize|isize|u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f32|f64|str|Self)\\b"
602 },
603 "core_vars": {
604 "comment": "Core type variant",
605 "name": "support.constant.core.rust",
606 "match": "\\b(Some|None|Ok|Err)\\b"
607 },
608 "core_marker": {
609 "comment": "Core trait (marker)",
610 "name": "entity.name.type.marker.rust",
611 "match": "\\b(Copy|Send|Sized|Sync)\\b"
612 },
613 "core_traits": {
614 "comment": "Core trait",
615 "name": "entity.name.type.core.rust",
616 "match": "\\b(Drop|Fn|FnMut|FnOnce|Clone|PartialEq|PartialOrd|Eq|Ord|AsRef|AsMut|Into|From|Default|Iterator|Extend|IntoIterator|DoubleEndedIterator|ExactSizeIterator)\\b"
617 },
618 "std_types": {
619 "comment": "Standard library type",
620 "name": "entity.name.type.class.std.rust",
621 "match": "\\b(Box|String|Vec|Path|PathBuf|Option|Result)\\b"
622 },
623 "std_traits": {
624 "comment": "Standard library trait",
625 "name": "entity.name.type.std.rust",
626 "match": "\\b(ToOwned|ToString)\\b"
627 },
628 "type": {
629 "comment": "A type",
630 "name": "entity.name.type.rust",
631 "match": "\\b([A-Za-z][_A-Za-z0-9]*|_[_A-Za-z0-9]+)\\b"
632 },
633 "type_params": {
634 "comment": "Type parameters",
635 "name": "meta.type_params.rust",
636 "begin": "<(?![=<])",
637 "end": "(?<![-])>",
638 "patterns": [
639 {
640 "include": "#block_comment"
641 },
642 {
643 "include": "#line_comment"
644 },
645 {
646 "include": "#sigils"
647 },
648 {
649 "include": "#mut"
650 },
651 {
652 "include": "#dyn"
653 },
654 {
655 "include": "#impl"
656 },
657 {
658 "include": "#lifetime"
659 },
660 {
661 "include": "#core_types"
662 },
663 {
664 "include": "#core_marker"
665 },
666 {
667 "include": "#core_traits"
668 },
669 {
670 "include": "#std_types"
671 },
672 {
673 "include": "#std_traits"
674 },
675 {
676 "include": "#type_params"
677 }
678 ]
679 }
680 }
681} \ No newline at end of file
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
index a328ba9bd..6a41873d0 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/cargo.ts
@@ -1,6 +1,9 @@
1import * as cp from 'child_process'; 1import * as cp from 'child_process';
2import * as os from 'os';
3import * as path from 'path';
2import * as readline from 'readline'; 4import * as readline from 'readline';
3import { OutputChannel } from 'vscode'; 5import { OutputChannel } from 'vscode';
6import { isValidExecutable } from './util';
4 7
5interface CompilationArtifact { 8interface CompilationArtifact {
6 fileName: string; 9 fileName: string;
@@ -10,17 +13,9 @@ interface CompilationArtifact {
10} 13}
11 14
12export class Cargo { 15export class Cargo {
13 rootFolder: string; 16 constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
14 env?: Record<string, string>;
15 output: OutputChannel;
16
17 public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record<string, string> | undefined = undefined) {
18 this.rootFolder = cargoTomlFolder;
19 this.output = output;
20 this.env = env;
21 }
22 17
23 public async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> { 18 private async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> {
24 const artifacts: CompilationArtifact[] = []; 19 const artifacts: CompilationArtifact[] = [];
25 20
26 try { 21 try {
@@ -37,17 +32,13 @@ export class Cargo {
37 isTest: message.profile.test 32 isTest: message.profile.test
38 }); 33 });
39 } 34 }
40 } 35 } else if (message.reason === 'compiler-message') {
41 else if (message.reason === 'compiler-message') {
42 this.output.append(message.message.rendered); 36 this.output.append(message.message.rendered);
43 } 37 }
44 }, 38 },
45 stderr => { 39 stderr => this.output.append(stderr),
46 this.output.append(stderr);
47 }
48 ); 40 );
49 } 41 } catch (err) {
50 catch (err) {
51 this.output.show(true); 42 this.output.show(true);
52 throw new Error(`Cargo invocation has failed: ${err}`); 43 throw new Error(`Cargo invocation has failed: ${err}`);
53 } 44 }
@@ -55,11 +46,27 @@ export class Cargo {
55 return artifacts; 46 return artifacts;
56 } 47 }
57 48
58 public async executableFromArgs(args: string[]): Promise<string> { 49 async executableFromArgs(args: readonly string[]): Promise<string> {
59 const cargoArgs = [...args]; // to remain args unchanged 50 const cargoArgs = [...args, "--message-format=json"];
60 cargoArgs.push("--message-format=json");
61 51
62 const artifacts = await this.artifactsFromArgs(cargoArgs); 52 // arguments for a runnable from the quick pick should be updated.
53 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
54 switch (cargoArgs[0]) {
55 case "run": cargoArgs[0] = "build"; break;
56 case "test": {
57 if (cargoArgs.indexOf("--no-run") === -1) {
58 cargoArgs.push("--no-run");
59 }
60 break;
61 }
62 }
63
64 let artifacts = await this.artifactsFromArgs(cargoArgs);
65 if (cargoArgs[0] === "test") {
66 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
67 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
68 artifacts = artifacts.filter(a => a.isTest);
69 }
63 70
64 if (artifacts.length === 0) { 71 if (artifacts.length === 0) {
65 throw new Error('No compilation artifacts'); 72 throw new Error('No compilation artifacts');
@@ -70,24 +77,27 @@ export class Cargo {
70 return artifacts[0].fileName; 77 return artifacts[0].fileName;
71 } 78 }
72 79
73 runCargo( 80 private runCargo(
74 cargoArgs: string[], 81 cargoArgs: string[],
75 onStdoutJson: (obj: any) => void, 82 onStdoutJson: (obj: any) => void,
76 onStderrString: (data: string) => void 83 onStderrString: (data: string) => void
77 ): Promise<number> { 84 ): Promise<number> {
78 return new Promise<number>((resolve, reject) => { 85 return new Promise((resolve, reject) => {
79 const cargo = cp.spawn('cargo', cargoArgs, { 86 let cargoPath;
87 try {
88 cargoPath = getCargoPathOrFail();
89 } catch (err) {
90 return reject(err);
91 }
92
93 const cargo = cp.spawn(cargoPath, cargoArgs, {
80 stdio: ['ignore', 'pipe', 'pipe'], 94 stdio: ['ignore', 'pipe', 'pipe'],
81 cwd: this.rootFolder, 95 cwd: this.rootFolder
82 env: this.env,
83 }); 96 });
84 97
85 cargo.on('error', err => { 98 cargo.on('error', err => reject(new Error(`could not launch cargo: ${err}`)));
86 reject(new Error(`could not launch cargo: ${err}`)); 99
87 }); 100 cargo.stderr.on('data', chunk => onStderrString(chunk.toString()));
88 cargo.stderr.on('data', chunk => {
89 onStderrString(chunk.toString());
90 });
91 101
92 const rl = readline.createInterface({ input: cargo.stdout }); 102 const rl = readline.createInterface({ input: cargo.stdout });
93 rl.on('line', line => { 103 rl.on('line', line => {
@@ -103,4 +113,28 @@ export class Cargo {
103 }); 113 });
104 }); 114 });
105 } 115 }
106} \ No newline at end of file 116}
117
118// Mirrors `ra_env::get_path_for_executable` implementation
119function getCargoPathOrFail(): string {
120 const envVar = process.env.CARGO;
121 const executableName = "cargo";
122
123 if (envVar) {
124 if (isValidExecutable(envVar)) return envVar;
125
126 throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`);
127 }
128
129 if (isValidExecutable(executableName)) return executableName;
130
131 const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName);
132
133 if (isValidExecutable(standardLocation)) return standardLocation;
134
135 throw new Error(
136 `Failed to find \`${executableName}\` executable. ` +
137 `Make sure \`${executableName}\` is in \`$PATH\`, ` +
138 `or set \`${envVar}\` to point to a valid executable.`
139 );
140}
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index cffdcf11a..fac1a0be3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -31,24 +31,79 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
31 const res = await next(document, token); 31 const res = await next(document, token);
32 if (res === undefined) throw new Error('busy'); 32 if (res === undefined) throw new Error('busy');
33 return res; 33 return res;
34 },
35 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
36 const params: lc.CodeActionParams = {
37 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
38 range: client.code2ProtocolConverter.asRange(range),
39 context: client.code2ProtocolConverter.asCodeActionContext(context)
40 };
41 return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => {
42 if (values === null) return undefined;
43 const result: (vscode.CodeAction | vscode.Command)[] = [];
44 for (const item of values) {
45 if (lc.CodeAction.is(item)) {
46 const action = client.protocol2CodeConverter.asCodeAction(item);
47 if (isSnippetEdit(item)) {
48 action.command = {
49 command: "rust-analyzer.applySnippetWorkspaceEdit",
50 title: "",
51 arguments: [action.edit],
52 };
53 action.edit = undefined;
54 }
55 result.push(action);
56 } else {
57 const command = client.protocol2CodeConverter.asCommand(item);
58 result.push(command);
59 }
60 }
61 return result;
62 },
63 (_error) => undefined
64 );
34 } 65 }
66
35 } as any 67 } as any
36 }; 68 };
37 69
38 const res = new lc.LanguageClient( 70 const client = new lc.LanguageClient(
39 'rust-analyzer', 71 'rust-analyzer',
40 'Rust Analyzer Language Server', 72 'Rust Analyzer Language Server',
41 serverOptions, 73 serverOptions,
42 clientOptions, 74 clientOptions,
43 ); 75 );
44 76
45 // To turn on all proposed features use: res.registerProposedFeatures(); 77 // To turn on all proposed features use: client.registerProposedFeatures();
46 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature 78 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature
47 // since they are available on stable. 79 // since they are available on stable.
48 // Note that while these features are stable in vscode their LSP protocol 80 // Note that while these features are stable in vscode their LSP protocol
49 // implementations are still in the "proposed" category for 3.16. 81 // implementations are still in the "proposed" category for 3.16.
50 res.registerFeature(new CallHierarchyFeature(res)); 82 client.registerFeature(new CallHierarchyFeature(client));
51 res.registerFeature(new SemanticTokensFeature(res)); 83 client.registerFeature(new SemanticTokensFeature(client));
84 client.registerFeature(new SnippetTextEditFeature());
85
86 return client;
87}
52 88
53 return res; 89class SnippetTextEditFeature implements lc.StaticFeature {
90 fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
91 const caps: any = capabilities.experimental ?? {};
92 caps.snippetTextEdit = true;
93 capabilities.experimental = caps;
94 }
95 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
96 }
97}
98
99function isSnippetEdit(action: lc.CodeAction): boolean {
100 const documentChanges = action.edit?.documentChanges ?? [];
101 for (const edit of documentChanges) {
102 if (lc.TextDocumentEdit.is(edit)) {
103 if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) {
104 return true;
105 }
106 }
107 }
108 return false;
54} 109}
diff --git a/editors/code/src/color_theme.ts b/editors/code/src/color_theme.ts
deleted file mode 100644
index 5b9327b28..000000000
--- a/editors/code/src/color_theme.ts
+++ /dev/null
@@ -1,129 +0,0 @@
1import * as fs from 'fs';
2import * as jsonc from 'jsonc-parser';
3import * as path from 'path';
4import * as vscode from 'vscode';
5
6export interface TextMateRuleSettings {
7 foreground?: string;
8 background?: string;
9 fontStyle?: string;
10}
11
12export class ColorTheme {
13 private rules: Map<string, TextMateRuleSettings> = new Map();
14
15 static load(): ColorTheme {
16 // Find out current color theme
17 const themeName = vscode.workspace
18 .getConfiguration('workbench')
19 .get('colorTheme');
20
21 if (typeof themeName !== 'string') {
22 // console.warn('workbench.colorTheme is', themeName)
23 return new ColorTheme();
24 }
25 return loadThemeNamed(themeName);
26 }
27
28 static fromRules(rules: TextMateRule[]): ColorTheme {
29 const res = new ColorTheme();
30 for (const rule of rules) {
31 const scopes = typeof rule.scope === 'undefined'
32 ? []
33 : typeof rule.scope === 'string'
34 ? [rule.scope]
35 : rule.scope;
36
37 for (const scope of scopes) {
38 res.rules.set(scope, rule.settings);
39 }
40 }
41 return res;
42 }
43
44 lookup(scopes: string[]): TextMateRuleSettings {
45 let res: TextMateRuleSettings = {};
46 for (const scope of scopes) {
47 this.rules.forEach((value, key) => {
48 if (scope.startsWith(key)) {
49 res = mergeRuleSettings(res, value);
50 }
51 });
52 }
53 return res;
54 }
55
56 mergeFrom(other: ColorTheme) {
57 other.rules.forEach((value, key) => {
58 const merged = mergeRuleSettings(this.rules.get(key), value);
59 this.rules.set(key, merged);
60 });
61 }
62}
63
64function loadThemeNamed(themeName: string): ColorTheme {
65 function isTheme(extension: vscode.Extension<unknown>): boolean {
66 return (
67 extension.extensionKind === vscode.ExtensionKind.UI &&
68 extension.packageJSON.contributes &&
69 extension.packageJSON.contributes.themes
70 );
71 }
72
73 const themePaths: string[] = vscode.extensions.all
74 .filter(isTheme)
75 .flatMap(
76 ext => ext.packageJSON.contributes.themes
77 .filter((it: any) => (it.id || it.label) === themeName)
78 .map((it: any) => path.join(ext.extensionPath, it.path))
79 );
80
81 const res = new ColorTheme();
82 for (const themePath of themePaths) {
83 res.mergeFrom(loadThemeFile(themePath));
84 }
85
86 const globalCustomizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
87 res.mergeFrom(ColorTheme.fromRules(globalCustomizations?.textMateRules ?? []));
88
89 const themeCustomizations: any = vscode.workspace.getConfiguration('editor.tokenColorCustomizations').get(`[${themeName}]`);
90 res.mergeFrom(ColorTheme.fromRules(themeCustomizations?.textMateRules ?? []));
91
92
93 return res;
94}
95
96function loadThemeFile(themePath: string): ColorTheme {
97 let text;
98 try {
99 text = fs.readFileSync(themePath, 'utf8');
100 } catch {
101 return new ColorTheme();
102 }
103 const obj = jsonc.parse(text);
104 const tokenColors: TextMateRule[] = obj?.tokenColors ?? [];
105 const res = ColorTheme.fromRules(tokenColors);
106
107 for (const include of obj?.include ?? []) {
108 const includePath = path.join(path.dirname(themePath), include);
109 res.mergeFrom(loadThemeFile(includePath));
110 }
111
112 return res;
113}
114
115interface TextMateRule {
116 scope: string | string[];
117 settings: TextMateRuleSettings;
118}
119
120function mergeRuleSettings(
121 defaultSetting: TextMateRuleSettings | undefined,
122 override: TextMateRuleSettings,
123): TextMateRuleSettings {
124 return {
125 foreground: override.foreground ?? defaultSetting?.foreground,
126 background: override.background ?? defaultSetting?.background,
127 fontStyle: override.fontStyle ?? defaultSetting?.fontStyle,
128 };
129}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index bdb7fc3b0..e5ed77e32 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -4,6 +4,7 @@ import * as ra from '../rust-analyzer-api';
4 4
5import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
6import * as sourceChange from '../source_change'; 6import * as sourceChange from '../source_change';
7import { assert } from '../util';
7 8
8export * from './analyzer_status'; 9export * from './analyzer_status';
9export * from './matching_brace'; 10export * from './matching_brace';
@@ -51,3 +52,58 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd {
51 } 52 }
52 }; 53 };
53} 54}
55
56export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
57 return async (edit: vscode.WorkspaceEdit) => {
58 await applySnippetWorkspaceEdit(edit);
59 };
60}
61
62export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
63 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
64 const [uri, edits] = edit.entries()[0];
65
66 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
67 if (!editor) return;
68
69 let selection: vscode.Selection | undefined = undefined;
70 let lineDelta = 0;
71 await editor.edit((builder) => {
72 for (const indel of edits) {
73 const parsed = parseSnippet(indel.newText);
74 if (parsed) {
75 const [newText, [placeholderStart, placeholderLength]] = parsed;
76 const prefix = newText.substr(0, placeholderStart);
77 const lastNewline = prefix.lastIndexOf('\n');
78
79 const startLine = indel.range.start.line + lineDelta + countLines(prefix);
80 const startColumn = lastNewline === -1 ?
81 indel.range.start.character + placeholderStart
82 : prefix.length - lastNewline - 1;
83 const endColumn = startColumn + placeholderLength;
84 selection = new vscode.Selection(
85 new vscode.Position(startLine, startColumn),
86 new vscode.Position(startLine, endColumn),
87 );
88 builder.replace(indel.range, newText);
89 } else {
90 lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
91 builder.replace(indel.range, indel.newText);
92 }
93 }
94 });
95 if (selection) editor.selection = selection;
96}
97
98function parseSnippet(snip: string): [string, [number, number]] | undefined {
99 const m = snip.match(/\$(0|\{0:([^}]*)\})/);
100 if (!m) return undefined;
101 const placeholder = m[2] ?? "";
102 const range: [number, number] = [m.index!!, placeholder.length];
103 const insert = snip.replace(m[0], placeholder);
104 return [insert, range];
105}
106
107function countLines(text: string): number {
108 return (text.match(/\n/g) || []).length;
109}
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
index de0614653..0bf1ee6e6 100644
--- a/editors/code/src/commands/join_lines.ts
+++ b/editors/code/src/commands/join_lines.ts
@@ -1,7 +1,7 @@
1import * as ra from '../rust-analyzer-api'; 1import * as ra from '../rust-analyzer-api';
2import * as lc from 'vscode-languageclient';
2 3
3import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
4import { applySourceChange } from '../source_change';
5 5
6export function joinLines(ctx: Ctx): Cmd { 6export function joinLines(ctx: Ctx): Cmd {
7 return async () => { 7 return async () => {
@@ -9,10 +9,14 @@ 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 change = await client.sendRequest(ra.joinLines, { 12 const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
13 range: client.code2ProtocolConverter.asRange(editor.selection), 13 ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
14 textDocument: { uri: editor.document.uri.toString() }, 14 textDocument: { uri: editor.document.uri.toString() },
15 }); 15 });
16 await applySourceChange(ctx, change); 16 editor.edit((builder) => {
17 client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
18 builder.replace(edit.range, edit.newText);
19 });
20 });
17 }; 21 };
18} 22}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index 285849db7..a7871c31e 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -1,8 +1,8 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { applySourceChange } from '../source_change';
5import { Cmd, Ctx } from '../ctx'; 4import { Cmd, Ctx } from '../ctx';
5import { applySnippetWorkspaceEdit } from '.';
6 6
7async function handleKeypress(ctx: Ctx) { 7async function handleKeypress(ctx: Ctx) {
8 const editor = ctx.activeRustEditor; 8 const editor = ctx.activeRustEditor;
@@ -21,7 +21,8 @@ async function handleKeypress(ctx: Ctx) {
21 }); 21 });
22 if (!change) return false; 22 if (!change) return false;
23 23
24 await applySourceChange(ctx, change); 24 const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
25 await applySnippetWorkspaceEdit(workspaceEdit);
25 return true; 26 return true;
26} 27}
27 28
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index d77e8188c..0bd30fb07 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,43 +1,93 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api'; 3import * as ra from '../rust-analyzer-api';
4import * as os from "os";
5 4
6import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
7import { Cargo } from '../cargo'; 6import { startDebugSession, getDebugConfiguration } from '../debug';
8 7
9export function run(ctx: Ctx): Cmd { 8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
10 let prevRunnable: RunnableQuickPick | undefined;
11 9
12 return async () => { 10async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> {
13 const editor = ctx.activeRustEditor; 11 const editor = ctx.activeRustEditor;
14 const client = ctx.client; 12 const client = ctx.client;
15 if (!editor || !client) return; 13 if (!editor || !client) return;
16 14
17 const textDocument: lc.TextDocumentIdentifier = { 15 const textDocument: lc.TextDocumentIdentifier = {
18 uri: editor.document.uri.toString(), 16 uri: editor.document.uri.toString(),
19 }; 17 };
20 18
21 const runnables = await client.sendRequest(ra.runnables, { 19 const runnables = await client.sendRequest(ra.runnables, {
22 textDocument, 20 textDocument,
23 position: client.code2ProtocolConverter.asPosition( 21 position: client.code2ProtocolConverter.asPosition(
24 editor.selection.active, 22 editor.selection.active,
25 ), 23 ),
26 }); 24 });
27 const items: RunnableQuickPick[] = []; 25 const items: RunnableQuickPick[] = [];
28 if (prevRunnable) { 26 if (prevRunnable) {
29 items.push(prevRunnable); 27 items.push(prevRunnable);
28 }
29 for (const r of runnables) {
30 if (
31 prevRunnable &&
32 JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
33 ) {
34 continue;
30 } 35 }
31 for (const r of runnables) { 36
32 if ( 37 if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) {
33 prevRunnable && 38 continue;
34 JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
35 ) {
36 continue;
37 }
38 items.push(new RunnableQuickPick(r));
39 } 39 }
40 const item = await vscode.window.showQuickPick(items); 40 items.push(new RunnableQuickPick(r));
41 }
42
43 if (items.length === 0) {
44 // it is the debug case, run always has at least 'cargo check ...'
45 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables
46 vscode.window.showErrorMessage("There's no debug target!");
47 return;
48 }
49
50 return await new Promise((resolve) => {
51 const disposables: vscode.Disposable[] = [];
52 const close = (result?: RunnableQuickPick) => {
53 resolve(result);
54 disposables.forEach(d => d.dispose());
55 };
56
57 const quickPick = vscode.window.createQuickPick<RunnableQuickPick>();
58 quickPick.items = items;
59 quickPick.title = "Select Runnable";
60 if (showButtons) {
61 quickPick.buttons = quickPickButtons;
62 }
63 disposables.push(
64 quickPick.onDidHide(() => close()),
65 quickPick.onDidAccept(() => close(quickPick.selectedItems[0])),
66 quickPick.onDidTriggerButton((_button) => {
67 (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))();
68 close();
69 }),
70 quickPick.onDidChangeActive((active) => {
71 if (showButtons && active.length > 0) {
72 if (active[0].label.startsWith('cargo')) {
73 // save button makes no sense for `cargo test` or `cargo check`
74 quickPick.buttons = [];
75 } else if (quickPick.buttons.length === 0) {
76 quickPick.buttons = quickPickButtons;
77 }
78 }
79 }),
80 quickPick
81 );
82 quickPick.show();
83 });
84}
85
86export function run(ctx: Ctx): Cmd {
87 let prevRunnable: RunnableQuickPick | undefined;
88
89 return async () => {
90 const item = await selectRunnable(ctx, prevRunnable);
41 if (!item) return; 91 if (!item) return;
42 92
43 item.detail = 'rerun'; 93 item.detail = 'rerun';
@@ -64,71 +114,54 @@ export function runSingle(ctx: Ctx): Cmd {
64 }; 114 };
65} 115}
66 116
67function getLldbDebugConfig(config: ra.Runnable, sourceFileMap: Record<string, string>): vscode.DebugConfiguration { 117export function debug(ctx: Ctx): Cmd {
68 return { 118 let prevDebuggee: RunnableQuickPick | undefined;
69 type: "lldb",
70 request: "launch",
71 name: config.label,
72 cargo: {
73 args: config.args,
74 },
75 args: config.extraArgs,
76 cwd: config.cwd,
77 sourceMap: sourceFileMap
78 };
79}
80
81const debugOutput = vscode.window.createOutputChannel("Debug");
82 119
83async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record<string, string>): Promise<vscode.DebugConfiguration> { 120 return async () => {
84 debugOutput.clear(); 121 const item = await selectRunnable(ctx, prevDebuggee, true);
85 122 if (!item) return;
86 const cargo = new Cargo(config.cwd || '.', debugOutput);
87 const executable = await cargo.executableFromArgs(config.args);
88 123
89 // if we are here, there were no compilation errors. 124 item.detail = 'restart';
90 return { 125 prevDebuggee = item;
91 type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg', 126 return await startDebugSession(ctx, item.runnable);
92 request: "launch",
93 name: config.label,
94 program: executable,
95 args: config.extraArgs,
96 cwd: config.cwd,
97 sourceFileMap: sourceFileMap,
98 }; 127 };
99} 128}
100 129
101export function debugSingle(ctx: Ctx): Cmd { 130export function debugSingle(ctx: Ctx): Cmd {
102 return async (config: ra.Runnable) => { 131 return async (config: ra.Runnable) => {
103 const editor = ctx.activeRustEditor; 132 await startDebugSession(ctx, config);
104 if (!editor) return; 133 };
134}
105 135
106 const lldbId = "vadimcn.vscode-lldb"; 136async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void> {
107 const cpptoolsId = "ms-vscode.cpptools"; 137 const scope = ctx.activeRustEditor?.document.uri;
138 if (!scope) return;
108 139
109 const debugEngineId = ctx.config.debug.engine; 140 const debugConfig = await getDebugConfiguration(ctx, item.runnable);
110 let debugEngine = null; 141 if (!debugConfig) return;
111 if (debugEngineId === "auto") {
112 debugEngine = vscode.extensions.getExtension(lldbId);
113 if (!debugEngine) {
114 debugEngine = vscode.extensions.getExtension(cpptoolsId);
115 }
116 }
117 else {
118 debugEngine = vscode.extensions.getExtension(debugEngineId);
119 }
120 142
121 if (!debugEngine) { 143 const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
122 vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})` 144 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
123 + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`); 145
124 return; 146 const index = configurations.findIndex(c => c.name === debugConfig.name);
125 } 147 if (index !== -1) {
148 const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
149 if (answer === "Cancel") return;
150
151 configurations[index] = debugConfig;
152 } else {
153 configurations.push(debugConfig);
154 }
155
156 await wsLaunchSection.update("configurations", configurations);
157}
126 158
127 const debugConfig = lldbId === debugEngine.id 159export function newDebugConfig(ctx: Ctx): Cmd {
128 ? getLldbDebugConfig(config, ctx.config.debug.sourceFileMap) 160 return async () => {
129 : await getCppvsDebugConfig(config, ctx.config.debug.sourceFileMap); 161 const item = await selectRunnable(ctx, undefined, true, false);
162 if (!item) return;
130 163
131 return vscode.debug.startDebugging(undefined, debugConfig); 164 await makeDebugConfig(ctx, item);
132 }; 165 };
133} 166}
134 167
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index 6fee051fd..4ef8cdf04 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -11,7 +11,7 @@ export function ssr(ctx: Ctx): Cmd {
11 11
12 const options: vscode.InputBoxOptions = { 12 const options: vscode.InputBoxOptions = {
13 value: "() ==>> ()", 13 value: "() ==>> ()",
14 prompt: "EnteR request, for example 'Foo($a:expr) ==> Foo::new($a)' ", 14 prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
15 validateInput: async (x: string) => { 15 validateInput: async (x: string) => {
16 try { 16 try {
17 await client.sendRequest(ra.ssr, { query: x, parseOnly: true }); 17 await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index cfcf47b2f..a5446c327 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -206,7 +206,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D
206 } 206 }
207 207
208 private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range { 208 private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range {
209 const parsedRange = /\[(\d+); (\d+)\)/.exec(astLine); 209 const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine);
210 if (!parsedRange) return; 210 if (!parsedRange) return;
211 211
212 const [begin, end] = parsedRange 212 const [begin, end] = parsedRange
@@ -225,7 +225,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D
225 return doc.positionAt(targetOffset); 225 return doc.positionAt(targetOffset);
226 } 226 }
227 227
228 // Shitty workaround for crlf line endings 228 // Dirty workaround for crlf line endings
229 // We are still in this prehistoric era of carriage returns here... 229 // We are still in this prehistoric era of carriage returns here...
230 230
231 let line = 0; 231 let line = 0;
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 110e54180..ee294fbe3 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,6 +16,10 @@ export class Config {
16 "files", 16 "files",
17 "highlighting", 17 "highlighting",
18 "updates.channel", 18 "updates.channel",
19 "lens.enable",
20 "lens.run",
21 "lens.debug",
22 "lens.implementations",
19 ] 23 ]
20 .map(opt => `${this.rootSection}.${opt}`); 24 .map(opt => `${this.rootSection}.${opt}`);
21 25
@@ -94,6 +98,7 @@ export class Config {
94 98
95 get inlayHints() { 99 get inlayHints() {
96 return { 100 return {
101 enable: this.get<boolean>("inlayHints.enable"),
97 typeHints: this.get<boolean>("inlayHints.typeHints"), 102 typeHints: this.get<boolean>("inlayHints.typeHints"),
98 parameterHints: this.get<boolean>("inlayHints.parameterHints"), 103 parameterHints: this.get<boolean>("inlayHints.parameterHints"),
99 chainingHints: this.get<boolean>("inlayHints.chainingHints"), 104 chainingHints: this.get<boolean>("inlayHints.chainingHints"),
@@ -108,10 +113,23 @@ export class Config {
108 } 113 }
109 114
110 get debug() { 115 get debug() {
116 // "/rustc/<id>" used by suggestions only.
117 const { ["/rustc/<id>"]: _, ...sourceFileMap } = this.get<Record<string, string>>("debug.sourceFileMap");
118
111 return { 119 return {
112 engine: this.get<string>("debug.engine"), 120 engine: this.get<string>("debug.engine"),
113 sourceFileMap: this.get<Record<string, string>>("debug.sourceFileMap"), 121 engineSettings: this.get<object>("debug.engineSettings"),
122 openUpDebugPane: this.get<boolean>("debug.openUpDebugPane"),
123 sourceFileMap: sourceFileMap
114 }; 124 };
115 } 125 }
116 126
127 get lens() {
128 return {
129 enable: this.get<boolean>("lens.enable"),
130 run: this.get<boolean>("lens.run"),
131 debug: this.get<boolean>("lens.debug"),
132 implementations: this.get<boolean>("lens.implementations"),
133 };
134 }
117} 135}
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
new file mode 100644
index 000000000..d3fe588e8
--- /dev/null
+++ b/editors/code/src/debug.ts
@@ -0,0 +1,124 @@
1import * as os from "os";
2import * as vscode from 'vscode';
3import * as path from 'path';
4import * as ra from './rust-analyzer-api';
5
6import { Cargo } from './cargo';
7import { Ctx } from "./ctx";
8
9const debugOutput = vscode.window.createOutputChannel("Debug");
10type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration;
11
12function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
13 return {
14 type: "lldb",
15 request: "launch",
16 name: config.label,
17 program: executable,
18 args: config.extraArgs,
19 cwd: config.cwd,
20 sourceMap: sourceFileMap,
21 sourceLanguages: ["rust"]
22 };
23}
24
25function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
26 return {
27 type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg",
28 request: "launch",
29 name: config.label,
30 program: executable,
31 args: config.extraArgs,
32 cwd: config.cwd,
33 sourceFileMap: sourceFileMap,
34 };
35}
36
37async function getDebugExecutable(config: ra.Runnable): Promise<string> {
38 const cargo = new Cargo(config.cwd || '.', debugOutput);
39 const executable = await cargo.executableFromArgs(config.args);
40
41 // if we are here, there were no compilation errors.
42 return executable;
43}
44
45export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> {
46 const editor = ctx.activeRustEditor;
47 if (!editor) return;
48
49 const knownEngines: Record<string, DebugConfigProvider> = {
50 "vadimcn.vscode-lldb": getLldbDebugConfig,
51 "ms-vscode.cpptools": getCppvsDebugConfig
52 };
53 const debugOptions = ctx.config.debug;
54
55 let debugEngine = null;
56 if (debugOptions.engine === "auto") {
57 for (var engineId in knownEngines) {
58 debugEngine = vscode.extensions.getExtension(engineId);
59 if (debugEngine) break;
60 }
61 } else {
62 debugEngine = vscode.extensions.getExtension(debugOptions.engine);
63 }
64
65 if (!debugEngine) {
66 vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)`
67 + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`);
68 return;
69 }
70
71 debugOutput.clear();
72 if (ctx.config.debug.openUpDebugPane) {
73 debugOutput.show(true);
74 }
75
76 const wsFolder = path.normalize(vscode.workspace.workspaceFolders![0].uri.fsPath); // folder exists or RA is not active.
77 function simplifyPath(p: string): string {
78 return path.normalize(p).replace(wsFolder, '${workspaceRoot}');
79 }
80
81 const executable = await getDebugExecutable(config);
82 const debugConfig = knownEngines[debugEngine.id](config, simplifyPath(executable), debugOptions.sourceFileMap);
83 if (debugConfig.type in debugOptions.engineSettings) {
84 const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
85 for (var key in settingsMap) {
86 debugConfig[key] = settingsMap[key];
87 }
88 }
89
90 if (debugConfig.name === "run binary") {
91 // The LSP side: crates\rust-analyzer\src\main_loop\handlers.rs,
92 // fn to_lsp_runnable(...) with RunnableKind::Bin
93 debugConfig.name = `run ${path.basename(executable)}`;
94 }
95
96 if (debugConfig.cwd) {
97 debugConfig.cwd = simplifyPath(debugConfig.cwd);
98 }
99
100 return debugConfig;
101}
102
103export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise<boolean> {
104 let debugConfig: vscode.DebugConfiguration | undefined = undefined;
105 let message = "";
106
107 const wsLaunchSection = vscode.workspace.getConfiguration("launch");
108 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
109
110 const index = configurations.findIndex(c => c.name === config.label);
111 if (-1 !== index) {
112 debugConfig = configurations[index];
113 message = " (from launch.json)";
114 debugOutput.clear();
115 } else {
116 debugConfig = await getDebugConfiguration(ctx, config);
117 }
118
119 if (!debugConfig) return false;
120
121 debugOutput.appendLine(`Launching debug configuration${message}:`);
122 debugOutput.appendLine(JSON.stringify(debugConfig, null, 2));
123 return vscode.debug.startDebugging(undefined, debugConfig);
124}
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index a09531797..a2b07d003 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -10,13 +10,13 @@ export function activateInlayHints(ctx: Ctx) {
10 const maybeUpdater = { 10 const maybeUpdater = {
11 updater: null as null | HintsUpdater, 11 updater: null as null | HintsUpdater,
12 async onConfigChange() { 12 async onConfigChange() {
13 if ( 13 const anyEnabled = ctx.config.inlayHints.typeHints
14 !ctx.config.inlayHints.typeHints && 14 || ctx.config.inlayHints.parameterHints
15 !ctx.config.inlayHints.parameterHints && 15 || ctx.config.inlayHints.chainingHints;
16 !ctx.config.inlayHints.chainingHints 16 const enabled = ctx.config.inlayHints.enable && anyEnabled;
17 ) { 17
18 return this.dispose(); 18 if (!enabled) return this.dispose();
19 } 19
20 await sleep(100); 20 await sleep(100);
21 if (this.updater) { 21 if (this.updater) {
22 this.updater.syncCacheAndRenderHints(); 22 this.updater.syncCacheAndRenderHints();
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index efd56a84b..8b0a9d870 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -8,10 +8,9 @@ import { activateInlayHints } from './inlay_hints';
8import { activateStatusDisplay } from './status_display'; 8import { activateStatusDisplay } from './status_display';
9import { Ctx } from './ctx'; 9import { Ctx } from './ctx';
10import { Config, NIGHTLY_TAG } from './config'; 10import { Config, NIGHTLY_TAG } from './config';
11import { log, assert } from './util'; 11import { log, assert, isValidExecutable } from './util';
12import { PersistentState } from './persistent_state'; 12import { PersistentState } from './persistent_state';
13import { fetchRelease, download } from './net'; 13import { fetchRelease, download } from './net';
14import { spawnSync } from 'child_process';
15import { activateTaskProvider } from './tasks'; 14import { activateTaskProvider } from './tasks';
16 15
17let ctx: Ctx | undefined; 16let ctx: Ctx | undefined;
@@ -78,6 +77,8 @@ export async function activate(context: vscode.ExtensionContext) {
78 ctx.registerCommand('syntaxTree', commands.syntaxTree); 77 ctx.registerCommand('syntaxTree', commands.syntaxTree);
79 ctx.registerCommand('expandMacro', commands.expandMacro); 78 ctx.registerCommand('expandMacro', commands.expandMacro);
80 ctx.registerCommand('run', commands.run); 79 ctx.registerCommand('run', commands.run);
80 ctx.registerCommand('debug', commands.debug);
81 ctx.registerCommand('newDebugConfig', commands.newDebugConfig);
81 82
82 defaultOnEnter.dispose(); 83 defaultOnEnter.dispose();
83 ctx.registerCommand('onEnter', commands.onEnter); 84 ctx.registerCommand('onEnter', commands.onEnter);
@@ -90,6 +91,7 @@ export async function activate(context: vscode.ExtensionContext) {
90 ctx.registerCommand('debugSingle', commands.debugSingle); 91 ctx.registerCommand('debugSingle', commands.debugSingle);
91 ctx.registerCommand('showReferences', commands.showReferences); 92 ctx.registerCommand('showReferences', commands.showReferences);
92 ctx.registerCommand('applySourceChange', commands.applySourceChange); 93 ctx.registerCommand('applySourceChange', commands.applySourceChange);
94 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
93 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); 95 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
94 96
95 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 97 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
@@ -179,10 +181,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
179 181
180 log.debug("Using server binary at", path); 182 log.debug("Using server binary at", path);
181 183
182 const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); 184 if (!isValidExecutable(path)) {
183 log.debug("Checked binary availability via --version", res);
184 log.debug(res, "--version output:", res.output);
185 if (res.status !== 0) {
186 throw new Error(`Failed to execute ${path} --version`); 185 throw new Error(`Failed to execute ${path} --version`);
187 } 186 }
188 187
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index 400ac3714..8ed56c173 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -64,12 +64,12 @@ export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Locati
64 64
65export interface JoinLinesParams { 65export interface JoinLinesParams {
66 textDocument: lc.TextDocumentIdentifier; 66 textDocument: lc.TextDocumentIdentifier;
67 range: lc.Range; 67 ranges: lc.Range[];
68} 68}
69export const joinLines = request<JoinLinesParams, SourceChange>("joinLines"); 69export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], unknown>('experimental/joinLines');
70 70
71 71
72export const onEnter = request<lc.TextDocumentPositionParams, Option<SourceChange>>("onEnter"); 72export const onEnter = request<lc.TextDocumentPositionParams, Option<lc.WorkspaceEdit>>("onEnter");
73 73
74export interface RunnablesParams { 74export interface RunnablesParams {
75 textDocument: lc.TextDocumentIdentifier; 75 textDocument: lc.TextDocumentIdentifier;
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 6f91f81d6..127a9e911 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -1,6 +1,7 @@
1import * as lc from "vscode-languageclient"; 1import * as lc from "vscode-languageclient";
2import * as vscode from "vscode"; 2import * as vscode from "vscode";
3import { strict as nativeAssert } from "assert"; 3import { strict as nativeAssert } from "assert";
4import { spawnSync } from "child_process";
4 5
5export function assert(condition: boolean, explanation: string): asserts condition { 6export function assert(condition: boolean, explanation: string): asserts condition {
6 try { 7 try {
@@ -82,3 +83,13 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD
82export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { 83export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
83 return isRustDocument(editor.document); 84 return isRustDocument(editor.document);
84} 85}
86
87export function isValidExecutable(path: string): boolean {
88 log.debug("Checking availability of a binary at", path);
89
90 const res = spawnSync(path, ["--version"], { encoding: 'utf8' });
91
92 log.debug(res, "--version output:", res.output);
93
94 return res.status === 0;
95}