mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
Auto merge of #12294 - listochkin:prettier, r=Veykril
Switch to Prettier for TypeScript Code formatting ## Summary of changes: 1. Added [`.editorconfig` file](https://editorconfig.org) to dictate general hygienic stuff like character encoding, no trailing whitespace, new line symbols etc. for all files (e.g. Markdown). Install an editor plugin to get this rudimentary formatting assistance automatically. Prettier can read this file and, for example, use it for indentation style and size. 2. Added a minimal prettier config file. All options are default except line width, which per [Veykril](https://github.com/Veykril) suggestion is set to 100 instead of 80, because [that's what `Rustfmt` uses](https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#max_width). 3. Change `package.json` to use Prettier instead of `tsfmt` for code formatting. 4. Performed initial formatting in a separate commit, per [bjorn3](https://github.com/bjorn3) suggestion added its hash to a `.git-blame-ignore-revs` file. For it to work you need to add a configuration to your git installation: ```Shell git config --global blame.ignoreRevsFile .git-blame-ignore-revs ``` 5. Finally, removed `typescript-formatter` from the list of dependencies. ---- What follows below is summary of the discussion we had on Zulip about the formatter switch: ## Background For the context, there are three reasons why we went with `tsfmt` originally: * stick to vscode default/built-in * don't add extra deps to package.json.lock * follow upstream (language server node I think still uses `tsfmt`) And the meta reason here was that we didn't have anyone familiar with frontend, so went for the simplest option, at the expense of features and convenience. Meanwhile, [**Prettier**](https://prettier.io) became a formatter project that JS community consolidated on a few years ago. It's similar to `go fmt` / `cargo fmt` in spirit: minimal to no configuration to promote general uniformity in the ecosystem. There are some options, that were needed early on to make sure the project gained momentum, but by no means it's a customizable formatter that is easy to adjust to reduce the number of changes for adoption. ## Overview of changes performed by Prettier Some of the changes are acceptable. Prettier dictates a unified string quoting style, and as a result half of our imports at the top are changed. No one would mind that. Some one-line changes are due to string quotes, too, and although these a re numerous, the surrounding lines aren't changed, and git blame / GitLens will still show relevant context. Some are toss ups. `trailingComma` option - set it to `none`, and get a bunch of meaningless changes in half of the code. set it to `all` and get a bunch of changes in the other half of the code. Same with using parentheses around single parameters in arrow functions: `x => x + 1` vs `(x) => x + 1`. Perrier forces one style or the other, but we use both in our code. Like I said, the changes above are Ok - they take a single line, don't disrupt GitLens / git blame much. **The big one is line width**. Prettier wants you to choose one and stick to it. The default is 80 and it forces some reformatting to squish deeply nested code or long function type declarations. If I set it to 100-120, then Prettier finds other parts of code where a multi-line expression can be smashed into a single long line. The problem is that in both cases some of the lines that get changed are interesting, they contain somewhat non-trivial logic, and if I were to work on them in future I would love to see the commit annotations that tell me something relevant. Alas, we use some of that. ## Project impact Though Prettier is a mainstream JS project it has no dependencies. We add another package so that it and ESLint work together nicely, and that's it.
This commit is contained in:
commit
1a5925dc84
30 changed files with 1255 additions and 966 deletions
19
.editorconfig
Normal file
19
.editorconfig
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# https://EditorConfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
|
||||||
|
[*.{rs,toml}]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
indent_size = 4
|
||||||
|
[*.js]
|
||||||
|
indent_size = 4
|
||||||
|
[*.json]
|
||||||
|
indent_size = 4
|
8
.git-blame-ignore-revs
Normal file
8
.git-blame-ignore-revs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# for this file to take effect make sure you use git ^2.23 and
|
||||||
|
# add ignoreFile to your git configuration:
|
||||||
|
# ```
|
||||||
|
# git config --global blame.ignoreRevsFile .git-blame-ignore-revs
|
||||||
|
# ```
|
||||||
|
|
||||||
|
# prettier format
|
||||||
|
f247090558c9ba3c551566eae5882b7ca865225f
|
|
@ -1,40 +1,37 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
env: {
|
||||||
"es6": true,
|
es6: true,
|
||||||
"node": true
|
node: true,
|
||||||
},
|
},
|
||||||
"parser": "@typescript-eslint/parser",
|
extends: ["prettier"],
|
||||||
"parserOptions": {
|
parser: "@typescript-eslint/parser",
|
||||||
"project": "tsconfig.eslint.json",
|
parserOptions: {
|
||||||
"tsconfigRootDir": __dirname,
|
project: "tsconfig.eslint.json",
|
||||||
"sourceType": "module"
|
tsconfigRootDir: __dirname,
|
||||||
|
sourceType: "module",
|
||||||
},
|
},
|
||||||
"plugins": [
|
plugins: ["@typescript-eslint"],
|
||||||
"@typescript-eslint"
|
rules: {
|
||||||
],
|
camelcase: ["error"],
|
||||||
"rules": {
|
eqeqeq: ["error", "always", { null: "ignore" }],
|
||||||
"camelcase": ["error"],
|
curly: ["error", "multi-line"],
|
||||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
|
||||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||||
"prefer-const": "error",
|
"prefer-const": "error",
|
||||||
"@typescript-eslint/member-delimiter-style": [
|
"@typescript-eslint/member-delimiter-style": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"multiline": {
|
multiline: {
|
||||||
"delimiter": "semi",
|
delimiter: "semi",
|
||||||
"requireLast": true
|
requireLast: true,
|
||||||
},
|
},
|
||||||
"singleline": {
|
singleline: {
|
||||||
"delimiter": "semi",
|
delimiter: "semi",
|
||||||
"requireLast": false
|
requireLast: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"@typescript-eslint/semi": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
],
|
||||||
|
"@typescript-eslint/semi": ["error", "always"],
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||||
"@typescript-eslint/no-floating-promises": "error"
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
3
editors/code/.prettierignore
Normal file
3
editors/code/.prettierignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
.vscode-test
|
||||||
|
out
|
5
editors/code/.prettierrc.js
Normal file
5
editors/code/.prettierrc.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
// use 100 because it's Rustfmt's default
|
||||||
|
// https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#max_width
|
||||||
|
printWidth: 100,
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"comments": {
|
"comments": {
|
||||||
"lineComment": "//",
|
"lineComment": "//",
|
||||||
"blockComment": [ "/*", "*/" ]
|
"blockComment": ["/*", "*/"]
|
||||||
},
|
},
|
||||||
"brackets": [
|
"brackets": [
|
||||||
["{", "}"],
|
["{", "}"],
|
||||||
|
@ -9,10 +9,10 @@
|
||||||
["(", ")"]
|
["(", ")"]
|
||||||
],
|
],
|
||||||
"colorizedBracketPairs": [
|
"colorizedBracketPairs": [
|
||||||
["{", "}"],
|
["{", "}"],
|
||||||
["[", "]"],
|
["[", "]"],
|
||||||
["(", ")"]
|
["(", ")"]
|
||||||
],
|
],
|
||||||
"autoClosingPairs": [
|
"autoClosingPairs": [
|
||||||
{ "open": "{", "close": "}" },
|
{ "open": "{", "close": "}" },
|
||||||
{ "open": "[", "close": "]" },
|
{ "open": "[", "close": "]" },
|
||||||
|
|
200
editors/code/package-lock.json
generated
200
editors/code/package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "rust-analyzer",
|
"name": "rust-analyzer",
|
||||||
"version": "0.4.0-dev",
|
"version": "0.5.0-dev",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "rust-analyzer",
|
"name": "rust-analyzer",
|
||||||
"version": "0.4.0-dev",
|
"version": "0.5.0-dev",
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"d3": "^7.3.0",
|
"d3": "^7.3.0",
|
||||||
|
@ -22,9 +22,10 @@
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.14.27",
|
"esbuild": "^0.14.27",
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.11.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"prettier": "^2.6.2",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.3",
|
||||||
"typescript-formatter": "^7.2.2",
|
|
||||||
"vsce": "^2.7.0"
|
"vsce": "^2.7.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -768,12 +769,6 @@
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commandpost": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/commandpost/-/commandpost-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-aE2Y4MTFJ870NuB/+2z1cXBhSBBzRydVVjzhFC4gtenEhpnj15yu0qptWGJsO9YGrcPZ3ezX8AWb1VA391MKpQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
@ -1492,52 +1487,6 @@
|
||||||
"readable-stream": "^2.0.2"
|
"readable-stream": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/editorconfig": {
|
|
||||||
"version": "0.15.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
|
|
||||||
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"commander": "^2.19.0",
|
|
||||||
"lru-cache": "^4.1.5",
|
|
||||||
"semver": "^5.6.0",
|
|
||||||
"sigmund": "^1.0.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"editorconfig": "bin/editorconfig"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/editorconfig/node_modules/commander": {
|
|
||||||
"version": "2.20.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
|
||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/editorconfig/node_modules/lru-cache": {
|
|
||||||
"version": "4.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
|
||||||
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"pseudomap": "^1.0.2",
|
|
||||||
"yallist": "^2.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/editorconfig/node_modules/semver": {
|
|
||||||
"version": "5.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/editorconfig/node_modules/yallist": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
|
||||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
@ -1988,6 +1937,18 @@
|
||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-config-prettier": {
|
||||||
|
"version": "8.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
|
||||||
|
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-scope": {
|
"node_modules/eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
@ -3130,18 +3091,27 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process-nextick-args": {
|
"node_modules/process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/pseudomap": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -3412,12 +3382,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sigmund": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
@ -3734,25 +3698,6 @@
|
||||||
"node": ">=4.2.0"
|
"node": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-formatter": {
|
|
||||||
"version": "7.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript-formatter/-/typescript-formatter-7.2.2.tgz",
|
|
||||||
"integrity": "sha512-V7vfI9XArVhriOTYHPzMU2WUnm5IMdu9X/CPxs8mIMGxmTBFpDABlbkBka64PZJ9/xgQeRpK8KzzAG4MPzxBDQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"commandpost": "^1.0.0",
|
|
||||||
"editorconfig": "^0.15.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"tsfmt": "bin/tsfmt"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4.2.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^2.1.6 || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/uc.micro": {
|
"node_modules/uc.micro": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||||
|
@ -4659,12 +4604,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
|
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
|
||||||
},
|
},
|
||||||
"commandpost": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/commandpost/-/commandpost-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-aE2Y4MTFJ870NuB/+2z1cXBhSBBzRydVVjzhFC4gtenEhpnj15yu0qptWGJsO9YGrcPZ3ezX8AWb1VA391MKpQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
@ -5207,48 +5146,6 @@
|
||||||
"readable-stream": "^2.0.2"
|
"readable-stream": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editorconfig": {
|
|
||||||
"version": "0.15.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
|
|
||||||
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"commander": "^2.19.0",
|
|
||||||
"lru-cache": "^4.1.5",
|
|
||||||
"semver": "^5.6.0",
|
|
||||||
"sigmund": "^1.0.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"commander": {
|
|
||||||
"version": "2.20.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
|
||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"lru-cache": {
|
|
||||||
"version": "4.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
|
||||||
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"pseudomap": "^1.0.2",
|
|
||||||
"yallist": "^2.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"semver": {
|
|
||||||
"version": "5.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"yallist": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
|
||||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"emoji-regex": {
|
"emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
@ -5509,6 +5406,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-config-prettier": {
|
||||||
|
"version": "8.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
|
||||||
|
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"eslint-scope": {
|
"eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
@ -6386,18 +6290,18 @@
|
||||||
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
|
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pseudomap": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"pump": {
|
"pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -6587,12 +6491,6 @@
|
||||||
"object-inspect": "^1.9.0"
|
"object-inspect": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sigmund": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"signal-exit": {
|
"signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
@ -6820,16 +6718,6 @@
|
||||||
"integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
|
"integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript-formatter": {
|
|
||||||
"version": "7.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript-formatter/-/typescript-formatter-7.2.2.tgz",
|
|
||||||
"integrity": "sha512-V7vfI9XArVhriOTYHPzMU2WUnm5IMdu9X/CPxs8mIMGxmTBFpDABlbkBka64PZJ9/xgQeRpK8KzzAG4MPzxBDQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"commandpost": "^1.0.0",
|
|
||||||
"editorconfig": "^0.15.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"uc.micro": {
|
"uc.micro": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||||
|
|
|
@ -29,8 +29,8 @@
|
||||||
"build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16",
|
"build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16",
|
||||||
"build": "npm run build-base -- --sourcemap",
|
"build": "npm run build-base -- --sourcemap",
|
||||||
"watch": "npm run build-base -- --sourcemap --watch",
|
"watch": "npm run build-base -- --sourcemap --watch",
|
||||||
"lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src ./tests",
|
"lint": "prettier --check . && eslint -c .eslintrc.js --ext ts ./src ./tests",
|
||||||
"fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
|
"fix": "prettier --write . && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
|
||||||
"pretest": "tsc && npm run build",
|
"pretest": "tsc && npm run build",
|
||||||
"test": "cross-env TEST_VARIABLE=test node ./out/tests/runTests.js"
|
"test": "cross-env TEST_VARIABLE=test node ./out/tests/runTests.js"
|
||||||
},
|
},
|
||||||
|
@ -48,9 +48,10 @@
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.14.27",
|
"esbuild": "^0.14.27",
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.11.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"prettier": "^2.6.2",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.3",
|
||||||
"typescript-formatter": "^7.2.2",
|
|
||||||
"vsce": "^2.7.0"
|
"vsce": "^2.7.0"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
|
|
|
@ -25,7 +25,5 @@
|
||||||
"name": "string"
|
"name": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fileTypes": [
|
"fileTypes": ["rast"]
|
||||||
"rast"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
import { Ctx, Disposable } from './ctx';
|
import { Ctx, Disposable } from "./ctx";
|
||||||
import { RustEditor, isRustEditor } from './util';
|
import { RustEditor, isRustEditor } from "./util";
|
||||||
|
|
||||||
// FIXME: consider implementing this via the Tree View API?
|
// FIXME: consider implementing this via the Tree View API?
|
||||||
// https://code.visualstudio.com/api/extension-guides/tree-view
|
// https://code.visualstudio.com/api/extension-guides/tree-view
|
||||||
export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
|
export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
|
||||||
private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
|
private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
|
||||||
borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
|
borderColor: new vscode.ThemeColor("rust_analyzer.syntaxTreeBorder"),
|
||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
borderWidth: "2px",
|
borderWidth: "2px",
|
||||||
});
|
});
|
||||||
|
@ -35,11 +35,23 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(ctx: Ctx) {
|
constructor(ctx: Ctx) {
|
||||||
ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: 'rust-analyzer' }, this));
|
ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: "rust-analyzer" }, this));
|
||||||
ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
|
ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
|
||||||
vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
|
vscode.workspace.onDidCloseTextDocument(
|
||||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
this.onDidCloseTextDocument,
|
||||||
vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
vscode.workspace.onDidChangeTextDocument(
|
||||||
|
this.onDidChangeTextDocument,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
vscode.window.onDidChangeVisibleTextEditors(
|
||||||
|
this.onDidChangeVisibleTextEditors,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
|
||||||
ctx.pushCleanup(this);
|
ctx.pushCleanup(this);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +60,10 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) {
|
if (
|
||||||
|
this.rustEditor &&
|
||||||
|
event.document.uri.toString() === this.rustEditor.document.uri.toString()
|
||||||
|
) {
|
||||||
this.rust2Ast.reset();
|
this.rust2Ast.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +83,9 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
}
|
}
|
||||||
|
|
||||||
private findAstTextEditor(): undefined | vscode.TextEditor {
|
private findAstTextEditor(): undefined | vscode.TextEditor {
|
||||||
return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === 'rust-analyzer');
|
return vscode.window.visibleTextEditors.find(
|
||||||
|
(it) => it.document.uri.scheme === "rust-analyzer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setRustEditor(newRustEditor: undefined | RustEditor) {
|
private setRustEditor(newRustEditor: undefined | RustEditor) {
|
||||||
|
@ -80,13 +97,20 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
}
|
}
|
||||||
|
|
||||||
// additional positional params are omitted
|
// additional positional params are omitted
|
||||||
provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> {
|
provideDefinition(
|
||||||
if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return;
|
doc: vscode.TextDocument,
|
||||||
|
pos: vscode.Position
|
||||||
|
): vscode.ProviderResult<vscode.DefinitionLink[]> {
|
||||||
|
if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const astEditor = this.findAstTextEditor();
|
const astEditor = this.findAstTextEditor();
|
||||||
if (!astEditor) return;
|
if (!astEditor) return;
|
||||||
|
|
||||||
const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos));
|
const rust2AstRanges = this.rust2Ast
|
||||||
|
.get()
|
||||||
|
?.find(([rustRange, _]) => rustRange.contains(pos));
|
||||||
if (!rust2AstRanges) return;
|
if (!rust2AstRanges) return;
|
||||||
|
|
||||||
const [rustFileRange, astFileRange] = rust2AstRanges;
|
const [rustFileRange, astFileRange] = rust2AstRanges;
|
||||||
|
@ -94,16 +118,21 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
astEditor.revealRange(astFileRange);
|
astEditor.revealRange(astFileRange);
|
||||||
astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
|
astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
|
||||||
|
|
||||||
return [{
|
return [
|
||||||
targetRange: astFileRange,
|
{
|
||||||
targetUri: astEditor.document.uri,
|
targetRange: astFileRange,
|
||||||
originSelectionRange: rustFileRange,
|
targetUri: astEditor.document.uri,
|
||||||
targetSelectionRange: astFileRange,
|
originSelectionRange: rustFileRange,
|
||||||
}];
|
targetSelectionRange: astFileRange,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// additional positional params are omitted
|
// additional positional params are omitted
|
||||||
provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
|
provideHover(
|
||||||
|
doc: vscode.TextDocument,
|
||||||
|
hoverPosition: vscode.Position
|
||||||
|
): vscode.ProviderResult<vscode.Hover> {
|
||||||
if (!this.rustEditor) return;
|
if (!this.rustEditor) return;
|
||||||
|
|
||||||
const astFileLine = doc.lineAt(hoverPosition.line);
|
const astFileLine = doc.lineAt(hoverPosition.line);
|
||||||
|
@ -127,13 +156,14 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
return new vscode.Range(begin, end);
|
return new vscode.Range(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range {
|
private parseRustTextRange(
|
||||||
|
doc: vscode.TextDocument,
|
||||||
|
astLine: string
|
||||||
|
): undefined | vscode.Range {
|
||||||
const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine);
|
const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine);
|
||||||
if (!parsedRange) return;
|
if (!parsedRange) return;
|
||||||
|
|
||||||
const [begin, end] = parsedRange
|
const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off));
|
||||||
.slice(1)
|
|
||||||
.map(off => this.positionAt(doc, +off));
|
|
||||||
|
|
||||||
return new vscode.Range(begin, end);
|
return new vscode.Range(begin, end);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +203,7 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||||
class Lazy<T> {
|
class Lazy<T> {
|
||||||
val: undefined | T;
|
val: undefined | T;
|
||||||
|
|
||||||
constructor(private readonly compute: () => undefined | T) { }
|
constructor(private readonly compute: () => undefined | T) {}
|
||||||
|
|
||||||
get() {
|
get() {
|
||||||
return this.val ?? (this.val = this.compute());
|
return this.val ?? (this.val = this.compute());
|
||||||
|
|
|
@ -1,39 +1,47 @@
|
||||||
import * as lc from 'vscode-languageclient/node';
|
import * as lc from "vscode-languageclient/node";
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as ra from '../src/lsp_ext';
|
import * as ra from "../src/lsp_ext";
|
||||||
import * as Is from 'vscode-languageclient/lib/common/utils/is';
|
import * as Is from "vscode-languageclient/lib/common/utils/is";
|
||||||
import { assert } from './util';
|
import { assert } from "./util";
|
||||||
import { WorkspaceEdit } from 'vscode';
|
import { WorkspaceEdit } from "vscode";
|
||||||
import { Workspace } from './ctx';
|
import { Workspace } from "./ctx";
|
||||||
import { updateConfig } from './config';
|
import { updateConfig } from "./config";
|
||||||
import { substituteVariablesInEnv } from './config';
|
import { substituteVariablesInEnv } from "./config";
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
[name: string]: string;
|
[name: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCommand(cmd: ra.CommandLink) {
|
function renderCommand(cmd: ra.CommandLink) {
|
||||||
return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip}')`;
|
return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(
|
||||||
|
JSON.stringify(cmd.arguments)
|
||||||
|
)} '${cmd.tooltip}')`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {
|
function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {
|
||||||
const text = actions.map(group =>
|
const text = actions
|
||||||
(group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ')
|
.map(
|
||||||
).join('___');
|
(group) =>
|
||||||
|
(group.title ? group.title + " " : "") +
|
||||||
|
group.commands.map(renderCommand).join(" | ")
|
||||||
|
)
|
||||||
|
.join("___");
|
||||||
|
|
||||||
const result = new vscode.MarkdownString(text);
|
const result = new vscode.MarkdownString(text);
|
||||||
result.isTrusted = true;
|
result.isTrusted = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createClient(serverPath: string, workspace: Workspace, extraEnv: Env): Promise<lc.LanguageClient> {
|
export async function createClient(
|
||||||
|
serverPath: string,
|
||||||
|
workspace: Workspace,
|
||||||
|
extraEnv: Env
|
||||||
|
): Promise<lc.LanguageClient> {
|
||||||
// '.' Is the fallback if no folder is open
|
// '.' Is the fallback if no folder is open
|
||||||
// TODO?: Workspace folders support Uri's (eg: file://test.txt).
|
// TODO?: Workspace folders support Uri's (eg: file://test.txt).
|
||||||
// It might be a good idea to test if the uri points to a file.
|
// It might be a good idea to test if the uri points to a file.
|
||||||
|
|
||||||
const newEnv = substituteVariablesInEnv(Object.assign(
|
const newEnv = substituteVariablesInEnv(Object.assign({}, process.env, extraEnv));
|
||||||
{}, process.env, extraEnv
|
|
||||||
));
|
|
||||||
const run: lc.Executable = {
|
const run: lc.Executable = {
|
||||||
command: serverPath,
|
command: serverPath,
|
||||||
options: { env: newEnv },
|
options: { env: newEnv },
|
||||||
|
@ -43,137 +51,176 @@ export async function createClient(serverPath: string, workspace: Workspace, ext
|
||||||
debug: run,
|
debug: run,
|
||||||
};
|
};
|
||||||
const traceOutputChannel = vscode.window.createOutputChannel(
|
const traceOutputChannel = vscode.window.createOutputChannel(
|
||||||
'Rust Analyzer Language Server Trace',
|
"Rust Analyzer Language Server Trace"
|
||||||
);
|
);
|
||||||
|
|
||||||
let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
|
let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
|
||||||
|
|
||||||
// Update outdated user configs
|
// Update outdated user configs
|
||||||
await updateConfig(initializationOptions).catch(err => {
|
await updateConfig(initializationOptions).catch((err) => {
|
||||||
void vscode.window.showErrorMessage(`Failed updating old config keys: ${err.message}`);
|
void vscode.window.showErrorMessage(`Failed updating old config keys: ${err.message}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workspace.kind === "Detached Files") {
|
if (workspace.kind === "Detached Files") {
|
||||||
initializationOptions = { "detachedFiles": workspace.files.map(file => file.uri.fsPath), ...initializationOptions };
|
initializationOptions = {
|
||||||
|
detachedFiles: workspace.files.map((file) => file.uri.fsPath),
|
||||||
|
...initializationOptions,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientOptions: lc.LanguageClientOptions = {
|
const clientOptions: lc.LanguageClientOptions = {
|
||||||
documentSelector: [{ scheme: 'file', language: 'rust' }],
|
documentSelector: [{ scheme: "file", language: "rust" }],
|
||||||
initializationOptions,
|
initializationOptions,
|
||||||
diagnosticCollectionName: "rustc",
|
diagnosticCollectionName: "rustc",
|
||||||
traceOutputChannel,
|
traceOutputChannel,
|
||||||
middleware: {
|
middleware: {
|
||||||
async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
|
async provideHover(
|
||||||
|
document: vscode.TextDocument,
|
||||||
|
position: vscode.Position,
|
||||||
|
token: vscode.CancellationToken,
|
||||||
|
_next: lc.ProvideHoverSignature
|
||||||
|
) {
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
const positionOrRange = editor?.selection?.contains(position) ? client.code2ProtocolConverter.asRange(editor.selection) : client.code2ProtocolConverter.asPosition(position);
|
const positionOrRange = editor?.selection?.contains(position)
|
||||||
return client.sendRequest(ra.hover, {
|
? client.code2ProtocolConverter.asRange(editor.selection)
|
||||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
: client.code2ProtocolConverter.asPosition(position);
|
||||||
position: positionOrRange
|
return client
|
||||||
}, token).then(
|
.sendRequest(
|
||||||
(result) => {
|
ra.hover,
|
||||||
const hover =
|
{
|
||||||
client.protocol2CodeConverter.asHover(result);
|
textDocument:
|
||||||
if (hover) {
|
client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||||
const actions = (<any>result).actions;
|
position: positionOrRange,
|
||||||
if (actions) {
|
},
|
||||||
hover.contents.push(renderHoverActions(actions));
|
token
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(result) => {
|
||||||
|
const hover = client.protocol2CodeConverter.asHover(result);
|
||||||
|
if (hover) {
|
||||||
|
const actions = (<any>result).actions;
|
||||||
|
if (actions) {
|
||||||
|
hover.contents.push(renderHoverActions(actions));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return hover;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
client.handleFailedRequest(lc.HoverRequest.type, token, error, null);
|
||||||
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
return hover;
|
);
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
client.handleFailedRequest(
|
|
||||||
lc.HoverRequest.type,
|
|
||||||
token,
|
|
||||||
error,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
// Using custom handling of CodeActions to support action groups and snippet edits.
|
// Using custom handling of CodeActions to support action groups and snippet edits.
|
||||||
// Note that this means we have to re-implement lazy edit resolving ourselves as well.
|
// Note that this means we have to re-implement lazy edit resolving ourselves as well.
|
||||||
async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
|
async provideCodeActions(
|
||||||
|
document: vscode.TextDocument,
|
||||||
|
range: vscode.Range,
|
||||||
|
context: vscode.CodeActionContext,
|
||||||
|
token: vscode.CancellationToken,
|
||||||
|
_next: lc.ProvideCodeActionsSignature
|
||||||
|
) {
|
||||||
const params: lc.CodeActionParams = {
|
const params: lc.CodeActionParams = {
|
||||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||||
range: client.code2ProtocolConverter.asRange(range),
|
range: client.code2ProtocolConverter.asRange(range),
|
||||||
context: await client.code2ProtocolConverter.asCodeActionContext(context, token)
|
context: await client.code2ProtocolConverter.asCodeActionContext(
|
||||||
|
context,
|
||||||
|
token
|
||||||
|
),
|
||||||
};
|
};
|
||||||
return client.sendRequest(lc.CodeActionRequest.type, params, token).then(async (values) => {
|
return client.sendRequest(lc.CodeActionRequest.type, params, token).then(
|
||||||
if (values === null) return undefined;
|
async (values) => {
|
||||||
const result: (vscode.CodeAction | vscode.Command)[] = [];
|
if (values === null) return undefined;
|
||||||
const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
|
const result: (vscode.CodeAction | vscode.Command)[] = [];
|
||||||
for (const item of values) {
|
const groups = new Map<
|
||||||
// In our case we expect to get code edits only from diagnostics
|
string,
|
||||||
if (lc.CodeAction.is(item)) {
|
{ index: number; items: vscode.CodeAction[] }
|
||||||
assert(!item.command, "We don't expect to receive commands in CodeActions");
|
>();
|
||||||
const action = await client.protocol2CodeConverter.asCodeAction(item, token);
|
for (const item of values) {
|
||||||
result.push(action);
|
// In our case we expect to get code edits only from diagnostics
|
||||||
continue;
|
if (lc.CodeAction.is(item)) {
|
||||||
}
|
assert(
|
||||||
assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here");
|
!item.command,
|
||||||
const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
|
"We don't expect to receive commands in CodeActions"
|
||||||
const action = new vscode.CodeAction(item.title, kind);
|
);
|
||||||
const group = (item as any).group;
|
const action = await client.protocol2CodeConverter.asCodeAction(
|
||||||
action.command = {
|
item,
|
||||||
command: "rust-analyzer.resolveCodeAction",
|
token
|
||||||
title: item.title,
|
);
|
||||||
arguments: [item],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
|
||||||
action.edit = new WorkspaceEdit();
|
|
||||||
|
|
||||||
if (group) {
|
|
||||||
let entry = groups.get(group);
|
|
||||||
if (!entry) {
|
|
||||||
entry = { index: result.length, items: [] };
|
|
||||||
groups.set(group, entry);
|
|
||||||
result.push(action);
|
result.push(action);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
entry.items.push(action);
|
assert(
|
||||||
} else {
|
isCodeActionWithoutEditsAndCommands(item),
|
||||||
result.push(action);
|
"We don't expect edits or commands here"
|
||||||
}
|
);
|
||||||
}
|
const kind = client.protocol2CodeConverter.asCodeActionKind(
|
||||||
for (const [group, { index, items }] of groups) {
|
(item as any).kind
|
||||||
if (items.length === 1) {
|
);
|
||||||
result[index] = items[0];
|
const action = new vscode.CodeAction(item.title, kind);
|
||||||
} else {
|
const group = (item as any).group;
|
||||||
const action = new vscode.CodeAction(group);
|
|
||||||
action.kind = items[0].kind;
|
|
||||||
action.command = {
|
action.command = {
|
||||||
command: "rust-analyzer.applyActionGroup",
|
command: "rust-analyzer.resolveCodeAction",
|
||||||
title: "",
|
title: item.title,
|
||||||
arguments: [items.map((item) => {
|
arguments: [item],
|
||||||
return { label: item.title, arguments: item.command!.arguments![0] };
|
|
||||||
})],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
||||||
action.edit = new WorkspaceEdit();
|
action.edit = new WorkspaceEdit();
|
||||||
|
|
||||||
result[index] = action;
|
if (group) {
|
||||||
|
let entry = groups.get(group);
|
||||||
|
if (!entry) {
|
||||||
|
entry = { index: result.length, items: [] };
|
||||||
|
groups.set(group, entry);
|
||||||
|
result.push(action);
|
||||||
|
}
|
||||||
|
entry.items.push(action);
|
||||||
|
} else {
|
||||||
|
result.push(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
for (const [group, { index, items }] of groups) {
|
||||||
return result;
|
if (items.length === 1) {
|
||||||
},
|
result[index] = items[0];
|
||||||
|
} else {
|
||||||
|
const action = new vscode.CodeAction(group);
|
||||||
|
action.kind = items[0].kind;
|
||||||
|
action.command = {
|
||||||
|
command: "rust-analyzer.applyActionGroup",
|
||||||
|
title: "",
|
||||||
|
arguments: [
|
||||||
|
items.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item.title,
|
||||||
|
arguments: item.command!.arguments![0],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set a dummy edit, so that VS Code doesn't try to resolve this.
|
||||||
|
action.edit = new WorkspaceEdit();
|
||||||
|
|
||||||
|
result[index] = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
(_error) => undefined
|
(_error) => undefined
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
markdown: {
|
markdown: {
|
||||||
supportHtml: true,
|
supportHtml: true,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const client = new lc.LanguageClient(
|
const client = new lc.LanguageClient(
|
||||||
'rust-analyzer',
|
"rust-analyzer",
|
||||||
'Rust Analyzer Language Server',
|
"Rust Analyzer Language Server",
|
||||||
serverOptions,
|
serverOptions,
|
||||||
clientOptions,
|
clientOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
// To turn on all proposed features use: client.registerProposedFeatures();
|
// To turn on all proposed features use: client.registerProposedFeatures();
|
||||||
|
@ -196,20 +243,26 @@ class ExperimentalFeatures implements lc.StaticFeature {
|
||||||
"rust-analyzer.showReferences",
|
"rust-analyzer.showReferences",
|
||||||
"rust-analyzer.gotoLocation",
|
"rust-analyzer.gotoLocation",
|
||||||
"editor.action.triggerParameterHints",
|
"editor.action.triggerParameterHints",
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
capabilities.experimental = caps;
|
capabilities.experimental = caps;
|
||||||
}
|
}
|
||||||
initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
|
initialize(
|
||||||
}
|
_capabilities: lc.ServerCapabilities<any>,
|
||||||
dispose(): void {
|
_documentSelector: lc.DocumentSelector | undefined
|
||||||
}
|
): void {}
|
||||||
|
dispose(): void {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCodeActionWithoutEditsAndCommands(value: any): boolean {
|
function isCodeActionWithoutEditsAndCommands(value: any): boolean {
|
||||||
const candidate: lc.CodeAction = value;
|
const candidate: lc.CodeAction = value;
|
||||||
return candidate && Is.string(candidate.title) &&
|
return (
|
||||||
(candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
|
candidate &&
|
||||||
|
Is.string(candidate.title) &&
|
||||||
|
(candidate.diagnostics === void 0 ||
|
||||||
|
Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
|
||||||
(candidate.kind === void 0 || Is.string(candidate.kind)) &&
|
(candidate.kind === void 0 || Is.string(candidate.kind)) &&
|
||||||
(candidate.edit === void 0 && candidate.command === void 0);
|
candidate.edit === void 0 &&
|
||||||
|
candidate.command === void 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,33 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as lc from 'vscode-languageclient';
|
import * as lc from "vscode-languageclient";
|
||||||
import * as ra from './lsp_ext';
|
import * as ra from "./lsp_ext";
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
|
|
||||||
import { Ctx, Cmd } from './ctx';
|
import { Ctx, Cmd } from "./ctx";
|
||||||
import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
|
import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from "child_process";
|
||||||
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from './run';
|
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
|
||||||
import { AstInspector } from './ast_inspector';
|
import { AstInspector } from "./ast_inspector";
|
||||||
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from './util';
|
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util";
|
||||||
import { startDebugSession, makeDebugConfig } from './debug';
|
import { startDebugSession, makeDebugConfig } from "./debug";
|
||||||
import { LanguageClient } from 'vscode-languageclient/node';
|
import { LanguageClient } from "vscode-languageclient/node";
|
||||||
|
|
||||||
export * from './ast_inspector';
|
export * from "./ast_inspector";
|
||||||
export * from './run';
|
export * from "./run";
|
||||||
|
|
||||||
export function analyzerStatus(ctx: Ctx): Cmd {
|
export function analyzerStatus(ctx: Ctx): Cmd {
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
|
readonly uri = vscode.Uri.parse("rust-analyzer-status://status");
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
|
|
||||||
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
||||||
if (!vscode.window.activeTextEditor) return '';
|
if (!vscode.window.activeTextEditor) return "";
|
||||||
|
|
||||||
const params: ra.AnalyzerStatusParams = {};
|
const params: ra.AnalyzerStatusParams = {};
|
||||||
const doc = ctx.activeRustEditor?.document;
|
const doc = ctx.activeRustEditor?.document;
|
||||||
if (doc != null) {
|
if (doc != null) {
|
||||||
params.textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(doc);
|
params.textDocument =
|
||||||
|
ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(doc);
|
||||||
}
|
}
|
||||||
return ctx.client.sendRequest(ra.analyzerStatus, params);
|
return ctx.client.sendRequest(ra.analyzerStatus, params);
|
||||||
}
|
}
|
||||||
|
@ -34,48 +35,42 @@ export function analyzerStatus(ctx: Ctx): Cmd {
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
}();
|
})();
|
||||||
|
|
||||||
ctx.pushCleanup(
|
ctx.pushCleanup(
|
||||||
vscode.workspace.registerTextDocumentContentProvider(
|
vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-status", tdcp)
|
||||||
'rust-analyzer-status',
|
|
||||||
tdcp,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||||
tdcp.eventEmitter.fire(tdcp.uri);
|
tdcp.eventEmitter.fire(tdcp.uri);
|
||||||
void await vscode.window.showTextDocument(document, {
|
void (await vscode.window.showTextDocument(document, {
|
||||||
viewColumn: vscode.ViewColumn.Two,
|
viewColumn: vscode.ViewColumn.Two,
|
||||||
preserveFocus: true
|
preserveFocus: true,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function memoryUsage(ctx: Ctx): Cmd {
|
export function memoryUsage(ctx: Ctx): Cmd {
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse('rust-analyzer-memory://memory');
|
readonly uri = vscode.Uri.parse("rust-analyzer-memory://memory");
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
|
|
||||||
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
|
||||||
if (!vscode.window.activeTextEditor) return '';
|
if (!vscode.window.activeTextEditor) return "";
|
||||||
|
|
||||||
return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
|
return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
|
||||||
return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)';
|
return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
}();
|
})();
|
||||||
|
|
||||||
ctx.pushCleanup(
|
ctx.pushCleanup(
|
||||||
vscode.workspace.registerTextDocumentContentProvider(
|
vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-memory", tdcp)
|
||||||
'rust-analyzer-memory',
|
|
||||||
tdcp,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
|
@ -101,15 +96,15 @@ export function matchingBrace(ctx: Ctx): Cmd {
|
||||||
if (!editor || !client) return;
|
if (!editor || !client) return;
|
||||||
|
|
||||||
const response = await client.sendRequest(ra.matchingBrace, {
|
const response = await client.sendRequest(ra.matchingBrace, {
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
positions: editor.selections.map(s =>
|
editor.document
|
||||||
client.code2ProtocolConverter.asPosition(s.active),
|
),
|
||||||
|
positions: editor.selections.map((s) =>
|
||||||
|
client.code2ProtocolConverter.asPosition(s.active)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
editor.selections = editor.selections.map((sel, idx) => {
|
editor.selections = editor.selections.map((sel, idx) => {
|
||||||
const active = client.protocol2CodeConverter.asPosition(
|
const active = client.protocol2CodeConverter.asPosition(response[idx]);
|
||||||
response[idx],
|
|
||||||
);
|
|
||||||
const anchor = sel.isEmpty ? active : sel.anchor;
|
const anchor = sel.isEmpty ? active : sel.anchor;
|
||||||
return new vscode.Selection(anchor, active);
|
return new vscode.Selection(anchor, active);
|
||||||
});
|
});
|
||||||
|
@ -125,7 +120,9 @@ export function joinLines(ctx: Ctx): Cmd {
|
||||||
|
|
||||||
const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
|
const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
|
||||||
ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
|
ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
editor.document
|
||||||
|
),
|
||||||
});
|
});
|
||||||
await editor.edit(async (builder) => {
|
await editor.edit(async (builder) => {
|
||||||
(await client.protocol2CodeConverter.asTextEdits(items)).forEach((edit: any) => {
|
(await client.protocol2CodeConverter.asTextEdits(items)).forEach((edit: any) => {
|
||||||
|
@ -151,8 +148,10 @@ export function moveItem(ctx: Ctx, direction: ra.Direction): Cmd {
|
||||||
|
|
||||||
const lcEdits = await client.sendRequest(ra.moveItem, {
|
const lcEdits = await client.sendRequest(ra.moveItem, {
|
||||||
range: client.code2ProtocolConverter.asRange(editor.selection),
|
range: client.code2ProtocolConverter.asRange(editor.selection),
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
direction
|
editor.document
|
||||||
|
),
|
||||||
|
direction,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!lcEdits) return;
|
if (!lcEdits) return;
|
||||||
|
@ -169,15 +168,17 @@ export function onEnter(ctx: Ctx): Cmd {
|
||||||
|
|
||||||
if (!editor || !client) return false;
|
if (!editor || !client) return false;
|
||||||
|
|
||||||
const lcEdits = await client.sendRequest(ra.onEnter, {
|
const lcEdits = await client
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
.sendRequest(ra.onEnter, {
|
||||||
position: client.code2ProtocolConverter.asPosition(
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
editor.selection.active,
|
editor.document
|
||||||
),
|
),
|
||||||
}).catch((_error: any) => {
|
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||||
// client.handleFailedRequest(OnEnterRequest.type, error, null);
|
})
|
||||||
return null;
|
.catch((_error: any) => {
|
||||||
});
|
// client.handleFailedRequest(OnEnterRequest.type, error, null);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
if (!lcEdits) return false;
|
if (!lcEdits) return false;
|
||||||
|
|
||||||
const edits = await client.protocol2CodeConverter.asTextEdits(lcEdits);
|
const edits = await client.protocol2CodeConverter.asTextEdits(lcEdits);
|
||||||
|
@ -188,7 +189,7 @@ export function onEnter(ctx: Ctx): Cmd {
|
||||||
return async () => {
|
return async () => {
|
||||||
if (await handleKeypress()) return;
|
if (await handleKeypress()) return;
|
||||||
|
|
||||||
await vscode.commands.executeCommand('default:type', { text: '\n' });
|
await vscode.commands.executeCommand("default:type", { text: "\n" });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,10 +201,10 @@ export function parentModule(ctx: Ctx): Cmd {
|
||||||
if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return;
|
if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return;
|
||||||
|
|
||||||
const locations = await client.sendRequest(ra.parentModule, {
|
const locations = await client.sendRequest(ra.parentModule, {
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
position: client.code2ProtocolConverter.asPosition(
|
editor.document
|
||||||
editor.selection.active,
|
|
||||||
),
|
),
|
||||||
|
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||||
});
|
});
|
||||||
if (!locations) return;
|
if (!locations) return;
|
||||||
|
|
||||||
|
@ -220,7 +221,12 @@ export function parentModule(ctx: Ctx): Cmd {
|
||||||
} else {
|
} else {
|
||||||
const uri = editor.document.uri.toString();
|
const uri = editor.document.uri.toString();
|
||||||
const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
|
const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
|
||||||
await showReferencesImpl(client, uri, position, locations.map(loc => lc.Location.create(loc.targetUri, loc.targetRange)));
|
await showReferencesImpl(
|
||||||
|
client,
|
||||||
|
uri,
|
||||||
|
position,
|
||||||
|
locations.map((loc) => lc.Location.create(loc.targetUri, loc.targetRange))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -232,7 +238,9 @@ export function openCargoToml(ctx: Ctx): Cmd {
|
||||||
if (!editor || !client) return;
|
if (!editor || !client) return;
|
||||||
|
|
||||||
const response = await client.sendRequest(ra.openCargoToml, {
|
const response = await client.sendRequest(ra.openCargoToml, {
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
editor.document
|
||||||
|
),
|
||||||
});
|
});
|
||||||
if (!response) return;
|
if (!response) return;
|
||||||
|
|
||||||
|
@ -254,7 +262,9 @@ export function ssr(ctx: Ctx): Cmd {
|
||||||
|
|
||||||
const position = editor.selection.active;
|
const position = editor.selection.active;
|
||||||
const selections = editor.selections;
|
const selections = editor.selections;
|
||||||
const textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document);
|
const textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
editor.document
|
||||||
|
);
|
||||||
|
|
||||||
const options: vscode.InputBoxOptions = {
|
const options: vscode.InputBoxOptions = {
|
||||||
value: "() ==>> ()",
|
value: "() ==>> ()",
|
||||||
|
@ -262,28 +272,41 @@ export function ssr(ctx: Ctx): Cmd {
|
||||||
validateInput: async (x: string) => {
|
validateInput: async (x: string) => {
|
||||||
try {
|
try {
|
||||||
await client.sendRequest(ra.ssr, {
|
await client.sendRequest(ra.ssr, {
|
||||||
query: x, parseOnly: true, textDocument, position, selections,
|
query: x,
|
||||||
|
parseOnly: true,
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
selections,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return e.toString();
|
return e.toString();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
const request = await vscode.window.showInputBox(options);
|
const request = await vscode.window.showInputBox(options);
|
||||||
if (!request) return;
|
if (!request) return;
|
||||||
|
|
||||||
await vscode.window.withProgress({
|
await vscode.window.withProgress(
|
||||||
location: vscode.ProgressLocation.Notification,
|
{
|
||||||
title: "Structured search replace in progress...",
|
location: vscode.ProgressLocation.Notification,
|
||||||
cancellable: false,
|
title: "Structured search replace in progress...",
|
||||||
}, async (_progress, token) => {
|
cancellable: false,
|
||||||
const edit = await client.sendRequest(ra.ssr, {
|
},
|
||||||
query: request, parseOnly: false, textDocument, position, selections,
|
async (_progress, token) => {
|
||||||
});
|
const edit = await client.sendRequest(ra.ssr, {
|
||||||
|
query: request,
|
||||||
|
parseOnly: false,
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
selections,
|
||||||
|
});
|
||||||
|
|
||||||
await vscode.workspace.applyEdit(await client.protocol2CodeConverter.asWorkspaceEdit(edit, token));
|
await vscode.workspace.applyEdit(
|
||||||
});
|
await client.protocol2CodeConverter.asWorkspaceEdit(edit, token)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,17 +315,17 @@ export function serverVersion(ctx: Ctx): Cmd {
|
||||||
const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
|
const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
|
||||||
const versionString = stdout.slice(`rust-analyzer `.length).trim();
|
const versionString = stdout.slice(`rust-analyzer `.length).trim();
|
||||||
|
|
||||||
void vscode.window.showInformationMessage(
|
void vscode.window.showInformationMessage(`rust-analyzer version: ${versionString}`);
|
||||||
`rust-analyzer version: ${versionString}`
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleInlayHints(_ctx: Ctx): Cmd {
|
export function toggleInlayHints(_ctx: Ctx): Cmd {
|
||||||
return async () => {
|
return async () => {
|
||||||
const config = vscode.workspace.getConfiguration("editor.inlayHints", { languageId: "rust" });
|
const config = vscode.workspace.getConfiguration("editor.inlayHints", {
|
||||||
|
languageId: "rust",
|
||||||
|
});
|
||||||
const value = !config.get("enabled");
|
const value = !config.get("enabled");
|
||||||
await config.update('enabled', value, vscode.ConfigurationTarget.Global);
|
await config.update("enabled", value, vscode.ConfigurationTarget.Global);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,12 +333,20 @@ export function toggleInlayHints(_ctx: Ctx): Cmd {
|
||||||
//
|
//
|
||||||
// The contents of the file come from the `TextDocumentContentProvider`
|
// The contents of the file come from the `TextDocumentContentProvider`
|
||||||
export function syntaxTree(ctx: Ctx): Cmd {
|
export function syntaxTree(ctx: Ctx): Cmd {
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
|
readonly uri = vscode.Uri.parse("rust-analyzer://syntaxtree/tree.rast");
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
constructor() {
|
constructor() {
|
||||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
vscode.workspace.onDidChangeTextDocument(
|
||||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
this.onDidChangeTextDocument,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
vscode.window.onDidChangeActiveTextEditor(
|
||||||
|
this.onDidChangeActiveTextEditor,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
|
@ -331,47 +362,51 @@ export function syntaxTree(ctx: Ctx): Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
provideTextDocumentContent(
|
||||||
|
uri: vscode.Uri,
|
||||||
|
ct: vscode.CancellationToken
|
||||||
|
): vscode.ProviderResult<string> {
|
||||||
const rustEditor = ctx.activeRustEditor;
|
const rustEditor = ctx.activeRustEditor;
|
||||||
if (!rustEditor) return '';
|
if (!rustEditor) return "";
|
||||||
|
|
||||||
// When the range based query is enabled we take the range of the selection
|
// When the range based query is enabled we take the range of the selection
|
||||||
const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
|
const range =
|
||||||
? ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
|
uri.query === "range=true" && !rustEditor.selection.isEmpty
|
||||||
: null;
|
? ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
|
||||||
|
: null;
|
||||||
|
|
||||||
const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
|
const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range };
|
||||||
return ctx.client.sendRequest(ra.syntaxTree, params, ct);
|
return ctx.client.sendRequest(ra.syntaxTree, params, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
|
|
||||||
void new AstInspector(ctx);
|
void new AstInspector(ctx);
|
||||||
|
|
||||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||||
ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
|
ctx.pushCleanup(
|
||||||
brackets: [["[", ")"]],
|
vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
|
||||||
}));
|
brackets: [["[", ")"]],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
const rangeEnabled = !!editor && !editor.selection.isEmpty;
|
const rangeEnabled = !!editor && !editor.selection.isEmpty;
|
||||||
|
|
||||||
const uri = rangeEnabled
|
const uri = rangeEnabled ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) : tdcp.uri;
|
||||||
? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
|
|
||||||
: tdcp.uri;
|
|
||||||
|
|
||||||
const document = await vscode.workspace.openTextDocument(uri);
|
const document = await vscode.workspace.openTextDocument(uri);
|
||||||
|
|
||||||
tdcp.eventEmitter.fire(uri);
|
tdcp.eventEmitter.fire(uri);
|
||||||
|
|
||||||
void await vscode.window.showTextDocument(document, {
|
void (await vscode.window.showTextDocument(document, {
|
||||||
viewColumn: vscode.ViewColumn.Two,
|
viewColumn: vscode.ViewColumn.Two,
|
||||||
preserveFocus: true
|
preserveFocus: true,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,12 +414,20 @@ export function syntaxTree(ctx: Ctx): Cmd {
|
||||||
//
|
//
|
||||||
// The contents of the file come from the `TextDocumentContentProvider`
|
// The contents of the file come from the `TextDocumentContentProvider`
|
||||||
export function viewHir(ctx: Ctx): Cmd {
|
export function viewHir(ctx: Ctx): Cmd {
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse('rust-analyzer://viewHir/hir.txt');
|
readonly uri = vscode.Uri.parse("rust-analyzer://viewHir/hir.txt");
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
constructor() {
|
constructor() {
|
||||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
vscode.workspace.onDidChangeTextDocument(
|
||||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
this.onDidChangeTextDocument,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
vscode.window.onDidChangeActiveTextEditor(
|
||||||
|
this.onDidChangeActiveTextEditor,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
|
@ -400,16 +443,19 @@ export function viewHir(ctx: Ctx): Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
provideTextDocumentContent(
|
||||||
|
_uri: vscode.Uri,
|
||||||
|
ct: vscode.CancellationToken
|
||||||
|
): vscode.ProviderResult<string> {
|
||||||
const rustEditor = ctx.activeRustEditor;
|
const rustEditor = ctx.activeRustEditor;
|
||||||
const client = ctx.client;
|
const client = ctx.client;
|
||||||
if (!rustEditor || !client) return '';
|
if (!rustEditor || !client) return "";
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document),
|
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
position: client.code2ProtocolConverter.asPosition(
|
rustEditor.document
|
||||||
rustEditor.selection.active,
|
|
||||||
),
|
),
|
||||||
|
position: client.code2ProtocolConverter.asPosition(rustEditor.selection.active),
|
||||||
};
|
};
|
||||||
return client.sendRequest(ra.viewHir, params, ct);
|
return client.sendRequest(ra.viewHir, params, ct);
|
||||||
}
|
}
|
||||||
|
@ -417,27 +463,35 @@ export function viewHir(ctx: Ctx): Cmd {
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
|
|
||||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||||
tdcp.eventEmitter.fire(tdcp.uri);
|
tdcp.eventEmitter.fire(tdcp.uri);
|
||||||
void await vscode.window.showTextDocument(document, {
|
void (await vscode.window.showTextDocument(document, {
|
||||||
viewColumn: vscode.ViewColumn.Two,
|
viewColumn: vscode.ViewColumn.Two,
|
||||||
preserveFocus: true
|
preserveFocus: true,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function viewFileText(ctx: Ctx): Cmd {
|
export function viewFileText(ctx: Ctx): Cmd {
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse('rust-analyzer://viewFileText/file.rs');
|
readonly uri = vscode.Uri.parse("rust-analyzer://viewFileText/file.rs");
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
constructor() {
|
constructor() {
|
||||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
vscode.workspace.onDidChangeTextDocument(
|
||||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
this.onDidChangeTextDocument,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
vscode.window.onDidChangeActiveTextEditor(
|
||||||
|
this.onDidChangeActiveTextEditor,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
|
@ -453,39 +507,52 @@ export function viewFileText(ctx: Ctx): Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
provideTextDocumentContent(
|
||||||
|
_uri: vscode.Uri,
|
||||||
|
ct: vscode.CancellationToken
|
||||||
|
): vscode.ProviderResult<string> {
|
||||||
const rustEditor = ctx.activeRustEditor;
|
const rustEditor = ctx.activeRustEditor;
|
||||||
const client = ctx.client;
|
const client = ctx.client;
|
||||||
if (!rustEditor || !client) return '';
|
if (!rustEditor || !client) return "";
|
||||||
|
|
||||||
const params = client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document);
|
const params = client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
rustEditor.document
|
||||||
|
);
|
||||||
return client.sendRequest(ra.viewFileText, params, ct);
|
return client.sendRequest(ra.viewFileText, params, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
|
|
||||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||||
tdcp.eventEmitter.fire(tdcp.uri);
|
tdcp.eventEmitter.fire(tdcp.uri);
|
||||||
void await vscode.window.showTextDocument(document, {
|
void (await vscode.window.showTextDocument(document, {
|
||||||
viewColumn: vscode.ViewColumn.Two,
|
viewColumn: vscode.ViewColumn.Two,
|
||||||
preserveFocus: true
|
preserveFocus: true,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function viewItemTree(ctx: Ctx): Cmd {
|
export function viewItemTree(ctx: Ctx): Cmd {
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse('rust-analyzer://viewItemTree/itemtree.rs');
|
readonly uri = vscode.Uri.parse("rust-analyzer://viewItemTree/itemtree.rs");
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
constructor() {
|
constructor() {
|
||||||
vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
|
vscode.workspace.onDidChangeTextDocument(
|
||||||
vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
|
this.onDidChangeTextDocument,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
|
vscode.window.onDidChangeActiveTextEditor(
|
||||||
|
this.onDidChangeActiveTextEditor,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
|
@ -501,13 +568,18 @@ export function viewItemTree(ctx: Ctx): Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
|
provideTextDocumentContent(
|
||||||
|
_uri: vscode.Uri,
|
||||||
|
ct: vscode.CancellationToken
|
||||||
|
): vscode.ProviderResult<string> {
|
||||||
const rustEditor = ctx.activeRustEditor;
|
const rustEditor = ctx.activeRustEditor;
|
||||||
const client = ctx.client;
|
const client = ctx.client;
|
||||||
if (!rustEditor || !client) return '';
|
if (!rustEditor || !client) return "";
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document),
|
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
rustEditor.document
|
||||||
|
),
|
||||||
};
|
};
|
||||||
return client.sendRequest(ra.viewItemTree, params, ct);
|
return client.sendRequest(ra.viewItemTree, params, ct);
|
||||||
}
|
}
|
||||||
|
@ -515,17 +587,17 @@ export function viewItemTree(ctx: Ctx): Cmd {
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
|
|
||||||
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
|
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||||
tdcp.eventEmitter.fire(tdcp.uri);
|
tdcp.eventEmitter.fire(tdcp.uri);
|
||||||
void await vscode.window.showTextDocument(document, {
|
void (await vscode.window.showTextDocument(document, {
|
||||||
viewColumn: vscode.ViewColumn.Two,
|
viewColumn: vscode.ViewColumn.Two,
|
||||||
preserveFocus: true
|
preserveFocus: true,
|
||||||
});
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,11 +605,16 @@ function crateGraph(ctx: Ctx, full: boolean): Cmd {
|
||||||
return async () => {
|
return async () => {
|
||||||
const nodeModulesPath = vscode.Uri.file(path.join(ctx.extensionPath, "node_modules"));
|
const nodeModulesPath = vscode.Uri.file(path.join(ctx.extensionPath, "node_modules"));
|
||||||
|
|
||||||
const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two, {
|
const panel = vscode.window.createWebviewPanel(
|
||||||
enableScripts: true,
|
"rust-analyzer.crate-graph",
|
||||||
retainContextWhenHidden: true,
|
"rust-analyzer crate graph",
|
||||||
localResourceRoots: [nodeModulesPath]
|
vscode.ViewColumn.Two,
|
||||||
});
|
{
|
||||||
|
enableScripts: true,
|
||||||
|
retainContextWhenHidden: true,
|
||||||
|
localResourceRoots: [nodeModulesPath],
|
||||||
|
}
|
||||||
|
);
|
||||||
const params = {
|
const params = {
|
||||||
full: full,
|
full: full,
|
||||||
};
|
};
|
||||||
|
@ -601,29 +678,31 @@ export function viewFullCrateGraph(ctx: Ctx): Cmd {
|
||||||
export function expandMacro(ctx: Ctx): Cmd {
|
export function expandMacro(ctx: Ctx): Cmd {
|
||||||
function codeFormat(expanded: ra.ExpandedMacro): string {
|
function codeFormat(expanded: ra.ExpandedMacro): string {
|
||||||
let result = `// Recursive expansion of ${expanded.name}! macro\n`;
|
let result = `// Recursive expansion of ${expanded.name}! macro\n`;
|
||||||
result += '// ' + '='.repeat(result.length - 3);
|
result += "// " + "=".repeat(result.length - 3);
|
||||||
result += '\n\n';
|
result += "\n\n";
|
||||||
result += expanded.expansion;
|
result += expanded.expansion;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tdcp = new class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
|
uri = vscode.Uri.parse("rust-analyzer://expandMacro/[EXPANSION].rs");
|
||||||
eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
|
async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
const client = ctx.client;
|
const client = ctx.client;
|
||||||
if (!editor || !client) return '';
|
if (!editor || !client) return "";
|
||||||
|
|
||||||
const position = editor.selection.active;
|
const position = editor.selection.active;
|
||||||
|
|
||||||
const expanded = await client.sendRequest(ra.expandMacro, {
|
const expanded = await client.sendRequest(ra.expandMacro, {
|
||||||
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
|
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
editor.document
|
||||||
|
),
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (expanded == null) return 'Not available';
|
if (expanded == null) return "Not available";
|
||||||
|
|
||||||
return codeFormat(expanded);
|
return codeFormat(expanded);
|
||||||
}
|
}
|
||||||
|
@ -631,23 +710,14 @@ export function expandMacro(ctx: Ctx): Cmd {
|
||||||
get onDidChange(): vscode.Event<vscode.Uri> {
|
get onDidChange(): vscode.Event<vscode.Uri> {
|
||||||
return this.eventEmitter.event;
|
return this.eventEmitter.event;
|
||||||
}
|
}
|
||||||
}();
|
})();
|
||||||
|
|
||||||
ctx.pushCleanup(
|
ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider("rust-analyzer", tdcp));
|
||||||
vscode.workspace.registerTextDocumentContentProvider(
|
|
||||||
'rust-analyzer',
|
|
||||||
tdcp,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||||
tdcp.eventEmitter.fire(tdcp.uri);
|
tdcp.eventEmitter.fire(tdcp.uri);
|
||||||
return vscode.window.showTextDocument(
|
return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
|
||||||
document,
|
|
||||||
vscode.ViewColumn.Two,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,13 +725,18 @@ export function reloadWorkspace(ctx: Ctx): Cmd {
|
||||||
return async () => ctx.client.sendRequest(ra.reloadWorkspace);
|
return async () => ctx.client.sendRequest(ra.reloadWorkspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showReferencesImpl(client: LanguageClient, uri: string, position: lc.Position, locations: lc.Location[]) {
|
async function showReferencesImpl(
|
||||||
|
client: LanguageClient,
|
||||||
|
uri: string,
|
||||||
|
position: lc.Position,
|
||||||
|
locations: lc.Location[]
|
||||||
|
) {
|
||||||
if (client) {
|
if (client) {
|
||||||
await vscode.commands.executeCommand(
|
await vscode.commands.executeCommand(
|
||||||
'editor.action.showReferences',
|
"editor.action.showReferences",
|
||||||
vscode.Uri.parse(uri),
|
vscode.Uri.parse(uri),
|
||||||
client.protocol2CodeConverter.asPosition(position),
|
client.protocol2CodeConverter.asPosition(position),
|
||||||
locations.map(client.protocol2CodeConverter.asLocation),
|
locations.map(client.protocol2CodeConverter.asLocation)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -677,8 +752,8 @@ export function applyActionGroup(_ctx: Ctx): Cmd {
|
||||||
const selectedAction = await vscode.window.showQuickPick(actions);
|
const selectedAction = await vscode.window.showQuickPick(actions);
|
||||||
if (!selectedAction) return;
|
if (!selectedAction) return;
|
||||||
await vscode.commands.executeCommand(
|
await vscode.commands.executeCommand(
|
||||||
'rust-analyzer.resolveCodeAction',
|
"rust-analyzer.resolveCodeAction",
|
||||||
selectedAction.arguments,
|
selectedAction.arguments
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -699,12 +774,11 @@ export function gotoLocation(ctx: Ctx): Cmd {
|
||||||
|
|
||||||
export function openDocs(ctx: Ctx): Cmd {
|
export function openDocs(ctx: Ctx): Cmd {
|
||||||
return async () => {
|
return async () => {
|
||||||
|
|
||||||
const client = ctx.client;
|
const client = ctx.client;
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
if (!editor || !client) {
|
if (!editor || !client) {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
const position = editor.selection.active;
|
const position = editor.selection.active;
|
||||||
const textDocument = { uri: editor.document.uri.toString() };
|
const textDocument = { uri: editor.document.uri.toString() };
|
||||||
|
@ -715,7 +789,6 @@ export function openDocs(ctx: Ctx): Cmd {
|
||||||
await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink));
|
await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(doclink));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveCodeAction(ctx: Ctx): Cmd {
|
export function resolveCodeAction(ctx: Ctx): Cmd {
|
||||||
|
@ -730,8 +803,13 @@ export function resolveCodeAction(ctx: Ctx): Cmd {
|
||||||
const edit = await client.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
|
const edit = await client.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
|
||||||
// filter out all text edits and recreate the WorkspaceEdit without them so we can apply
|
// filter out all text edits and recreate the WorkspaceEdit without them so we can apply
|
||||||
// snippet edits on our own
|
// snippet edits on our own
|
||||||
const lcFileSystemEdit = { ...itemEdit, documentChanges: itemEdit.documentChanges?.filter(change => "kind" in change) };
|
const lcFileSystemEdit = {
|
||||||
const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit(lcFileSystemEdit);
|
...itemEdit,
|
||||||
|
documentChanges: itemEdit.documentChanges?.filter((change) => "kind" in change),
|
||||||
|
};
|
||||||
|
const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit(
|
||||||
|
lcFileSystemEdit
|
||||||
|
);
|
||||||
await vscode.workspace.applyEdit(fileSystemEdit);
|
await vscode.workspace.applyEdit(fileSystemEdit);
|
||||||
await applySnippetWorkspaceEdit(edit);
|
await applySnippetWorkspaceEdit(edit);
|
||||||
if (item.command != null) {
|
if (item.command != null) {
|
||||||
|
@ -753,7 +831,7 @@ export function run(ctx: Ctx): Cmd {
|
||||||
const item = await selectRunnable(ctx, prevRunnable);
|
const item = await selectRunnable(ctx, prevRunnable);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
item.detail = 'rerun';
|
item.detail = "rerun";
|
||||||
prevRunnable = item;
|
prevRunnable = item;
|
||||||
const task = await createTask(item.runnable, ctx.config);
|
const task = await createTask(item.runnable, ctx.config);
|
||||||
return await vscode.tasks.executeTask(task);
|
return await vscode.tasks.executeTask(task);
|
||||||
|
@ -767,29 +845,33 @@ export function peekTests(ctx: Ctx): Cmd {
|
||||||
const editor = ctx.activeRustEditor;
|
const editor = ctx.activeRustEditor;
|
||||||
if (!editor || !client) return;
|
if (!editor || !client) return;
|
||||||
|
|
||||||
await vscode.window.withProgress({
|
await vscode.window.withProgress(
|
||||||
location: vscode.ProgressLocation.Notification,
|
{
|
||||||
title: "Looking for tests...",
|
location: vscode.ProgressLocation.Notification,
|
||||||
cancellable: false,
|
title: "Looking for tests...",
|
||||||
}, async (_progress, _token) => {
|
cancellable: false,
|
||||||
const uri = editor.document.uri.toString();
|
},
|
||||||
const position = client.code2ProtocolConverter.asPosition(
|
async (_progress, _token) => {
|
||||||
editor.selection.active,
|
const uri = editor.document.uri.toString();
|
||||||
);
|
const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
|
||||||
|
|
||||||
const tests = await client.sendRequest(ra.relatedTests, {
|
const tests = await client.sendRequest(ra.relatedTests, {
|
||||||
textDocument: { uri: uri },
|
textDocument: { uri: uri },
|
||||||
position: position,
|
position: position,
|
||||||
});
|
});
|
||||||
const locations: lc.Location[] = tests.map(it =>
|
const locations: lc.Location[] = tests.map((it) =>
|
||||||
lc.Location.create(it.runnable.location!.targetUri, it.runnable.location!.targetSelectionRange));
|
lc.Location.create(
|
||||||
|
it.runnable.location!.targetUri,
|
||||||
|
it.runnable.location!.targetSelectionRange
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
await showReferencesImpl(client, uri, position, locations);
|
await showReferencesImpl(client, uri, position, locations);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function runSingle(ctx: Ctx): Cmd {
|
export function runSingle(ctx: Ctx): Cmd {
|
||||||
return async (runnable: ra.Runnable) => {
|
return async (runnable: ra.Runnable) => {
|
||||||
const editor = ctx.activeRustEditor;
|
const editor = ctx.activeRustEditor;
|
||||||
|
@ -826,7 +908,7 @@ export function debug(ctx: Ctx): Cmd {
|
||||||
const item = await selectRunnable(ctx, prevDebuggee, true);
|
const item = await selectRunnable(ctx, prevDebuggee, true);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
item.detail = 'restart';
|
item.detail = "restart";
|
||||||
prevDebuggee = item;
|
prevDebuggee = item;
|
||||||
return await startDebugSession(ctx, item.runnable);
|
return await startDebugSession(ctx, item.runnable);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import path = require('path');
|
import path = require("path");
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import { Env } from './client';
|
import { Env } from "./client";
|
||||||
import { log } from "./util";
|
import { log } from "./util";
|
||||||
|
|
||||||
export type UpdatesChannel = "stable" | "nightly";
|
export type UpdatesChannel = "stable" | "nightly";
|
||||||
|
|
||||||
const NIGHTLY_TAG = "nightly";
|
const NIGHTLY_TAG = "nightly";
|
||||||
|
|
||||||
export type RunnableEnvCfg = undefined | Record<string, string> | { mask?: string; env: Record<string, string> }[];
|
export type RunnableEnvCfg =
|
||||||
|
| undefined
|
||||||
|
| Record<string, string>
|
||||||
|
| { mask?: string; env: Record<string, string> }[];
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
readonly extensionId = "rust-lang.rust-analyzer";
|
readonly extensionId = "rust-lang.rust-analyzer";
|
||||||
|
@ -20,8 +23,7 @@ export class Config {
|
||||||
"procMacro",
|
"procMacro",
|
||||||
"files",
|
"files",
|
||||||
"lens", // works as lens.*
|
"lens", // works as lens.*
|
||||||
]
|
].map((opt) => `${this.rootSection}.${opt}`);
|
||||||
.map(opt => `${this.rootSection}.${opt}`);
|
|
||||||
|
|
||||||
readonly package: {
|
readonly package: {
|
||||||
version: string;
|
version: string;
|
||||||
|
@ -33,7 +35,11 @@ export class Config {
|
||||||
|
|
||||||
constructor(ctx: vscode.ExtensionContext) {
|
constructor(ctx: vscode.ExtensionContext) {
|
||||||
this.globalStorageUri = ctx.globalStorageUri;
|
this.globalStorageUri = ctx.globalStorageUri;
|
||||||
vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions);
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
|
this.onDidChangeConfiguration,
|
||||||
|
this,
|
||||||
|
ctx.subscriptions
|
||||||
|
);
|
||||||
this.refreshLogging();
|
this.refreshLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +54,8 @@ export class Config {
|
||||||
private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) {
|
private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) {
|
||||||
this.refreshLogging();
|
this.refreshLogging();
|
||||||
|
|
||||||
const requiresReloadOpt = this.requiresReloadOpts.find(
|
const requiresReloadOpt = this.requiresReloadOpts.find((opt) =>
|
||||||
opt => event.affectsConfiguration(opt)
|
event.affectsConfiguration(opt)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!requiresReloadOpt) return;
|
if (!requiresReloadOpt) return;
|
||||||
|
@ -94,8 +100,12 @@ export class Config {
|
||||||
get serverPath() {
|
get serverPath() {
|
||||||
return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
|
return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
|
||||||
}
|
}
|
||||||
get serverExtraEnv() { return this.get<Env | null>("server.extraEnv") ?? {}; }
|
get serverExtraEnv() {
|
||||||
get traceExtension() { return this.get<boolean>("trace.extension"); }
|
return this.get<Env | null>("server.extraEnv") ?? {};
|
||||||
|
}
|
||||||
|
get traceExtension() {
|
||||||
|
return this.get<boolean>("trace.extension");
|
||||||
|
}
|
||||||
|
|
||||||
get cargoRunner() {
|
get cargoRunner() {
|
||||||
return this.get<string | undefined>("cargoRunner");
|
return this.get<string | undefined>("cargoRunner");
|
||||||
|
@ -109,7 +119,8 @@ export class Config {
|
||||||
let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap");
|
let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap");
|
||||||
if (sourceFileMap !== "auto") {
|
if (sourceFileMap !== "auto") {
|
||||||
// "/rustc/<id>" used by suggestions only.
|
// "/rustc/<id>" used by suggestions only.
|
||||||
const { ["/rustc/<id>"]: _, ...trimmed } = this.get<Record<string, string>>("debug.sourceFileMap");
|
const { ["/rustc/<id>"]: _, ...trimmed } =
|
||||||
|
this.get<Record<string, string>>("debug.sourceFileMap");
|
||||||
sourceFileMap = trimmed;
|
sourceFileMap = trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +128,7 @@ export class Config {
|
||||||
engine: this.get<string>("debug.engine"),
|
engine: this.get<string>("debug.engine"),
|
||||||
engineSettings: this.get<object>("debug.engineSettings"),
|
engineSettings: this.get<object>("debug.engineSettings"),
|
||||||
openDebugPane: this.get<boolean>("debug.openDebugPane"),
|
openDebugPane: this.get<boolean>("debug.openDebugPane"),
|
||||||
sourceFileMap: sourceFileMap
|
sourceFileMap: sourceFileMap,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,57 +150,69 @@ export class Config {
|
||||||
|
|
||||||
export async function updateConfig(config: vscode.WorkspaceConfiguration) {
|
export async function updateConfig(config: vscode.WorkspaceConfiguration) {
|
||||||
const renames = [
|
const renames = [
|
||||||
["assist.allowMergingIntoGlobImports", "imports.merge.glob",],
|
["assist.allowMergingIntoGlobImports", "imports.merge.glob"],
|
||||||
["assist.exprFillDefault", "assist.expressionFillDefault",],
|
["assist.exprFillDefault", "assist.expressionFillDefault"],
|
||||||
["assist.importEnforceGranularity", "imports.granularity.enforce",],
|
["assist.importEnforceGranularity", "imports.granularity.enforce"],
|
||||||
["assist.importGranularity", "imports.granularity.group",],
|
["assist.importGranularity", "imports.granularity.group"],
|
||||||
["assist.importMergeBehavior", "imports.granularity.group",],
|
["assist.importMergeBehavior", "imports.granularity.group"],
|
||||||
["assist.importMergeBehaviour", "imports.granularity.group",],
|
["assist.importMergeBehaviour", "imports.granularity.group"],
|
||||||
["assist.importGroup", "imports.group.enable",],
|
["assist.importGroup", "imports.group.enable"],
|
||||||
["assist.importPrefix", "imports.prefix",],
|
["assist.importPrefix", "imports.prefix"],
|
||||||
["primeCaches.enable", "cachePriming.enable",],
|
["primeCaches.enable", "cachePriming.enable"],
|
||||||
["cache.warmup", "cachePriming.enable",],
|
["cache.warmup", "cachePriming.enable"],
|
||||||
["cargo.loadOutDirsFromCheck", "cargo.buildScripts.enable",],
|
["cargo.loadOutDirsFromCheck", "cargo.buildScripts.enable"],
|
||||||
["cargo.runBuildScripts", "cargo.buildScripts.enable",],
|
["cargo.runBuildScripts", "cargo.buildScripts.enable"],
|
||||||
["cargo.runBuildScriptsCommand", "cargo.buildScripts.overrideCommand",],
|
["cargo.runBuildScriptsCommand", "cargo.buildScripts.overrideCommand"],
|
||||||
["cargo.useRustcWrapperForBuildScripts", "cargo.buildScripts.useRustcWrapper",],
|
["cargo.useRustcWrapperForBuildScripts", "cargo.buildScripts.useRustcWrapper"],
|
||||||
["completion.snippets", "completion.snippets.custom",],
|
["completion.snippets", "completion.snippets.custom"],
|
||||||
["diagnostics.enableExperimental", "diagnostics.experimental.enable",],
|
["diagnostics.enableExperimental", "diagnostics.experimental.enable"],
|
||||||
["experimental.procAttrMacros", "procMacro.attributes.enable",],
|
["experimental.procAttrMacros", "procMacro.attributes.enable"],
|
||||||
["highlighting.strings", "semanticHighlighting.strings.enable",],
|
["highlighting.strings", "semanticHighlighting.strings.enable"],
|
||||||
["highlightRelated.breakPoints", "highlightRelated.breakPoints.enable",],
|
["highlightRelated.breakPoints", "highlightRelated.breakPoints.enable"],
|
||||||
["highlightRelated.exitPoints", "highlightRelated.exitPoints.enable",],
|
["highlightRelated.exitPoints", "highlightRelated.exitPoints.enable"],
|
||||||
["highlightRelated.yieldPoints", "highlightRelated.yieldPoints.enable",],
|
["highlightRelated.yieldPoints", "highlightRelated.yieldPoints.enable"],
|
||||||
["highlightRelated.references", "highlightRelated.references.enable",],
|
["highlightRelated.references", "highlightRelated.references.enable"],
|
||||||
["hover.documentation", "hover.documentation.enable",],
|
["hover.documentation", "hover.documentation.enable"],
|
||||||
["hover.linksInHover", "hover.links.enable",],
|
["hover.linksInHover", "hover.links.enable"],
|
||||||
["hoverActions.linksInHover", "hover.links.enable",],
|
["hoverActions.linksInHover", "hover.links.enable"],
|
||||||
["hoverActions.debug", "hover.actions.debug.enable",],
|
["hoverActions.debug", "hover.actions.debug.enable"],
|
||||||
["hoverActions.enable", "hover.actions.enable.enable",],
|
["hoverActions.enable", "hover.actions.enable.enable"],
|
||||||
["hoverActions.gotoTypeDef", "hover.actions.gotoTypeDef.enable",],
|
["hoverActions.gotoTypeDef", "hover.actions.gotoTypeDef.enable"],
|
||||||
["hoverActions.implementations", "hover.actions.implementations.enable",],
|
["hoverActions.implementations", "hover.actions.implementations.enable"],
|
||||||
["hoverActions.references", "hover.actions.references.enable",],
|
["hoverActions.references", "hover.actions.references.enable"],
|
||||||
["hoverActions.run", "hover.actions.run.enable",],
|
["hoverActions.run", "hover.actions.run.enable"],
|
||||||
["inlayHints.chainingHints", "inlayHints.chainingHints.enable",],
|
["inlayHints.chainingHints", "inlayHints.chainingHints.enable"],
|
||||||
["inlayHints.closureReturnTypeHints", "inlayHints.closureReturnTypeHints.enable",],
|
["inlayHints.closureReturnTypeHints", "inlayHints.closureReturnTypeHints.enable"],
|
||||||
["inlayHints.hideNamedConstructorHints", "inlayHints.typeHints.hideNamedConstructor",],
|
["inlayHints.hideNamedConstructorHints", "inlayHints.typeHints.hideNamedConstructor"],
|
||||||
["inlayHints.parameterHints", "inlayHints.parameterHints.enable",],
|
["inlayHints.parameterHints", "inlayHints.parameterHints.enable"],
|
||||||
["inlayHints.reborrowHints", "inlayHints.reborrowHints.enable",],
|
["inlayHints.reborrowHints", "inlayHints.reborrowHints.enable"],
|
||||||
["inlayHints.typeHints", "inlayHints.typeHints.enable",],
|
["inlayHints.typeHints", "inlayHints.typeHints.enable"],
|
||||||
["lruCapacity", "lru.capacity",],
|
["lruCapacity", "lru.capacity"],
|
||||||
["runnables.cargoExtraArgs", "runnables.extraArgs",],
|
["runnables.cargoExtraArgs", "runnables.extraArgs"],
|
||||||
["runnables.overrideCargo", "runnables.command",],
|
["runnables.overrideCargo", "runnables.command"],
|
||||||
["rustcSource", "rustc.source",],
|
["rustcSource", "rustc.source"],
|
||||||
["rustfmt.enableRangeFormatting", "rustfmt.rangeFormatting.enable"]
|
["rustfmt.enableRangeFormatting", "rustfmt.rangeFormatting.enable"],
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const [oldKey, newKey] of renames) {
|
for (const [oldKey, newKey] of renames) {
|
||||||
const inspect = config.inspect(oldKey);
|
const inspect = config.inspect(oldKey);
|
||||||
if (inspect !== undefined) {
|
if (inspect !== undefined) {
|
||||||
const valMatrix = [
|
const valMatrix = [
|
||||||
{ val: inspect.globalValue, langVal: inspect.globalLanguageValue, target: vscode.ConfigurationTarget.Global },
|
{
|
||||||
{ val: inspect.workspaceFolderValue, langVal: inspect.workspaceFolderLanguageValue, target: vscode.ConfigurationTarget.WorkspaceFolder },
|
val: inspect.globalValue,
|
||||||
{ val: inspect.workspaceValue, langVal: inspect.workspaceLanguageValue, target: vscode.ConfigurationTarget.Workspace }
|
langVal: inspect.globalLanguageValue,
|
||||||
|
target: vscode.ConfigurationTarget.Global,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: inspect.workspaceFolderValue,
|
||||||
|
langVal: inspect.workspaceFolderLanguageValue,
|
||||||
|
target: vscode.ConfigurationTarget.WorkspaceFolder,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val: inspect.workspaceValue,
|
||||||
|
langVal: inspect.workspaceLanguageValue,
|
||||||
|
target: vscode.ConfigurationTarget.Workspace,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
for (const { val, langVal, target } of valMatrix) {
|
for (const { val, langVal, target } of valMatrix) {
|
||||||
const pred = (val: unknown) => {
|
const pred = (val: unknown) => {
|
||||||
|
@ -197,7 +220,14 @@ export async function updateConfig(config: vscode.WorkspaceConfiguration) {
|
||||||
// that means on the next run we would find these again, but as objects with
|
// that means on the next run we would find these again, but as objects with
|
||||||
// these properties causing us to destroy the config
|
// these properties causing us to destroy the config
|
||||||
// so filter those already updated ones out
|
// so filter those already updated ones out
|
||||||
return val !== undefined && !(typeof val === "object" && val !== null && (val.hasOwnProperty("enable") || val.hasOwnProperty("custom")));
|
return (
|
||||||
|
val !== undefined &&
|
||||||
|
!(
|
||||||
|
typeof val === "object" &&
|
||||||
|
val !== null &&
|
||||||
|
(val.hasOwnProperty("enable") || val.hasOwnProperty("custom"))
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
if (pred(val)) {
|
if (pred(val)) {
|
||||||
await config.update(newKey, val, target, false);
|
await config.update(newKey, val, target, false);
|
||||||
|
@ -216,48 +246,50 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||||
const missingDeps = new Set<string>();
|
const missingDeps = new Set<string>();
|
||||||
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
|
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
|
||||||
// to follow the same convention for our dependency tracking
|
// to follow the same convention for our dependency tracking
|
||||||
const definedEnvKeys = new Set(Object.keys(env).map(key => `env:${key}`));
|
const definedEnvKeys = new Set(Object.keys(env).map((key) => `env:${key}`));
|
||||||
const envWithDeps = Object.fromEntries(Object.entries(env).map(([key, value]) => {
|
const envWithDeps = Object.fromEntries(
|
||||||
const deps = new Set<string>();
|
Object.entries(env).map(([key, value]) => {
|
||||||
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
|
const deps = new Set<string>();
|
||||||
let match = undefined;
|
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
|
||||||
while ((match = depRe.exec(value))) {
|
let match = undefined;
|
||||||
const depName = match.groups!.depName;
|
while ((match = depRe.exec(value))) {
|
||||||
deps.add(depName);
|
const depName = match.groups!.depName;
|
||||||
// `depName` at this point can have a form of `expression` or
|
deps.add(depName);
|
||||||
// `prefix:expression`
|
// `depName` at this point can have a form of `expression` or
|
||||||
if (!definedEnvKeys.has(depName)) {
|
// `prefix:expression`
|
||||||
missingDeps.add(depName);
|
if (!definedEnvKeys.has(depName)) {
|
||||||
|
missingDeps.add(depName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return [`env:${key}`, { deps: [...deps], value }];
|
||||||
return [`env:${key}`, { deps: [...deps], value }];
|
})
|
||||||
}));
|
);
|
||||||
|
|
||||||
const resolved = new Set<string>();
|
const resolved = new Set<string>();
|
||||||
for (const dep of missingDeps) {
|
for (const dep of missingDeps) {
|
||||||
const match = /(?<prefix>.*?):(?<body>.+)/.exec(dep);
|
const match = /(?<prefix>.*?):(?<body>.+)/.exec(dep);
|
||||||
if (match) {
|
if (match) {
|
||||||
const { prefix, body } = match.groups!;
|
const { prefix, body } = match.groups!;
|
||||||
if (prefix === 'env') {
|
if (prefix === "env") {
|
||||||
const envName = body;
|
const envName = body;
|
||||||
envWithDeps[dep] = {
|
envWithDeps[dep] = {
|
||||||
value: process.env[envName] ?? '',
|
value: process.env[envName] ?? "",
|
||||||
deps: []
|
deps: [],
|
||||||
};
|
};
|
||||||
resolved.add(dep);
|
resolved.add(dep);
|
||||||
} else {
|
} else {
|
||||||
// we can't handle other prefixes at the moment
|
// we can't handle other prefixes at the moment
|
||||||
// leave values as is, but still mark them as resolved
|
// leave values as is, but still mark them as resolved
|
||||||
envWithDeps[dep] = {
|
envWithDeps[dep] = {
|
||||||
value: '${' + dep + '}',
|
value: "${" + dep + "}",
|
||||||
deps: []
|
deps: [],
|
||||||
};
|
};
|
||||||
resolved.add(dep);
|
resolved.add(dep);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
envWithDeps[dep] = {
|
envWithDeps[dep] = {
|
||||||
value: computeVscodeVar(dep),
|
value: computeVscodeVar(dep),
|
||||||
deps: []
|
deps: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,11 +299,13 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||||
do {
|
do {
|
||||||
leftToResolveSize = toResolve.size;
|
leftToResolveSize = toResolve.size;
|
||||||
for (const key of toResolve) {
|
for (const key of toResolve) {
|
||||||
if (envWithDeps[key].deps.every(dep => resolved.has(dep))) {
|
if (envWithDeps[key].deps.every((dep) => resolved.has(dep))) {
|
||||||
envWithDeps[key].value = envWithDeps[key].value.replace(
|
envWithDeps[key].value = envWithDeps[key].value.replace(
|
||||||
/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
|
/\${(?<depName>.+?)}/g,
|
||||||
|
(_wholeMatch, depName) => {
|
||||||
return envWithDeps[depName].value;
|
return envWithDeps[depName].value;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
resolved.add(key);
|
resolved.add(key);
|
||||||
toResolve.delete(key);
|
toResolve.delete(key);
|
||||||
}
|
}
|
||||||
|
@ -302,16 +336,16 @@ function computeVscodeVar(varName: string): string {
|
||||||
return folders[0].uri.fsPath;
|
return folders[0].uri.fsPath;
|
||||||
} else {
|
} else {
|
||||||
// no workspace opened
|
// no workspace opened
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
workspaceFolderBasename: () => {
|
workspaceFolderBasename: () => {
|
||||||
const workspaceFolder = computeVscodeVar('workspaceFolder');
|
const workspaceFolder = computeVscodeVar("workspaceFolder");
|
||||||
if (workspaceFolder) {
|
if (workspaceFolder) {
|
||||||
return path.basename(workspaceFolder);
|
return path.basename(workspaceFolder);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -323,13 +357,13 @@ function computeVscodeVar(varName: string): string {
|
||||||
// https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56
|
// https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56
|
||||||
execPath: () => process.env.VSCODE_EXEC_PATH ?? process.execPath,
|
execPath: () => process.env.VSCODE_EXEC_PATH ?? process.execPath,
|
||||||
|
|
||||||
pathSeparator: () => path.sep
|
pathSeparator: () => path.sep,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (varName in supportedVariables) {
|
if (varName in supportedVariables) {
|
||||||
return supportedVariables[varName]();
|
return supportedVariables[varName]();
|
||||||
} else {
|
} else {
|
||||||
// can't resolve, keep the expression as is
|
// can't resolve, keep the expression as is
|
||||||
return '${' + varName + '}';
|
return "${" + varName + "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as lc from 'vscode-languageclient/node';
|
import * as lc from "vscode-languageclient/node";
|
||||||
import * as ra from './lsp_ext';
|
import * as ra from "./lsp_ext";
|
||||||
|
|
||||||
import { Config } from './config';
|
import { Config } from "./config";
|
||||||
import { createClient } from './client';
|
import { createClient } from "./client";
|
||||||
import { isRustEditor, RustEditor } from './util';
|
import { isRustEditor, RustEditor } from "./util";
|
||||||
import { ServerStatusParams } from './lsp_ext';
|
import { ServerStatusParams } from "./lsp_ext";
|
||||||
|
|
||||||
export type Workspace =
|
export type Workspace =
|
||||||
{
|
|
||||||
kind: 'Workspace Folder';
|
|
||||||
}
|
|
||||||
| {
|
| {
|
||||||
kind: 'Detached Files';
|
kind: "Workspace Folder";
|
||||||
files: vscode.TextDocument[];
|
}
|
||||||
};
|
| {
|
||||||
|
kind: "Detached Files";
|
||||||
|
files: vscode.TextDocument[];
|
||||||
|
};
|
||||||
|
|
||||||
export class Ctx {
|
export class Ctx {
|
||||||
private constructor(
|
private constructor(
|
||||||
|
@ -22,16 +22,14 @@ export class Ctx {
|
||||||
private readonly extCtx: vscode.ExtensionContext,
|
private readonly extCtx: vscode.ExtensionContext,
|
||||||
readonly client: lc.LanguageClient,
|
readonly client: lc.LanguageClient,
|
||||||
readonly serverPath: string,
|
readonly serverPath: string,
|
||||||
readonly statusBar: vscode.StatusBarItem,
|
readonly statusBar: vscode.StatusBarItem
|
||||||
) {
|
) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static async create(
|
static async create(
|
||||||
config: Config,
|
config: Config,
|
||||||
extCtx: vscode.ExtensionContext,
|
extCtx: vscode.ExtensionContext,
|
||||||
serverPath: string,
|
serverPath: string,
|
||||||
workspace: Workspace,
|
workspace: Workspace
|
||||||
): Promise<Ctx> {
|
): Promise<Ctx> {
|
||||||
const client = await createClient(serverPath, workspace, config.serverExtraEnv);
|
const client = await createClient(serverPath, workspace, config.serverExtraEnv);
|
||||||
|
|
||||||
|
@ -52,9 +50,7 @@ export class Ctx {
|
||||||
|
|
||||||
get activeRustEditor(): RustEditor | undefined {
|
get activeRustEditor(): RustEditor | undefined {
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
return editor && isRustEditor(editor)
|
return editor && isRustEditor(editor) ? editor : undefined;
|
||||||
? editor
|
|
||||||
: undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get visibleRustEditors(): RustEditor[] {
|
get visibleRustEditors(): RustEditor[] {
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
import * as ra from './lsp_ext';
|
import * as ra from "./lsp_ext";
|
||||||
|
|
||||||
import { Cargo, getRustcId, getSysroot } from './toolchain';
|
import { Cargo, getRustcId, getSysroot } from "./toolchain";
|
||||||
import { Ctx } from "./ctx";
|
import { Ctx } from "./ctx";
|
||||||
import { prepareEnv } from "./run";
|
import { prepareEnv } from "./run";
|
||||||
|
|
||||||
const debugOutput = vscode.window.createOutputChannel("Debug");
|
const debugOutput = vscode.window.createOutputChannel("Debug");
|
||||||
type DebugConfigProvider = (config: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration;
|
type DebugConfigProvider = (
|
||||||
|
config: ra.Runnable,
|
||||||
|
executable: string,
|
||||||
|
env: Record<string, string>,
|
||||||
|
sourceFileMap?: Record<string, string>
|
||||||
|
) => vscode.DebugConfiguration;
|
||||||
|
|
||||||
export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> {
|
export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> {
|
||||||
const scope = ctx.activeRustEditor?.document.uri;
|
const scope = ctx.activeRustEditor?.document.uri;
|
||||||
|
@ -20,9 +25,13 @@ export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<
|
||||||
const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
|
const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
|
||||||
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
|
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
|
||||||
|
|
||||||
const index = configurations.findIndex(c => c.name === debugConfig.name);
|
const index = configurations.findIndex((c) => c.name === debugConfig.name);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
|
const answer = await vscode.window.showErrorMessage(
|
||||||
|
`Launch configuration '${debugConfig.name}' already exists!`,
|
||||||
|
"Cancel",
|
||||||
|
"Update"
|
||||||
|
);
|
||||||
if (answer === "Cancel") return;
|
if (answer === "Cancel") return;
|
||||||
|
|
||||||
configurations[index] = debugConfig;
|
configurations[index] = debugConfig;
|
||||||
|
@ -40,7 +49,7 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
|
||||||
const wsLaunchSection = vscode.workspace.getConfiguration("launch");
|
const wsLaunchSection = vscode.workspace.getConfiguration("launch");
|
||||||
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
|
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
|
||||||
|
|
||||||
const index = configurations.findIndex(c => c.name === runnable.label);
|
const index = configurations.findIndex((c) => c.name === runnable.label);
|
||||||
if (-1 !== index) {
|
if (-1 !== index) {
|
||||||
debugConfig = configurations[index];
|
debugConfig = configurations[index];
|
||||||
message = " (from launch.json)";
|
message = " (from launch.json)";
|
||||||
|
@ -56,13 +65,16 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
|
||||||
return vscode.debug.startDebugging(undefined, debugConfig);
|
return vscode.debug.startDebugging(undefined, debugConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> {
|
async function getDebugConfiguration(
|
||||||
|
ctx: Ctx,
|
||||||
|
runnable: ra.Runnable
|
||||||
|
): Promise<vscode.DebugConfiguration | undefined> {
|
||||||
const editor = ctx.activeRustEditor;
|
const editor = ctx.activeRustEditor;
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
|
|
||||||
const knownEngines: Record<string, DebugConfigProvider> = {
|
const knownEngines: Record<string, DebugConfigProvider> = {
|
||||||
"vadimcn.vscode-lldb": getLldbDebugConfig,
|
"vadimcn.vscode-lldb": getLldbDebugConfig,
|
||||||
"ms-vscode.cpptools": getCppvsDebugConfig
|
"ms-vscode.cpptools": getCppvsDebugConfig,
|
||||||
};
|
};
|
||||||
const debugOptions = ctx.config.debug;
|
const debugOptions = ctx.config.debug;
|
||||||
|
|
||||||
|
@ -77,8 +89,10 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!debugEngine) {
|
if (!debugEngine) {
|
||||||
await vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)`
|
await vscode.window.showErrorMessage(
|
||||||
+ ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`);
|
`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` +
|
||||||
|
` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,15 +105,17 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||||
const workspaceFolders = vscode.workspace.workspaceFolders!;
|
const workspaceFolders = vscode.workspace.workspaceFolders!;
|
||||||
const isMultiFolderWorkspace = workspaceFolders.length > 1;
|
const isMultiFolderWorkspace = workspaceFolders.length > 1;
|
||||||
const firstWorkspace = workspaceFolders[0];
|
const firstWorkspace = workspaceFolders[0];
|
||||||
const workspace = !isMultiFolderWorkspace || !runnable.args.workspaceRoot ?
|
const workspace =
|
||||||
firstWorkspace :
|
!isMultiFolderWorkspace || !runnable.args.workspaceRoot
|
||||||
workspaceFolders.find(w => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) || firstWorkspace;
|
? firstWorkspace
|
||||||
|
: workspaceFolders.find((w) => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) ||
|
||||||
|
firstWorkspace;
|
||||||
|
|
||||||
const wsFolder = path.normalize(workspace.uri.fsPath);
|
const wsFolder = path.normalize(workspace.uri.fsPath);
|
||||||
const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : '';
|
const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : "";
|
||||||
function simplifyPath(p: string): string {
|
function simplifyPath(p: string): string {
|
||||||
// see https://github.com/rust-analyzer/rust-analyzer/pull/5513#issuecomment-663458818 for why this is needed
|
// see https://github.com/rust-analyzer/rust-analyzer/pull/5513#issuecomment-663458818 for why this is needed
|
||||||
return path.normalize(p).replace(wsFolder, '${workspaceFolder' + workspaceQualifier + '}');
|
return path.normalize(p).replace(wsFolder, "${workspaceFolder" + workspaceQualifier + "}");
|
||||||
}
|
}
|
||||||
|
|
||||||
const executable = await getDebugExecutable(runnable);
|
const executable = await getDebugExecutable(runnable);
|
||||||
|
@ -114,7 +130,12 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||||
sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
|
sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
|
||||||
}
|
}
|
||||||
|
|
||||||
const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, sourceFileMap);
|
const debugConfig = knownEngines[debugEngine.id](
|
||||||
|
runnable,
|
||||||
|
simplifyPath(executable),
|
||||||
|
env,
|
||||||
|
sourceFileMap
|
||||||
|
);
|
||||||
if (debugConfig.type in debugOptions.engineSettings) {
|
if (debugConfig.type in debugOptions.engineSettings) {
|
||||||
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
|
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
|
||||||
for (var key in settingsMap) {
|
for (var key in settingsMap) {
|
||||||
|
@ -136,14 +157,19 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
|
async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
|
||||||
const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput);
|
const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput);
|
||||||
const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
|
const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
|
||||||
|
|
||||||
// if we are here, there were no compilation errors.
|
// if we are here, there were no compilation errors.
|
||||||
return executable;
|
return executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLldbDebugConfig(runnable: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
|
function getLldbDebugConfig(
|
||||||
|
runnable: ra.Runnable,
|
||||||
|
executable: string,
|
||||||
|
env: Record<string, string>,
|
||||||
|
sourceFileMap?: Record<string, string>
|
||||||
|
): vscode.DebugConfiguration {
|
||||||
return {
|
return {
|
||||||
type: "lldb",
|
type: "lldb",
|
||||||
request: "launch",
|
request: "launch",
|
||||||
|
@ -153,13 +179,18 @@ function getLldbDebugConfig(runnable: ra.Runnable, executable: string, env: Reco
|
||||||
cwd: runnable.args.workspaceRoot,
|
cwd: runnable.args.workspaceRoot,
|
||||||
sourceMap: sourceFileMap,
|
sourceMap: sourceFileMap,
|
||||||
sourceLanguages: ["rust"],
|
sourceLanguages: ["rust"],
|
||||||
env
|
env,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, env: Record<string, string>, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
|
function getCppvsDebugConfig(
|
||||||
|
runnable: ra.Runnable,
|
||||||
|
executable: string,
|
||||||
|
env: Record<string, string>,
|
||||||
|
sourceFileMap?: Record<string, string>
|
||||||
|
): vscode.DebugConfiguration {
|
||||||
return {
|
return {
|
||||||
type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg",
|
type: os.platform() === "win32" ? "cppvsdbg" : "cppdbg",
|
||||||
request: "launch",
|
request: "launch",
|
||||||
name: runnable.label,
|
name: runnable.label,
|
||||||
program: executable,
|
program: executable,
|
||||||
|
|
|
@ -7,7 +7,9 @@ import * as lc from "vscode-languageclient";
|
||||||
export interface AnalyzerStatusParams {
|
export interface AnalyzerStatusParams {
|
||||||
textDocument?: lc.TextDocumentIdentifier;
|
textDocument?: lc.TextDocumentIdentifier;
|
||||||
}
|
}
|
||||||
export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus");
|
export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>(
|
||||||
|
"rust-analyzer/analyzerStatus"
|
||||||
|
);
|
||||||
export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
|
export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
|
||||||
export const shuffleCrateGraph = new lc.RequestType0<null, void>("rust-analyzer/shuffleCrateGraph");
|
export const shuffleCrateGraph = new lc.RequestType0<null, void>("rust-analyzer/shuffleCrateGraph");
|
||||||
|
|
||||||
|
@ -16,7 +18,9 @@ export interface ServerStatusParams {
|
||||||
quiescent: boolean;
|
quiescent: boolean;
|
||||||
message?: string;
|
message?: string;
|
||||||
}
|
}
|
||||||
export const serverStatus = new lc.NotificationType<ServerStatusParams>("experimental/serverStatus");
|
export const serverStatus = new lc.NotificationType<ServerStatusParams>(
|
||||||
|
"experimental/serverStatus"
|
||||||
|
);
|
||||||
|
|
||||||
export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
|
export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
|
||||||
|
|
||||||
|
@ -31,23 +35,33 @@ export interface SyntaxTreeParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
range: lc.Range | null;
|
range: lc.Range | null;
|
||||||
}
|
}
|
||||||
export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree");
|
export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>(
|
||||||
|
"rust-analyzer/syntaxTree"
|
||||||
|
);
|
||||||
|
|
||||||
export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir");
|
export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
|
||||||
|
"rust-analyzer/viewHir"
|
||||||
|
);
|
||||||
|
|
||||||
export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string, void>("rust-analyzer/viewFileText");
|
export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string, void>(
|
||||||
|
"rust-analyzer/viewFileText"
|
||||||
|
);
|
||||||
|
|
||||||
export interface ViewItemTreeParams {
|
export interface ViewItemTreeParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>("rust-analyzer/viewItemTree");
|
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
|
||||||
|
"rust-analyzer/viewItemTree"
|
||||||
|
);
|
||||||
|
|
||||||
export interface ViewCrateGraphParams {
|
export interface ViewCrateGraphParams {
|
||||||
full: boolean;
|
full: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>("rust-analyzer/viewCrateGraph");
|
export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>(
|
||||||
|
"rust-analyzer/viewCrateGraph"
|
||||||
|
);
|
||||||
|
|
||||||
export interface ExpandMacroParams {
|
export interface ExpandMacroParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
|
@ -57,23 +71,35 @@ export interface ExpandedMacro {
|
||||||
name: string;
|
name: string;
|
||||||
expansion: string;
|
expansion: string;
|
||||||
}
|
}
|
||||||
export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro | null, void>("rust-analyzer/expandMacro");
|
export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro | null, void>(
|
||||||
|
"rust-analyzer/expandMacro"
|
||||||
|
);
|
||||||
|
|
||||||
export interface MatchingBraceParams {
|
export interface MatchingBraceParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
positions: lc.Position[];
|
positions: lc.Position[];
|
||||||
}
|
}
|
||||||
export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>("experimental/matchingBrace");
|
export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>(
|
||||||
|
"experimental/matchingBrace"
|
||||||
|
);
|
||||||
|
|
||||||
export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[] | null, void>("experimental/parentModule");
|
export const parentModule = new lc.RequestType<
|
||||||
|
lc.TextDocumentPositionParams,
|
||||||
|
lc.LocationLink[] | null,
|
||||||
|
void
|
||||||
|
>("experimental/parentModule");
|
||||||
|
|
||||||
export interface JoinLinesParams {
|
export interface JoinLinesParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
ranges: lc.Range[];
|
ranges: lc.Range[];
|
||||||
}
|
}
|
||||||
export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], void>("experimental/joinLines");
|
export const joinLines = new lc.RequestType<JoinLinesParams, lc.TextEdit[], void>(
|
||||||
|
"experimental/joinLines"
|
||||||
|
);
|
||||||
|
|
||||||
export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>("experimental/onEnter");
|
export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>(
|
||||||
|
"experimental/onEnter"
|
||||||
|
);
|
||||||
|
|
||||||
export interface RunnablesParams {
|
export interface RunnablesParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
|
@ -93,13 +119,17 @@ export interface Runnable {
|
||||||
overrideCargo?: string;
|
overrideCargo?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables");
|
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>(
|
||||||
|
"experimental/runnables"
|
||||||
|
);
|
||||||
|
|
||||||
export interface TestInfo {
|
export interface TestInfo {
|
||||||
runnable: Runnable;
|
runnable: Runnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>("rust-analyzer/relatedTests");
|
export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>(
|
||||||
|
"rust-analyzer/relatedTests"
|
||||||
|
);
|
||||||
|
|
||||||
export interface SsrParams {
|
export interface SsrParams {
|
||||||
query: string;
|
query: string;
|
||||||
|
@ -108,7 +138,7 @@ export interface SsrParams {
|
||||||
position: lc.Position;
|
position: lc.Position;
|
||||||
selections: readonly lc.Range[];
|
selections: readonly lc.Range[];
|
||||||
}
|
}
|
||||||
export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
|
export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr");
|
||||||
|
|
||||||
export interface CommandLink extends lc.Command {
|
export interface CommandLink extends lc.Command {
|
||||||
/**
|
/**
|
||||||
|
@ -122,15 +152,21 @@ export interface CommandLinkGroup {
|
||||||
commands: CommandLink[];
|
commands: CommandLink[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs');
|
export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>(
|
||||||
|
"experimental/externalDocs"
|
||||||
|
);
|
||||||
|
|
||||||
export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>("experimental/openCargoToml");
|
export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>(
|
||||||
|
"experimental/openCargoToml"
|
||||||
|
);
|
||||||
|
|
||||||
export interface OpenCargoTomlParams {
|
export interface OpenCargoTomlParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>("experimental/moveItem");
|
export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>(
|
||||||
|
"experimental/moveItem"
|
||||||
|
);
|
||||||
|
|
||||||
export interface MoveItemParams {
|
export interface MoveItemParams {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
|
@ -140,5 +176,5 @@ export interface MoveItemParams {
|
||||||
|
|
||||||
export const enum Direction {
|
export const enum Direction {
|
||||||
Up = "Up",
|
Up = "Up",
|
||||||
Down = "Down"
|
Down = "Down",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as lc from 'vscode-languageclient/node';
|
import * as lc from "vscode-languageclient/node";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
|
|
||||||
import * as commands from './commands';
|
import * as commands from "./commands";
|
||||||
import { Ctx } from './ctx';
|
import { Ctx } from "./ctx";
|
||||||
import { Config } from './config';
|
import { Config } from "./config";
|
||||||
import { log, isValidExecutable, isRustDocument } from './util';
|
import { log, isValidExecutable, isRustDocument } from "./util";
|
||||||
import { PersistentState } from './persistent_state';
|
import { PersistentState } from "./persistent_state";
|
||||||
import { activateTaskProvider } from './tasks';
|
import { activateTaskProvider } from "./tasks";
|
||||||
import { setContextValue } from './util';
|
import { setContextValue } from "./util";
|
||||||
import { exec } from 'child_process';
|
import { exec } from "child_process";
|
||||||
|
|
||||||
let ctx: Ctx | undefined;
|
let ctx: Ctx | undefined;
|
||||||
|
|
||||||
|
@ -19,10 +19,12 @@ export interface RustAnalyzerExtensionApi {
|
||||||
client: lc.LanguageClient;
|
client: lc.LanguageClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
|
export async function activate(
|
||||||
|
context: vscode.ExtensionContext
|
||||||
|
): Promise<RustAnalyzerExtensionApi> {
|
||||||
// VS Code doesn't show a notification when an extension fails to activate
|
// VS Code doesn't show a notification when an extension fails to activate
|
||||||
// so we do it ourselves.
|
// so we do it ourselves.
|
||||||
return await tryActivate(context).catch(err => {
|
return await tryActivate(context).catch((err) => {
|
||||||
void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
|
void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
@ -31,7 +33,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<RustAn
|
||||||
async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
|
async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
|
||||||
const config = new Config(context);
|
const config = new Config(context);
|
||||||
const state = new PersistentState(context.globalState);
|
const state = new PersistentState(context.globalState);
|
||||||
const serverPath = await bootstrap(context, config, state).catch(err => {
|
const serverPath = await bootstrap(context, config, state).catch((err) => {
|
||||||
let message = "bootstrap error. ";
|
let message = "bootstrap error. ";
|
||||||
|
|
||||||
message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
|
message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
|
||||||
|
@ -42,9 +44,14 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((vscode.workspace.workspaceFolders || []).length === 0) {
|
if ((vscode.workspace.workspaceFolders || []).length === 0) {
|
||||||
const rustDocuments = vscode.workspace.textDocuments.filter(document => isRustDocument(document));
|
const rustDocuments = vscode.workspace.textDocuments.filter((document) =>
|
||||||
|
isRustDocument(document)
|
||||||
|
);
|
||||||
if (rustDocuments.length > 0) {
|
if (rustDocuments.length > 0) {
|
||||||
ctx = await Ctx.create(config, context, serverPath, { kind: 'Detached Files', files: rustDocuments });
|
ctx = await Ctx.create(config, context, serverPath, {
|
||||||
|
kind: "Detached Files",
|
||||||
|
files: rustDocuments,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error("no rust files are opened");
|
throw new Error("no rust files are opened");
|
||||||
}
|
}
|
||||||
|
@ -63,13 +70,16 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
|
||||||
ctx.pushCleanup(configureLanguage());
|
ctx.pushCleanup(configureLanguage());
|
||||||
|
|
||||||
vscode.workspace.onDidChangeConfiguration(
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
_ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }).catch(log.error),
|
(_) =>
|
||||||
|
ctx?.client
|
||||||
|
?.sendNotification("workspace/didChangeConfiguration", { settings: "" })
|
||||||
|
.catch(log.error),
|
||||||
null,
|
null,
|
||||||
ctx.subscriptions,
|
ctx.subscriptions
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
client: ctx.client
|
client: ctx.client,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,9 +98,8 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
|
||||||
// "rust-analyzer is not available"
|
// "rust-analyzer is not available"
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
const defaultOnEnter = vscode.commands.registerCommand(
|
const defaultOnEnter = vscode.commands.registerCommand("rust-analyzer.onEnter", () =>
|
||||||
'rust-analyzer.onEnter',
|
vscode.commands.executeCommand("default:type", { text: "\n" })
|
||||||
() => vscode.commands.executeCommand('default:type', { text: '\n' }),
|
|
||||||
);
|
);
|
||||||
context.subscriptions.push(defaultOnEnter);
|
context.subscriptions.push(defaultOnEnter);
|
||||||
|
|
||||||
|
@ -99,8 +108,8 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
|
||||||
// Commands which invokes manually via command palette, shortcut, etc.
|
// Commands which invokes manually via command palette, shortcut, etc.
|
||||||
|
|
||||||
// Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895
|
// Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895
|
||||||
ctx.registerCommand('reload', _ => async () => {
|
ctx.registerCommand("reload", (_) => async () => {
|
||||||
void vscode.window.showInformationMessage('Reloading rust-analyzer...');
|
void vscode.window.showInformationMessage("Reloading rust-analyzer...");
|
||||||
await deactivate();
|
await deactivate();
|
||||||
while (context.subscriptions.length > 0) {
|
while (context.subscriptions.length > 0) {
|
||||||
try {
|
try {
|
||||||
|
@ -112,45 +121,45 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
|
||||||
await activate(context).catch(log.error);
|
await activate(context).catch(log.error);
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
|
ctx.registerCommand("analyzerStatus", commands.analyzerStatus);
|
||||||
ctx.registerCommand('memoryUsage', commands.memoryUsage);
|
ctx.registerCommand("memoryUsage", commands.memoryUsage);
|
||||||
ctx.registerCommand('shuffleCrateGraph', commands.shuffleCrateGraph);
|
ctx.registerCommand("shuffleCrateGraph", commands.shuffleCrateGraph);
|
||||||
ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace);
|
ctx.registerCommand("reloadWorkspace", commands.reloadWorkspace);
|
||||||
ctx.registerCommand('matchingBrace', commands.matchingBrace);
|
ctx.registerCommand("matchingBrace", commands.matchingBrace);
|
||||||
ctx.registerCommand('joinLines', commands.joinLines);
|
ctx.registerCommand("joinLines", commands.joinLines);
|
||||||
ctx.registerCommand('parentModule', commands.parentModule);
|
ctx.registerCommand("parentModule", commands.parentModule);
|
||||||
ctx.registerCommand('syntaxTree', commands.syntaxTree);
|
ctx.registerCommand("syntaxTree", commands.syntaxTree);
|
||||||
ctx.registerCommand('viewHir', commands.viewHir);
|
ctx.registerCommand("viewHir", commands.viewHir);
|
||||||
ctx.registerCommand('viewFileText', commands.viewFileText);
|
ctx.registerCommand("viewFileText", commands.viewFileText);
|
||||||
ctx.registerCommand('viewItemTree', commands.viewItemTree);
|
ctx.registerCommand("viewItemTree", commands.viewItemTree);
|
||||||
ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph);
|
ctx.registerCommand("viewCrateGraph", commands.viewCrateGraph);
|
||||||
ctx.registerCommand('viewFullCrateGraph', commands.viewFullCrateGraph);
|
ctx.registerCommand("viewFullCrateGraph", commands.viewFullCrateGraph);
|
||||||
ctx.registerCommand('expandMacro', commands.expandMacro);
|
ctx.registerCommand("expandMacro", commands.expandMacro);
|
||||||
ctx.registerCommand('run', commands.run);
|
ctx.registerCommand("run", commands.run);
|
||||||
ctx.registerCommand('copyRunCommandLine', commands.copyRunCommandLine);
|
ctx.registerCommand("copyRunCommandLine", commands.copyRunCommandLine);
|
||||||
ctx.registerCommand('debug', commands.debug);
|
ctx.registerCommand("debug", commands.debug);
|
||||||
ctx.registerCommand('newDebugConfig', commands.newDebugConfig);
|
ctx.registerCommand("newDebugConfig", commands.newDebugConfig);
|
||||||
ctx.registerCommand('openDocs', commands.openDocs);
|
ctx.registerCommand("openDocs", commands.openDocs);
|
||||||
ctx.registerCommand('openCargoToml', commands.openCargoToml);
|
ctx.registerCommand("openCargoToml", commands.openCargoToml);
|
||||||
ctx.registerCommand('peekTests', commands.peekTests);
|
ctx.registerCommand("peekTests", commands.peekTests);
|
||||||
ctx.registerCommand('moveItemUp', commands.moveItemUp);
|
ctx.registerCommand("moveItemUp", commands.moveItemUp);
|
||||||
ctx.registerCommand('moveItemDown', commands.moveItemDown);
|
ctx.registerCommand("moveItemDown", commands.moveItemDown);
|
||||||
|
|
||||||
defaultOnEnter.dispose();
|
defaultOnEnter.dispose();
|
||||||
ctx.registerCommand('onEnter', commands.onEnter);
|
ctx.registerCommand("onEnter", commands.onEnter);
|
||||||
|
|
||||||
ctx.registerCommand('ssr', commands.ssr);
|
ctx.registerCommand("ssr", commands.ssr);
|
||||||
ctx.registerCommand('serverVersion', commands.serverVersion);
|
ctx.registerCommand("serverVersion", commands.serverVersion);
|
||||||
ctx.registerCommand('toggleInlayHints', commands.toggleInlayHints);
|
ctx.registerCommand("toggleInlayHints", commands.toggleInlayHints);
|
||||||
|
|
||||||
// Internal commands which are invoked by the server.
|
// Internal commands which are invoked by the server.
|
||||||
ctx.registerCommand('runSingle', commands.runSingle);
|
ctx.registerCommand("runSingle", commands.runSingle);
|
||||||
ctx.registerCommand('debugSingle', commands.debugSingle);
|
ctx.registerCommand("debugSingle", commands.debugSingle);
|
||||||
ctx.registerCommand('showReferences', commands.showReferences);
|
ctx.registerCommand("showReferences", commands.showReferences);
|
||||||
ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
|
ctx.registerCommand("applySnippetWorkspaceEdit", commands.applySnippetWorkspaceEditCommand);
|
||||||
ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
|
ctx.registerCommand("resolveCodeAction", commands.resolveCodeAction);
|
||||||
ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
|
ctx.registerCommand("applyActionGroup", commands.applyActionGroup);
|
||||||
ctx.registerCommand('gotoLocation', commands.gotoLocation);
|
ctx.registerCommand("gotoLocation", commands.gotoLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deactivate() {
|
export async function deactivate() {
|
||||||
|
@ -159,12 +168,16 @@ export async function deactivate() {
|
||||||
ctx = undefined;
|
ctx = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bootstrap(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise<string> {
|
async function bootstrap(
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
config: Config,
|
||||||
|
state: PersistentState
|
||||||
|
): Promise<string> {
|
||||||
const path = await getServer(context, config, state);
|
const path = await getServer(context, config, state);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Rust Analyzer Language Server is not available. " +
|
"Rust Analyzer Language Server is not available. " +
|
||||||
"Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation)."
|
"Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation)."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +199,7 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
location: vscode.ProgressLocation.Notification,
|
location: vscode.ProgressLocation.Notification,
|
||||||
title: "Patching rust-analyzer for NixOS"
|
title: "Patching rust-analyzer for NixOS",
|
||||||
},
|
},
|
||||||
async (progress, _) => {
|
async (progress, _) => {
|
||||||
const expression = `
|
const expression = `
|
||||||
|
@ -207,14 +220,16 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||||
try {
|
try {
|
||||||
progress.report({ message: "Patching executable", increment: 20 });
|
progress.report({ message: "Patching executable", increment: 20 });
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const handle = exec(`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
const handle = exec(
|
||||||
|
`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
||||||
(err, stdout, stderr) => {
|
(err, stdout, stderr) => {
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
reject(Error(stderr));
|
reject(Error(stderr));
|
||||||
} else {
|
} else {
|
||||||
resolve(stdout);
|
resolve(stdout);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
handle.stdin?.write(expression);
|
handle.stdin?.write(expression);
|
||||||
handle.stdin?.end();
|
handle.stdin?.end();
|
||||||
});
|
});
|
||||||
|
@ -225,25 +240,35 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getServer(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise<string | undefined> {
|
async function getServer(
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
config: Config,
|
||||||
|
state: PersistentState
|
||||||
|
): Promise<string | undefined> {
|
||||||
const explicitPath = serverPath(config);
|
const explicitPath = serverPath(config);
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
if (explicitPath.startsWith("~/")) {
|
if (explicitPath.startsWith("~/")) {
|
||||||
return os.homedir() + explicitPath.slice("~".length);
|
return os.homedir() + explicitPath.slice("~".length);
|
||||||
}
|
}
|
||||||
return explicitPath;
|
return explicitPath;
|
||||||
};
|
}
|
||||||
if (config.package.releaseTag === null) return "rust-analyzer";
|
if (config.package.releaseTag === null) return "rust-analyzer";
|
||||||
|
|
||||||
const ext = process.platform === "win32" ? ".exe" : "";
|
const ext = process.platform === "win32" ? ".exe" : "";
|
||||||
const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `rust-analyzer${ext}`);
|
const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `rust-analyzer${ext}`);
|
||||||
const bundledExists = await vscode.workspace.fs.stat(bundled).then(() => true, () => false);
|
const bundledExists = await vscode.workspace.fs.stat(bundled).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
);
|
||||||
if (bundledExists) {
|
if (bundledExists) {
|
||||||
let server = bundled;
|
let server = bundled;
|
||||||
if (await isNixOs()) {
|
if (await isNixOs()) {
|
||||||
await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
|
await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
|
||||||
const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer${ext}`);
|
const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer${ext}`);
|
||||||
let exists = await vscode.workspace.fs.stat(dest).then(() => true, () => false);
|
let exists = await vscode.workspace.fs.stat(dest).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
);
|
||||||
if (exists && config.package.version !== state.serverVersion) {
|
if (exists && config.package.version !== state.serverVersion) {
|
||||||
await vscode.workspace.fs.delete(dest);
|
await vscode.workspace.fs.delete(dest);
|
||||||
exists = false;
|
exists = false;
|
||||||
|
@ -261,11 +286,11 @@ async function getServer(context: vscode.ExtensionContext, config: Config, state
|
||||||
await state.updateServerVersion(undefined);
|
await state.updateServerVersion(undefined);
|
||||||
await vscode.window.showErrorMessage(
|
await vscode.window.showErrorMessage(
|
||||||
"Unfortunately we don't ship binaries for your platform yet. " +
|
"Unfortunately we don't ship binaries for your platform yet. " +
|
||||||
"You need to manually clone the rust-analyzer repository and " +
|
"You need to manually clone the rust-analyzer repository and " +
|
||||||
"run `cargo xtask install --server` to build the language server from sources. " +
|
"run `cargo xtask install --server` to build the language server from sources. " +
|
||||||
"If you feel that your platform should be supported, please create an issue " +
|
"If you feel that your platform should be supported, please create an issue " +
|
||||||
"about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
|
"about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
|
||||||
"will consider it."
|
"will consider it."
|
||||||
);
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -276,8 +301,10 @@ function serverPath(config: Config): string | null {
|
||||||
|
|
||||||
async function isNixOs(): Promise<boolean> {
|
async function isNixOs(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const contents = (await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))).toString();
|
const contents = (
|
||||||
const idString = contents.split('\n').find((a) => a.startsWith("ID=")) || "ID=linux";
|
await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))
|
||||||
|
).toString();
|
||||||
|
const idString = contents.split("\n").find((a) => a.startsWith("ID=")) || "ID=linux";
|
||||||
return idString.indexOf("nixos") !== -1;
|
return idString.indexOf("nixos") !== -1;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
|
@ -286,11 +313,14 @@ async function isNixOs(): Promise<boolean> {
|
||||||
|
|
||||||
function warnAboutExtensionConflicts() {
|
function warnAboutExtensionConflicts() {
|
||||||
if (vscode.extensions.getExtension("rust-lang.rust")) {
|
if (vscode.extensions.getExtension("rust-lang.rust")) {
|
||||||
vscode.window.showWarningMessage(
|
vscode.window
|
||||||
`You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
|
.showWarningMessage(
|
||||||
"plugins enabled. These are known to conflict and cause various functions of " +
|
`You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
|
||||||
"both plugins to not work correctly. You should disable one of them.", "Got it")
|
"plugins enabled. These are known to conflict and cause various functions of " +
|
||||||
.then(() => { }, console.error);
|
"both plugins to not work correctly. You should disable one of them.",
|
||||||
|
"Got it"
|
||||||
|
)
|
||||||
|
.then(() => {}, console.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,38 +332,38 @@ function warnAboutExtensionConflicts() {
|
||||||
*/
|
*/
|
||||||
function configureLanguage(): vscode.Disposable {
|
function configureLanguage(): vscode.Disposable {
|
||||||
const indentAction = vscode.IndentAction.None;
|
const indentAction = vscode.IndentAction.None;
|
||||||
return vscode.languages.setLanguageConfiguration('rust', {
|
return vscode.languages.setLanguageConfiguration("rust", {
|
||||||
onEnterRules: [
|
onEnterRules: [
|
||||||
{
|
{
|
||||||
// Doc single-line comment
|
// Doc single-line comment
|
||||||
// e.g. ///|
|
// e.g. ///|
|
||||||
beforeText: /^\s*\/{3}.*$/,
|
beforeText: /^\s*\/{3}.*$/,
|
||||||
action: { indentAction, appendText: '/// ' },
|
action: { indentAction, appendText: "/// " },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Parent doc single-line comment
|
// Parent doc single-line comment
|
||||||
// e.g. //!|
|
// e.g. //!|
|
||||||
beforeText: /^\s*\/{2}\!.*$/,
|
beforeText: /^\s*\/{2}\!.*$/,
|
||||||
action: { indentAction, appendText: '//! ' },
|
action: { indentAction, appendText: "//! " },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Begins an auto-closed multi-line comment (standard or parent doc)
|
// Begins an auto-closed multi-line comment (standard or parent doc)
|
||||||
// e.g. /** | */ or /*! | */
|
// e.g. /** | */ or /*! | */
|
||||||
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
|
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
|
||||||
afterText: /^\s*\*\/$/,
|
afterText: /^\s*\*\/$/,
|
||||||
action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: ' * ' },
|
action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: " * " },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Begins a multi-line comment (standard or parent doc)
|
// Begins a multi-line comment (standard or parent doc)
|
||||||
// e.g. /** ...| or /*! ...|
|
// e.g. /** ...| or /*! ...|
|
||||||
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
|
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
|
||||||
action: { indentAction, appendText: ' * ' },
|
action: { indentAction, appendText: " * " },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Continues a multi-line comment
|
// Continues a multi-line comment
|
||||||
// e.g. * ...|
|
// e.g. * ...|
|
||||||
beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
|
beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
|
||||||
action: { indentAction, appendText: '* ' },
|
action: { indentAction, appendText: "* " },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Dedents after closing a multi-line comment
|
// Dedents after closing a multi-line comment
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import { log } from './util';
|
import { log } from "./util";
|
||||||
|
|
||||||
export class PersistentState {
|
export class PersistentState {
|
||||||
constructor(private readonly globalState: vscode.Memento) {
|
constructor(private readonly globalState: vscode.Memento) {
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as lc from 'vscode-languageclient';
|
import * as lc from "vscode-languageclient";
|
||||||
import * as ra from './lsp_ext';
|
import * as ra from "./lsp_ext";
|
||||||
import * as tasks from './tasks';
|
import * as tasks from "./tasks";
|
||||||
|
|
||||||
import { Ctx } from './ctx';
|
import { Ctx } from "./ctx";
|
||||||
import { makeDebugConfig } from './debug';
|
import { makeDebugConfig } from "./debug";
|
||||||
import { Config, RunnableEnvCfg } from './config';
|
import { Config, RunnableEnvCfg } from "./config";
|
||||||
|
|
||||||
const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
|
const quickPickButtons = [
|
||||||
|
{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." },
|
||||||
|
];
|
||||||
|
|
||||||
export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> {
|
export async function selectRunnable(
|
||||||
|
ctx: Ctx,
|
||||||
|
prevRunnable?: RunnableQuickPick,
|
||||||
|
debuggeeOnly = false,
|
||||||
|
showButtons: boolean = true
|
||||||
|
): Promise<RunnableQuickPick | undefined> {
|
||||||
const editor = ctx.activeRustEditor;
|
const editor = ctx.activeRustEditor;
|
||||||
const client = ctx.client;
|
const client = ctx.client;
|
||||||
if (!editor || !client) return;
|
if (!editor || !client) return;
|
||||||
|
@ -20,23 +27,18 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
|
||||||
|
|
||||||
const runnables = await client.sendRequest(ra.runnables, {
|
const runnables = await client.sendRequest(ra.runnables, {
|
||||||
textDocument,
|
textDocument,
|
||||||
position: client.code2ProtocolConverter.asPosition(
|
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||||
editor.selection.active,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
const items: RunnableQuickPick[] = [];
|
const items: RunnableQuickPick[] = [];
|
||||||
if (prevRunnable) {
|
if (prevRunnable) {
|
||||||
items.push(prevRunnable);
|
items.push(prevRunnable);
|
||||||
}
|
}
|
||||||
for (const r of runnables) {
|
for (const r of runnables) {
|
||||||
if (
|
if (prevRunnable && JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)) {
|
||||||
prevRunnable &&
|
|
||||||
JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) {
|
if (debuggeeOnly && (r.label.startsWith("doctest") || r.label.startsWith("cargo"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
items.push(new RunnableQuickPick(r));
|
items.push(new RunnableQuickPick(r));
|
||||||
|
@ -53,7 +55,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
|
||||||
const disposables: vscode.Disposable[] = [];
|
const disposables: vscode.Disposable[] = [];
|
||||||
const close = (result?: RunnableQuickPick) => {
|
const close = (result?: RunnableQuickPick) => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
disposables.forEach(d => d.dispose());
|
disposables.forEach((d) => d.dispose());
|
||||||
};
|
};
|
||||||
|
|
||||||
const quickPick = vscode.window.createQuickPick<RunnableQuickPick>();
|
const quickPick = vscode.window.createQuickPick<RunnableQuickPick>();
|
||||||
|
@ -71,7 +73,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
|
||||||
}),
|
}),
|
||||||
quickPick.onDidChangeActive((active) => {
|
quickPick.onDidChangeActive((active) => {
|
||||||
if (showButtons && active.length > 0) {
|
if (showButtons && active.length > 0) {
|
||||||
if (active[0].label.startsWith('cargo')) {
|
if (active[0].label.startsWith("cargo")) {
|
||||||
// save button makes no sense for `cargo test` or `cargo check`
|
// save button makes no sense for `cargo test` or `cargo check`
|
||||||
quickPick.buttons = [];
|
quickPick.buttons = [];
|
||||||
} else if (quickPick.buttons.length === 0) {
|
} else if (quickPick.buttons.length === 0) {
|
||||||
|
@ -96,8 +98,11 @@ export class RunnableQuickPick implements vscode.QuickPickItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareEnv(runnable: ra.Runnable, runnableEnvCfg: RunnableEnvCfg): Record<string, string> {
|
export function prepareEnv(
|
||||||
const env: Record<string, string> = { "RUST_BACKTRACE": "short" };
|
runnable: ra.Runnable,
|
||||||
|
runnableEnvCfg: RunnableEnvCfg
|
||||||
|
): Record<string, string> {
|
||||||
|
const env: Record<string, string> = { RUST_BACKTRACE: "short" };
|
||||||
|
|
||||||
if (runnable.args.expectTest) {
|
if (runnable.args.expectTest) {
|
||||||
env["UPDATE_EXPECT"] = "1";
|
env["UPDATE_EXPECT"] = "1";
|
||||||
|
@ -141,7 +146,14 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
|
const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
|
||||||
const cargoTask = await tasks.buildCargoTask(target, definition, runnable.label, args, config.cargoRunner, true);
|
const cargoTask = await tasks.buildCargoTask(
|
||||||
|
target,
|
||||||
|
definition,
|
||||||
|
runnable.label,
|
||||||
|
args,
|
||||||
|
config.cargoRunner,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
cargoTask.presentationOptions.clear = true;
|
cargoTask.presentationOptions.clear = true;
|
||||||
// Sadly, this doesn't prevent focus stealing if the terminal is currently
|
// Sadly, this doesn't prevent focus stealing if the terminal is currently
|
||||||
|
@ -157,7 +169,7 @@ export function createArgs(runnable: ra.Runnable): string[] {
|
||||||
args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options.
|
args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options.
|
||||||
}
|
}
|
||||||
if (runnable.args.executableArgs.length > 0) {
|
if (runnable.args.executableArgs.length > 0) {
|
||||||
args.push('--', ...runnable.args.executableArgs);
|
args.push("--", ...runnable.args.executableArgs);
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
import { assert } from './util';
|
import { assert } from "./util";
|
||||||
|
|
||||||
export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
|
export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
|
||||||
if (edit.entries().length === 1) {
|
if (edit.entries().length === 1) {
|
||||||
|
@ -11,12 +11,17 @@ export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
|
||||||
}
|
}
|
||||||
for (const [uri, edits] of edit.entries()) {
|
for (const [uri, edits] of edit.entries()) {
|
||||||
const editor = await editorFromUri(uri);
|
const editor = await editorFromUri(uri);
|
||||||
if (editor) await editor.edit((builder) => {
|
if (editor) {
|
||||||
for (const indel of edits) {
|
await editor.edit((builder) => {
|
||||||
assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`);
|
for (const indel of edits) {
|
||||||
builder.replace(indel.range, indel.newText);
|
assert(
|
||||||
}
|
!parseSnippet(indel.newText),
|
||||||
});
|
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`
|
||||||
|
);
|
||||||
|
builder.replace(indel.range, indel.newText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +30,9 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef
|
||||||
// `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
|
// `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
|
||||||
await vscode.window.showTextDocument(uri, {});
|
await vscode.window.showTextDocument(uri, {});
|
||||||
}
|
}
|
||||||
return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
|
return vscode.window.visibleTextEditors.find(
|
||||||
|
(it) => it.document.uri.toString() === uri.toString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
|
export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
|
||||||
|
@ -37,22 +44,26 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
const [newText, [placeholderStart, placeholderLength]] = parsed;
|
const [newText, [placeholderStart, placeholderLength]] = parsed;
|
||||||
const prefix = newText.substr(0, placeholderStart);
|
const prefix = newText.substr(0, placeholderStart);
|
||||||
const lastNewline = prefix.lastIndexOf('\n');
|
const lastNewline = prefix.lastIndexOf("\n");
|
||||||
|
|
||||||
const startLine = indel.range.start.line + lineDelta + countLines(prefix);
|
const startLine = indel.range.start.line + lineDelta + countLines(prefix);
|
||||||
const startColumn = lastNewline === -1 ?
|
const startColumn =
|
||||||
indel.range.start.character + placeholderStart
|
lastNewline === -1
|
||||||
: prefix.length - lastNewline - 1;
|
? indel.range.start.character + placeholderStart
|
||||||
|
: prefix.length - lastNewline - 1;
|
||||||
const endColumn = startColumn + placeholderLength;
|
const endColumn = startColumn + placeholderLength;
|
||||||
selections.push(new vscode.Selection(
|
selections.push(
|
||||||
new vscode.Position(startLine, startColumn),
|
new vscode.Selection(
|
||||||
new vscode.Position(startLine, endColumn),
|
new vscode.Position(startLine, startColumn),
|
||||||
));
|
new vscode.Position(startLine, endColumn)
|
||||||
|
)
|
||||||
|
);
|
||||||
builder.replace(indel.range, newText);
|
builder.replace(indel.range, newText);
|
||||||
} else {
|
} else {
|
||||||
builder.replace(indel.range, indel.newText);
|
builder.replace(indel.range, indel.newText);
|
||||||
}
|
}
|
||||||
lineDelta += countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
|
lineDelta +=
|
||||||
|
countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (selections.length > 0) editor.selections = selections;
|
if (selections.length > 0) editor.selections = selections;
|
||||||
|
@ -65,8 +76,7 @@ function parseSnippet(snip: string): [string, [number, number]] | undefined {
|
||||||
const m = snip.match(/\$(0|\{0:([^}]*)\})/);
|
const m = snip.match(/\$(0|\{0:([^}]*)\})/);
|
||||||
if (!m) return undefined;
|
if (!m) return undefined;
|
||||||
const placeholder = m[2] ?? "";
|
const placeholder = m[2] ?? "";
|
||||||
if (m.index == null)
|
if (m.index == null) return undefined;
|
||||||
return undefined;
|
|
||||||
const range: [number, number] = [m.index, placeholder.length];
|
const range: [number, number] = [m.index, placeholder.length];
|
||||||
const insert = snip.replace(m[0], placeholder);
|
const insert = snip.replace(m[0], placeholder);
|
||||||
return [insert, range];
|
return [insert, range];
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import * as toolchain from "./toolchain";
|
import * as toolchain from "./toolchain";
|
||||||
import { Config } from './config';
|
import { Config } from "./config";
|
||||||
import { log } from './util';
|
import { log } from "./util";
|
||||||
|
|
||||||
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
|
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
|
||||||
// our configuration should be compatible with it so use the same key.
|
// our configuration should be compatible with it so use the same key.
|
||||||
export const TASK_TYPE = 'cargo';
|
export const TASK_TYPE = "cargo";
|
||||||
export const TASK_SOURCE = 'rust';
|
export const TASK_SOURCE = "rust";
|
||||||
|
|
||||||
export interface CargoTaskDefinition extends vscode.TaskDefinition {
|
export interface CargoTaskDefinition extends vscode.TaskDefinition {
|
||||||
command?: string;
|
command?: string;
|
||||||
|
@ -30,17 +30,23 @@ class CargoTaskProvider implements vscode.TaskProvider {
|
||||||
// tasks.json - only tweaked.
|
// tasks.json - only tweaked.
|
||||||
|
|
||||||
const defs = [
|
const defs = [
|
||||||
{ command: 'build', group: vscode.TaskGroup.Build },
|
{ command: "build", group: vscode.TaskGroup.Build },
|
||||||
{ command: 'check', group: vscode.TaskGroup.Build },
|
{ command: "check", group: vscode.TaskGroup.Build },
|
||||||
{ command: 'test', group: vscode.TaskGroup.Test },
|
{ command: "test", group: vscode.TaskGroup.Test },
|
||||||
{ command: 'clean', group: vscode.TaskGroup.Clean },
|
{ command: "clean", group: vscode.TaskGroup.Clean },
|
||||||
{ command: 'run', group: undefined },
|
{ command: "run", group: undefined },
|
||||||
];
|
];
|
||||||
|
|
||||||
const tasks: vscode.Task[] = [];
|
const tasks: vscode.Task[] = [];
|
||||||
for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
|
for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
|
||||||
for (const def of defs) {
|
for (const def of defs) {
|
||||||
const vscodeTask = await buildCargoTask(workspaceTarget, { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, [def.command], this.config.cargoRunner);
|
const vscodeTask = await buildCargoTask(
|
||||||
|
workspaceTarget,
|
||||||
|
{ type: TASK_TYPE, command: def.command },
|
||||||
|
`cargo ${def.command}`,
|
||||||
|
[def.command],
|
||||||
|
this.config.cargoRunner
|
||||||
|
);
|
||||||
vscodeTask.group = def.group;
|
vscodeTask.group = def.group;
|
||||||
tasks.push(vscodeTask);
|
tasks.push(vscodeTask);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +64,13 @@ class CargoTaskProvider implements vscode.TaskProvider {
|
||||||
|
|
||||||
if (definition.type === TASK_TYPE && definition.command) {
|
if (definition.type === TASK_TYPE && definition.command) {
|
||||||
const args = [definition.command].concat(definition.args ?? []);
|
const args = [definition.command].concat(definition.args ?? []);
|
||||||
return await buildCargoTask(task.scope, definition, task.name, args, this.config.cargoRunner);
|
return await buildCargoTask(
|
||||||
|
task.scope,
|
||||||
|
definition,
|
||||||
|
task.name,
|
||||||
|
args,
|
||||||
|
this.config.cargoRunner
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -73,7 +85,6 @@ export async function buildCargoTask(
|
||||||
customRunner?: string,
|
customRunner?: string,
|
||||||
throwOnError: boolean = false
|
throwOnError: boolean = false
|
||||||
): Promise<vscode.Task> {
|
): Promise<vscode.Task> {
|
||||||
|
|
||||||
let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined;
|
let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined;
|
||||||
|
|
||||||
if (customRunner) {
|
if (customRunner) {
|
||||||
|
@ -90,7 +101,6 @@ export async function buildCargoTask(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fallback to default processing
|
// fallback to default processing
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`;
|
if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`;
|
||||||
// fallback to default processing
|
// fallback to default processing
|
||||||
|
@ -117,7 +127,7 @@ export async function buildCargoTask(
|
||||||
name,
|
name,
|
||||||
TASK_SOURCE,
|
TASK_SOURCE,
|
||||||
exec,
|
exec,
|
||||||
['$rustc']
|
["$rustc"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as cp from 'child_process';
|
import * as cp from "child_process";
|
||||||
import * as os from 'os';
|
import * as os from "os";
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
import * as readline from 'readline';
|
import * as readline from "readline";
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from "vscode";
|
||||||
import { execute, log, memoizeAsync } from './util';
|
import { execute, log, memoizeAsync } from "./util";
|
||||||
|
|
||||||
interface CompilationArtifact {
|
interface CompilationArtifact {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
@ -18,7 +18,7 @@ export interface ArtifactSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Cargo {
|
export class Cargo {
|
||||||
constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) { }
|
constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) {}
|
||||||
|
|
||||||
// Made public for testing purposes
|
// Made public for testing purposes
|
||||||
static artifactSpec(args: readonly string[]): ArtifactSpec {
|
static artifactSpec(args: readonly string[]): ArtifactSpec {
|
||||||
|
@ -27,7 +27,9 @@ export class Cargo {
|
||||||
// arguments for a runnable from the quick pick should be updated.
|
// arguments for a runnable from the quick pick should be updated.
|
||||||
// see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
|
// see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
|
||||||
switch (cargoArgs[0]) {
|
switch (cargoArgs[0]) {
|
||||||
case "run": cargoArgs[0] = "build"; break;
|
case "run":
|
||||||
|
cargoArgs[0] = "build";
|
||||||
|
break;
|
||||||
case "test": {
|
case "test": {
|
||||||
if (!cargoArgs.includes("--no-run")) {
|
if (!cargoArgs.includes("--no-run")) {
|
||||||
cargoArgs.push("--no-run");
|
cargoArgs.push("--no-run");
|
||||||
|
@ -40,7 +42,7 @@ export class Cargo {
|
||||||
if (cargoArgs[0] === "test") {
|
if (cargoArgs[0] === "test") {
|
||||||
// for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
|
// for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
|
||||||
// produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
|
// produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
|
||||||
result.filter = (artifacts) => artifacts.filter(it => it.isTest);
|
result.filter = (artifacts) => artifacts.filter((it) => it.isTest);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -50,24 +52,25 @@ export class Cargo {
|
||||||
const artifacts: CompilationArtifact[] = [];
|
const artifacts: CompilationArtifact[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.runCargo(spec.cargoArgs,
|
await this.runCargo(
|
||||||
message => {
|
spec.cargoArgs,
|
||||||
if (message.reason === 'compiler-artifact' && message.executable) {
|
(message) => {
|
||||||
const isBinary = message.target.crate_types.includes('bin');
|
if (message.reason === "compiler-artifact" && message.executable) {
|
||||||
const isBuildScript = message.target.kind.includes('custom-build');
|
const isBinary = message.target.crate_types.includes("bin");
|
||||||
|
const isBuildScript = message.target.kind.includes("custom-build");
|
||||||
if ((isBinary && !isBuildScript) || message.profile.test) {
|
if ((isBinary && !isBuildScript) || message.profile.test) {
|
||||||
artifacts.push({
|
artifacts.push({
|
||||||
fileName: message.executable,
|
fileName: message.executable,
|
||||||
name: message.target.name,
|
name: message.target.name,
|
||||||
kind: message.target.kind[0],
|
kind: message.target.kind[0],
|
||||||
isTest: message.profile.test
|
isTest: message.profile.test,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (message.reason === 'compiler-message') {
|
} else if (message.reason === "compiler-message") {
|
||||||
this.output.append(message.message.rendered);
|
this.output.append(message.message.rendered);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stderr => this.output.append(stderr),
|
(stderr) => this.output.append(stderr)
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.output.show(true);
|
this.output.show(true);
|
||||||
|
@ -81,9 +84,9 @@ export class Cargo {
|
||||||
const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
|
const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
|
||||||
|
|
||||||
if (artifacts.length === 0) {
|
if (artifacts.length === 0) {
|
||||||
throw new Error('No compilation artifacts');
|
throw new Error("No compilation artifacts");
|
||||||
} else if (artifacts.length > 1) {
|
} else if (artifacts.length > 1) {
|
||||||
throw new Error('Multiple compilation artifacts are not supported.');
|
throw new Error("Multiple compilation artifacts are not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return artifacts[0].fileName;
|
return artifacts[0].fileName;
|
||||||
|
@ -97,25 +100,23 @@ export class Cargo {
|
||||||
const path = await cargoPath();
|
const path = await cargoPath();
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
const cargo = cp.spawn(path, cargoArgs, {
|
const cargo = cp.spawn(path, cargoArgs, {
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
cwd: this.rootFolder
|
cwd: this.rootFolder,
|
||||||
});
|
});
|
||||||
|
|
||||||
cargo.on('error', err => reject(new Error(`could not launch cargo: ${err}`)));
|
cargo.on("error", (err) => reject(new Error(`could not launch cargo: ${err}`)));
|
||||||
|
|
||||||
cargo.stderr.on('data', chunk => onStderrString(chunk.toString()));
|
cargo.stderr.on("data", (chunk) => onStderrString(chunk.toString()));
|
||||||
|
|
||||||
const rl = readline.createInterface({ input: cargo.stdout });
|
const rl = readline.createInterface({ input: cargo.stdout });
|
||||||
rl.on('line', line => {
|
rl.on("line", (line) => {
|
||||||
const message = JSON.parse(line);
|
const message = JSON.parse(line);
|
||||||
onStdoutJson(message);
|
onStdoutJson(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
cargo.on('exit', (exitCode, _) => {
|
cargo.on("exit", (exitCode, _) => {
|
||||||
if (exitCode === 0)
|
if (exitCode === 0) resolve(exitCode);
|
||||||
resolve(exitCode);
|
else reject(new Error(`exit code: ${exitCode}.`));
|
||||||
else
|
|
||||||
reject(new Error(`exit code: ${exitCode}.`));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -158,7 +159,12 @@ export const getPathForExecutable = memoizeAsync(
|
||||||
try {
|
try {
|
||||||
// hmm, `os.homedir()` seems to be infallible
|
// hmm, `os.homedir()` seems to be infallible
|
||||||
// it is not mentioned in docs and cannot be infered by the type signature...
|
// it is not mentioned in docs and cannot be infered by the type signature...
|
||||||
const standardPath = vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo", "bin", executableName);
|
const standardPath = vscode.Uri.joinPath(
|
||||||
|
vscode.Uri.file(os.homedir()),
|
||||||
|
".cargo",
|
||||||
|
"bin",
|
||||||
|
executableName
|
||||||
|
);
|
||||||
|
|
||||||
if (await isFileAtUri(standardPath)) return standardPath.fsPath;
|
if (await isFileAtUri(standardPath)) return standardPath.fsPath;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -169,13 +175,11 @@ export const getPathForExecutable = memoizeAsync(
|
||||||
);
|
);
|
||||||
|
|
||||||
async function lookupInPath(exec: string): Promise<boolean> {
|
async function lookupInPath(exec: string): Promise<boolean> {
|
||||||
const paths = process.env.PATH ?? "";;
|
const paths = process.env.PATH ?? "";
|
||||||
|
|
||||||
const candidates = paths.split(path.delimiter).flatMap(dirInPath => {
|
const candidates = paths.split(path.delimiter).flatMap((dirInPath) => {
|
||||||
const candidate = path.join(dirInPath, exec);
|
const candidate = path.join(dirInPath, exec);
|
||||||
return os.type() === "Windows_NT"
|
return os.type() === "Windows_NT" ? [candidate, `${candidate}.exe`] : [candidate];
|
||||||
? [candidate, `${candidate}.exe`]
|
|
||||||
: [candidate];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const isFile of candidates.map(isFileAtPath)) {
|
for await (const isFile of candidates.map(isFileAtPath)) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ export function assert(condition: boolean, explanation: string): asserts conditi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const log = new class {
|
export const log = new (class {
|
||||||
private enabled = true;
|
private enabled = true;
|
||||||
private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client");
|
private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client");
|
||||||
|
|
||||||
|
@ -55,21 +55,20 @@ export const log = new class {
|
||||||
depth: 6, // heuristic
|
depth: 6, // heuristic
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
|
|
||||||
export async function sendRequestWithRetry<TParam, TRet>(
|
export async function sendRequestWithRetry<TParam, TRet>(
|
||||||
client: lc.LanguageClient,
|
client: lc.LanguageClient,
|
||||||
reqType: lc.RequestType<TParam, TRet, unknown>,
|
reqType: lc.RequestType<TParam, TRet, unknown>,
|
||||||
param: TParam,
|
param: TParam,
|
||||||
token?: vscode.CancellationToken,
|
token?: vscode.CancellationToken
|
||||||
): Promise<TRet> {
|
): Promise<TRet> {
|
||||||
// The sequence is `10 * (2 ** (2 * n))` where n is 1, 2, 3...
|
// The sequence is `10 * (2 ** (2 * n))` where n is 1, 2, 3...
|
||||||
for (const delay of [40, 160, 640, 2560, 10240, null]) {
|
for (const delay of [40, 160, 640, 2560, 10240, null]) {
|
||||||
try {
|
try {
|
||||||
return await (token
|
return await (token
|
||||||
? client.sendRequest(reqType, param, token)
|
? client.sendRequest(reqType, param, token)
|
||||||
: client.sendRequest(reqType, param)
|
: client.sendRequest(reqType, param));
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (delay === null) {
|
if (delay === null) {
|
||||||
log.warn("LSP request timed out", { method: reqType.method, param, error });
|
log.warn("LSP request timed out", { method: reqType.method, param, error });
|
||||||
|
@ -86,11 +85,11 @@ export async function sendRequestWithRetry<TParam, TRet>(
|
||||||
await sleep(delay);
|
await sleep(delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw 'unreachable';
|
throw "unreachable";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sleep(ms: number) {
|
export function sleep(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RustDocument = vscode.TextDocument & { languageId: "rust" };
|
export type RustDocument = vscode.TextDocument & { languageId: "rust" };
|
||||||
|
@ -101,12 +100,12 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD
|
||||||
// by allowing only `file` schemes
|
// by allowing only `file` schemes
|
||||||
// unfortunately extensions that use diff views not always set this
|
// unfortunately extensions that use diff views not always set this
|
||||||
// to something different than 'file' (see ongoing bug: #4608)
|
// to something different than 'file' (see ongoing bug: #4608)
|
||||||
return document.languageId === 'rust' && document.uri.scheme === 'file';
|
return document.languageId === "rust" && document.uri.scheme === "file";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCargoTomlDocument(document: vscode.TextDocument): document is RustDocument {
|
export function isCargoTomlDocument(document: vscode.TextDocument): document is RustDocument {
|
||||||
// ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed
|
// ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed
|
||||||
return document.uri.scheme === 'file' && document.fileName.endsWith('Cargo.toml');
|
return document.uri.scheme === "file" && document.fileName.endsWith("Cargo.toml");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
|
export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
|
||||||
|
@ -116,9 +115,9 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
|
||||||
export function isValidExecutable(path: string): boolean {
|
export function isValidExecutable(path: string): boolean {
|
||||||
log.debug("Checking availability of a binary at", path);
|
log.debug("Checking availability of a binary at", path);
|
||||||
|
|
||||||
const res = spawnSync(path, ["--version"], { encoding: 'utf8' });
|
const res = spawnSync(path, ["--version"], { encoding: "utf8" });
|
||||||
|
|
||||||
const printOutput = res.error && (res.error as any).code !== 'ENOENT' ? log.warn : log.debug;
|
const printOutput = res.error && (res.error as any).code !== "ENOENT" ? log.warn : log.debug;
|
||||||
printOutput(path, "--version:", res);
|
printOutput(path, "--version:", res);
|
||||||
|
|
||||||
return res.status === 0;
|
return res.status === 0;
|
||||||
|
@ -126,17 +125,19 @@ export function isValidExecutable(path: string): boolean {
|
||||||
|
|
||||||
/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */
|
/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */
|
||||||
export function setContextValue(key: string, value: any): Thenable<void> {
|
export function setContextValue(key: string, value: any): Thenable<void> {
|
||||||
return vscode.commands.executeCommand('setContext', key, value);
|
return vscode.commands.executeCommand("setContext", key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a higher-order function that caches the results of invoking the
|
* Returns a higher-order function that caches the results of invoking the
|
||||||
* underlying async function.
|
* underlying async function.
|
||||||
*/
|
*/
|
||||||
export function memoizeAsync<Ret, TThis, Param extends string>(func: (this: TThis, arg: Param) => Promise<Ret>) {
|
export function memoizeAsync<Ret, TThis, Param extends string>(
|
||||||
|
func: (this: TThis, arg: Param) => Promise<Ret>
|
||||||
|
) {
|
||||||
const cache = new Map<string, Ret>();
|
const cache = new Map<string, Ret>();
|
||||||
|
|
||||||
return async function(this: TThis, arg: Param) {
|
return async function (this: TThis, arg: Param) {
|
||||||
const cached = cache.get(arg);
|
const cached = cache.get(arg);
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
import * as fs from 'fs';
|
import * as fs from "fs";
|
||||||
|
|
||||||
import { runTests } from '@vscode/test-electron';
|
import { runTests } from "@vscode/test-electron";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// The folder containing the Extension Manifest package.json
|
// The folder containing the Extension Manifest package.json
|
||||||
// Passed to `--extensionDevelopmentPath`
|
// Passed to `--extensionDevelopmentPath`
|
||||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
const extensionDevelopmentPath = path.resolve(__dirname, "../../");
|
||||||
|
|
||||||
// Minimum supported version.
|
// Minimum supported version.
|
||||||
const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
|
const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, "package.json"));
|
||||||
const json = JSON.parse(jsonData.toString());
|
const json = JSON.parse(jsonData.toString());
|
||||||
let minimalVersion: string = json.engines.vscode;
|
let minimalVersion: string = json.engines.vscode;
|
||||||
if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
|
if (minimalVersion.startsWith("^")) minimalVersion = minimalVersion.slice(1);
|
||||||
|
|
||||||
const launchArgs = ["--disable-extensions", extensionDevelopmentPath];
|
const launchArgs = ["--disable-extensions", extensionDevelopmentPath];
|
||||||
|
|
||||||
// All test suites (either unit tests or integration tests) should be in subfolders.
|
// All test suites (either unit tests or integration tests) should be in subfolders.
|
||||||
const extensionTestsPath = path.resolve(__dirname, './unit/index');
|
const extensionTestsPath = path.resolve(__dirname, "./unit/index");
|
||||||
|
|
||||||
// Run tests using the minimal supported version.
|
// Run tests using the minimal supported version.
|
||||||
await runTests({
|
await runTests({
|
||||||
version: minimalVersion,
|
version: minimalVersion,
|
||||||
launchArgs,
|
launchArgs,
|
||||||
extensionDevelopmentPath,
|
extensionDevelopmentPath,
|
||||||
extensionTestsPath
|
extensionTestsPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
// and the latest one
|
// and the latest one
|
||||||
await runTests({
|
await runTests({
|
||||||
version: 'stable',
|
version: "stable",
|
||||||
launchArgs,
|
launchArgs,
|
||||||
extensionDevelopmentPath,
|
extensionDevelopmentPath,
|
||||||
extensionTestsPath
|
extensionTestsPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(err => {
|
main().catch((err) => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('Failed to run tests', err);
|
console.error("Failed to run tests", err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from "fs/promises";
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
|
@ -59,7 +59,9 @@ export class Context {
|
||||||
export async function run(): Promise<void> {
|
export async function run(): Promise<void> {
|
||||||
const context = new Context();
|
const context = new Context();
|
||||||
|
|
||||||
const testFiles = (await readdir(path.resolve(__dirname))).filter(name => name.endsWith('.test.js'));
|
const testFiles = (await readdir(path.resolve(__dirname))).filter((name) =>
|
||||||
|
name.endsWith(".test.js")
|
||||||
|
);
|
||||||
for (const testFile of testFiles) {
|
for (const testFile of testFiles) {
|
||||||
try {
|
try {
|
||||||
const testModule = require(path.resolve(__dirname, testFile));
|
const testModule = require(path.resolve(__dirname, testFile));
|
||||||
|
|
|
@ -1,51 +1,98 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from "assert";
|
||||||
import { Cargo } from '../../src/toolchain';
|
import { Cargo } from "../../src/toolchain";
|
||||||
import { Context } from '.';
|
import { Context } from ".";
|
||||||
|
|
||||||
export async function getTests(ctx: Context) {
|
export async function getTests(ctx: Context) {
|
||||||
await ctx.suite('Launch configuration/Lens', suite => {
|
await ctx.suite("Launch configuration/Lens", (suite) => {
|
||||||
suite.addTest('A binary', async () => {
|
suite.addTest("A binary", async () => {
|
||||||
const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
|
const args = Cargo.artifactSpec([
|
||||||
|
"build",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--bin",
|
||||||
|
"pkg_name",
|
||||||
|
]);
|
||||||
|
|
||||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
|
assert.deepStrictEqual(args.cargoArgs, [
|
||||||
|
"build",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--bin",
|
||||||
|
"pkg_name",
|
||||||
|
"--message-format=json",
|
||||||
|
]);
|
||||||
assert.deepStrictEqual(args.filter, undefined);
|
assert.deepStrictEqual(args.filter, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('One of Multiple Binaries', async () => {
|
suite.addTest("One of Multiple Binaries", async () => {
|
||||||
const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
|
const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
|
||||||
|
|
||||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
|
assert.deepStrictEqual(args.cargoArgs, [
|
||||||
|
"build",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--bin",
|
||||||
|
"bin1",
|
||||||
|
"--message-format=json",
|
||||||
|
]);
|
||||||
assert.deepStrictEqual(args.filter, undefined);
|
assert.deepStrictEqual(args.filter, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('A test', async () => {
|
suite.addTest("A test", async () => {
|
||||||
const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
|
const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
|
||||||
|
|
||||||
assert.deepStrictEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
|
assert.deepStrictEqual(args.cargoArgs, [
|
||||||
|
"test",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--lib",
|
||||||
|
"--no-run",
|
||||||
|
"--message-format=json",
|
||||||
|
]);
|
||||||
assert.notDeepStrictEqual(args.filter, undefined);
|
assert.notDeepStrictEqual(args.filter, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await ctx.suite('Launch configuration/QuickPick', suite => {
|
await ctx.suite("Launch configuration/QuickPick", (suite) => {
|
||||||
suite.addTest('A binary', async () => {
|
suite.addTest("A binary", async () => {
|
||||||
const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
|
const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
|
||||||
|
|
||||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
|
assert.deepStrictEqual(args.cargoArgs, [
|
||||||
|
"build",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--bin",
|
||||||
|
"pkg_name",
|
||||||
|
"--message-format=json",
|
||||||
|
]);
|
||||||
assert.deepStrictEqual(args.filter, undefined);
|
assert.deepStrictEqual(args.filter, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite.addTest("One of Multiple Binaries", async () => {
|
||||||
suite.addTest('One of Multiple Binaries', async () => {
|
|
||||||
const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
|
const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
|
||||||
|
|
||||||
assert.deepStrictEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
|
assert.deepStrictEqual(args.cargoArgs, [
|
||||||
|
"build",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--bin",
|
||||||
|
"bin2",
|
||||||
|
"--message-format=json",
|
||||||
|
]);
|
||||||
assert.deepStrictEqual(args.filter, undefined);
|
assert.deepStrictEqual(args.filter, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('A test', async () => {
|
suite.addTest("A test", async () => {
|
||||||
const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
|
const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
|
||||||
|
|
||||||
assert.deepStrictEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
|
assert.deepStrictEqual(args.cargoArgs, [
|
||||||
|
"test",
|
||||||
|
"--package",
|
||||||
|
"pkg_name",
|
||||||
|
"--lib",
|
||||||
|
"--message-format=json",
|
||||||
|
"--no-run",
|
||||||
|
]);
|
||||||
assert.notDeepStrictEqual(args.filter, undefined);
|
assert.notDeepStrictEqual(args.filter, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from "assert";
|
||||||
import { prepareEnv } from '../../src/run';
|
import { prepareEnv } from "../../src/run";
|
||||||
import { RunnableEnvCfg } from '../../src/config';
|
import { RunnableEnvCfg } from "../../src/config";
|
||||||
import { Context } from '.';
|
import { Context } from ".";
|
||||||
import * as ra from '../../src/lsp_ext';
|
import * as ra from "../../src/lsp_ext";
|
||||||
|
|
||||||
function makeRunnable(label: string): ra.Runnable {
|
function makeRunnable(label: string): ra.Runnable {
|
||||||
return {
|
return {
|
||||||
|
@ -11,8 +11,8 @@ function makeRunnable(label: string): ra.Runnable {
|
||||||
args: {
|
args: {
|
||||||
cargoArgs: [],
|
cargoArgs: [],
|
||||||
executableArgs: [],
|
executableArgs: [],
|
||||||
cargoExtraArgs: []
|
cargoExtraArgs: [],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,20 +22,20 @@ function fakePrepareEnv(runnableName: string, config: RunnableEnvCfg): Record<st
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTests(ctx: Context) {
|
export async function getTests(ctx: Context) {
|
||||||
await ctx.suite('Runnable env', suite => {
|
await ctx.suite("Runnable env", (suite) => {
|
||||||
suite.addTest('Global config works', async () => {
|
suite.addTest("Global config works", async () => {
|
||||||
const binEnv = fakePrepareEnv("run project_name", { "GLOBAL": "g" });
|
const binEnv = fakePrepareEnv("run project_name", { GLOBAL: "g" });
|
||||||
assert.strictEqual(binEnv["GLOBAL"], "g");
|
assert.strictEqual(binEnv["GLOBAL"], "g");
|
||||||
|
|
||||||
const testEnv = fakePrepareEnv("test some::mod::test_name", { "GLOBAL": "g" });
|
const testEnv = fakePrepareEnv("test some::mod::test_name", { GLOBAL: "g" });
|
||||||
assert.strictEqual(testEnv["GLOBAL"], "g");
|
assert.strictEqual(testEnv["GLOBAL"], "g");
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('null mask works', async () => {
|
suite.addTest("null mask works", async () => {
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
env: { DATA: "data" }
|
env: { DATA: "data" },
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const binEnv = fakePrepareEnv("run project_name", config);
|
const binEnv = fakePrepareEnv("run project_name", config);
|
||||||
assert.strictEqual(binEnv["DATA"], "data");
|
assert.strictEqual(binEnv["DATA"], "data");
|
||||||
|
@ -44,14 +44,14 @@ export async function getTests(ctx: Context) {
|
||||||
assert.strictEqual(testEnv["DATA"], "data");
|
assert.strictEqual(testEnv["DATA"], "data");
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('order works', async () => {
|
suite.addTest("order works", async () => {
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
env: { DATA: "data" }
|
env: { DATA: "data" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
env: { DATA: "newdata" }
|
env: { DATA: "newdata" },
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const binEnv = fakePrepareEnv("run project_name", config);
|
const binEnv = fakePrepareEnv("run project_name", config);
|
||||||
assert.strictEqual(binEnv["DATA"], "newdata");
|
assert.strictEqual(binEnv["DATA"], "newdata");
|
||||||
|
@ -60,19 +60,19 @@ export async function getTests(ctx: Context) {
|
||||||
assert.strictEqual(testEnv["DATA"], "newdata");
|
assert.strictEqual(testEnv["DATA"], "newdata");
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('mask works', async () => {
|
suite.addTest("mask works", async () => {
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
env: { DATA: "data" }
|
env: { DATA: "data" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mask: "^run",
|
mask: "^run",
|
||||||
env: { DATA: "rundata" }
|
env: { DATA: "rundata" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mask: "special_test$",
|
mask: "special_test$",
|
||||||
env: { DATA: "special_test" }
|
env: { DATA: "special_test" },
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const binEnv = fakePrepareEnv("run project_name", config);
|
const binEnv = fakePrepareEnv("run project_name", config);
|
||||||
assert.strictEqual(binEnv["DATA"], "rundata");
|
assert.strictEqual(binEnv["DATA"], "rundata");
|
||||||
|
@ -84,15 +84,15 @@ export async function getTests(ctx: Context) {
|
||||||
assert.strictEqual(specialTestEnv["DATA"], "special_test");
|
assert.strictEqual(specialTestEnv["DATA"], "special_test");
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('exact test name works', async () => {
|
suite.addTest("exact test name works", async () => {
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
env: { DATA: "data" }
|
env: { DATA: "data" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mask: "some::mod::test_name",
|
mask: "some::mod::test_name",
|
||||||
env: { DATA: "test special" }
|
env: { DATA: "test special" },
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const testEnv = fakePrepareEnv("test some::mod::test_name", config);
|
const testEnv = fakePrepareEnv("test some::mod::test_name", config);
|
||||||
assert.strictEqual(testEnv["DATA"], "test special");
|
assert.strictEqual(testEnv["DATA"], "test special");
|
||||||
|
@ -101,15 +101,15 @@ export async function getTests(ctx: Context) {
|
||||||
assert.strictEqual(specialTestEnv["DATA"], "data");
|
assert.strictEqual(specialTestEnv["DATA"], "data");
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('test mod name works', async () => {
|
suite.addTest("test mod name works", async () => {
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
env: { DATA: "data" }
|
env: { DATA: "data" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mask: "some::mod",
|
mask: "some::mod",
|
||||||
env: { DATA: "mod special" }
|
env: { DATA: "mod special" },
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
const testEnv = fakePrepareEnv("test some::mod::test_name", config);
|
const testEnv = fakePrepareEnv("test some::mod::test_name", config);
|
||||||
assert.strictEqual(testEnv["DATA"], "mod special");
|
assert.strictEqual(testEnv["DATA"], "mod special");
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from "assert";
|
||||||
import { Context } from '.';
|
import { Context } from ".";
|
||||||
import { substituteVariablesInEnv } from '../../src/config';
|
import { substituteVariablesInEnv } from "../../src/config";
|
||||||
|
|
||||||
export async function getTests(ctx: Context) {
|
export async function getTests(ctx: Context) {
|
||||||
await ctx.suite('Server Env Settings', suite => {
|
await ctx.suite("Server Env Settings", (suite) => {
|
||||||
suite.addTest('Replacing Env Variables', async () => {
|
suite.addTest("Replacing Env Variables", async () => {
|
||||||
const envJson = {
|
const envJson = {
|
||||||
USING_MY_VAR: "${env:MY_VAR} test ${env:MY_VAR}",
|
USING_MY_VAR: "${env:MY_VAR} test ${env:MY_VAR}",
|
||||||
MY_VAR: "test"
|
MY_VAR: "test",
|
||||||
};
|
};
|
||||||
const expectedEnv = {
|
const expectedEnv = {
|
||||||
USING_MY_VAR: "test test test",
|
USING_MY_VAR: "test test test",
|
||||||
MY_VAR: "test"
|
MY_VAR: "test",
|
||||||
};
|
};
|
||||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||||
assert.deepStrictEqual(actualEnv, expectedEnv);
|
assert.deepStrictEqual(actualEnv, expectedEnv);
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('Circular dependencies remain as is', async () => {
|
suite.addTest("Circular dependencies remain as is", async () => {
|
||||||
const envJson = {
|
const envJson = {
|
||||||
A_USES_B: "${env:B_USES_A}",
|
A_USES_B: "${env:B_USES_A}",
|
||||||
B_USES_A: "${env:A_USES_B}",
|
B_USES_A: "${env:A_USES_B}",
|
||||||
C_USES_ITSELF: "${env:C_USES_ITSELF}",
|
C_USES_ITSELF: "${env:C_USES_ITSELF}",
|
||||||
D_USES_C: "${env:C_USES_ITSELF}",
|
D_USES_C: "${env:C_USES_ITSELF}",
|
||||||
E_IS_ISOLATED: "test",
|
E_IS_ISOLATED: "test",
|
||||||
F_USES_E: "${env:E_IS_ISOLATED}"
|
F_USES_E: "${env:E_IS_ISOLATED}",
|
||||||
};
|
};
|
||||||
const expectedEnv = {
|
const expectedEnv = {
|
||||||
A_USES_B: "${env:B_USES_A}",
|
A_USES_B: "${env:B_USES_A}",
|
||||||
|
@ -32,30 +32,30 @@ export async function getTests(ctx: Context) {
|
||||||
C_USES_ITSELF: "${env:C_USES_ITSELF}",
|
C_USES_ITSELF: "${env:C_USES_ITSELF}",
|
||||||
D_USES_C: "${env:C_USES_ITSELF}",
|
D_USES_C: "${env:C_USES_ITSELF}",
|
||||||
E_IS_ISOLATED: "test",
|
E_IS_ISOLATED: "test",
|
||||||
F_USES_E: "test"
|
F_USES_E: "test",
|
||||||
};
|
};
|
||||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||||
assert.deepStrictEqual(actualEnv, expectedEnv);
|
assert.deepStrictEqual(actualEnv, expectedEnv);
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('Should support external variables', async () => {
|
suite.addTest("Should support external variables", async () => {
|
||||||
const envJson = {
|
const envJson = {
|
||||||
USING_EXTERNAL_VAR: "${env:TEST_VARIABLE} test ${env:TEST_VARIABLE}"
|
USING_EXTERNAL_VAR: "${env:TEST_VARIABLE} test ${env:TEST_VARIABLE}",
|
||||||
};
|
};
|
||||||
const expectedEnv = {
|
const expectedEnv = {
|
||||||
USING_EXTERNAL_VAR: "test test test"
|
USING_EXTERNAL_VAR: "test test test",
|
||||||
};
|
};
|
||||||
|
|
||||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||||
assert.deepStrictEqual(actualEnv, expectedEnv);
|
assert.deepStrictEqual(actualEnv, expectedEnv);
|
||||||
});
|
});
|
||||||
|
|
||||||
suite.addTest('should support VSCode variables', async () => {
|
suite.addTest("should support VSCode variables", async () => {
|
||||||
const envJson = {
|
const envJson = {
|
||||||
USING_VSCODE_VAR: "${workspaceFolderBasename}"
|
USING_VSCODE_VAR: "${workspaceFolderBasename}",
|
||||||
};
|
};
|
||||||
const actualEnv = await substituteVariablesInEnv(envJson);
|
const actualEnv = await substituteVariablesInEnv(envJson);
|
||||||
assert.deepStrictEqual(actualEnv.USING_VSCODE_VAR, 'code');
|
assert.deepStrictEqual(actualEnv.USING_VSCODE_VAR, "code");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// Special typescript project file, used by eslint only.
|
// Special typescript project file, used by eslint only.
|
||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"include": [
|
"include": [
|
||||||
// repeated from base config's "include" setting
|
// repeated from base config's "include" setting
|
||||||
"src",
|
"src",
|
||||||
"tests",
|
"tests",
|
||||||
// these are the eslint-only inclusions
|
// these are the eslint-only inclusions
|
||||||
".eslintrc.js",
|
".eslintrc.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es2021",
|
"target": "es2021",
|
||||||
"outDir": "out",
|
"outDir": "out",
|
||||||
"lib": [
|
"lib": ["es2021"],
|
||||||
"es2021"
|
|
||||||
],
|
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
@ -16,12 +14,6 @@
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"newLine": "LF"
|
"newLine": "LF"
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": ["node_modules", ".vscode-test"],
|
||||||
"node_modules",
|
"include": ["src", "tests"]
|
||||||
".vscode-test"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"src",
|
|
||||||
"tests"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue