diff --git a/README.md b/README.md index 3a10b463101..df87a4eea0b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ a code editor with what developers need for their core edit-build-debug cycle. Code provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. -VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily. +VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.

VS Code in action diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 20531bdc4b1..68caf689ded 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -43,8 +43,8 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.16.4' }, - { name: 'ms-vscode.node-debug2', version: '1.16.0' } + { name: 'ms-vscode.node-debug', version: '1.16.5' }, + { name: 'ms-vscode.node-debug2', version: '1.16.3' } ]; const excludedExtensions = [ diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 096f516afcf..9c2970d508b 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -5,16 +5,7 @@ declare module monaco { - interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; - } + type Thenable = PromiseLike; export interface IDisposable { dispose(): void; @@ -41,7 +32,7 @@ declare module monaco { Error = 3, } -#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, TPromise +#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, Promise #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken #include(vs/base/common/uri): URI #include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 7b156212bec..95205aa1fe1 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -24,6 +24,7 @@ npmInstall('extensions'); // node modules shared by all extensions const extensions = [ 'vscode-api-tests', 'vscode-colorize-tests', + 'azure-account', 'json', 'configuration-editing', 'extension-editing', diff --git a/extensions/azure-account/.vscodeignore b/extensions/azure-account/.vscodeignore new file mode 100644 index 00000000000..24428a6f758 --- /dev/null +++ b/extensions/azure-account/.vscodeignore @@ -0,0 +1,4 @@ +test/** +src/** +tsconfig.json +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/azure-account/npm-shrinkwrap.json b/extensions/azure-account/npm-shrinkwrap.json new file mode 100644 index 00000000000..18206b8124a --- /dev/null +++ b/extensions/azure-account/npm-shrinkwrap.json @@ -0,0 +1,521 @@ +{ + "name": "azure-account", + "version": "0.1.0", + "dependencies": { + "@types/copy-paste": { + "version": "1.1.30", + "from": "@types/copy-paste@>=1.1.30 <2.0.0", + "resolved": "https://registry.npmjs.org/@types/copy-paste/-/copy-paste-1.1.30.tgz", + "dev": true + }, + "@types/form-data": { + "version": "2.2.0", + "from": "@types/form-data@*", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.0.tgz", + "dependencies": { + "@types/node": { + "version": "8.0.24", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz" + } + } + }, + "@types/node": { + "version": "6.0.87", + "from": "@types/node@>=6.0.40 <7.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.87.tgz", + "dev": true + }, + "@types/opn": { + "version": "3.0.28", + "from": "@types/opn@>=3.0.28 <4.0.0", + "resolved": "https://registry.npmjs.org/@types/opn/-/opn-3.0.28.tgz", + "dev": true + }, + "@types/request": { + "version": "0.0.45", + "from": "@types/request@>=0.0.45 <0.0.46", + "resolved": "https://registry.npmjs.org/@types/request/-/request-0.0.45.tgz", + "dependencies": { + "@types/node": { + "version": "8.0.24", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz" + } + } + }, + "@types/uuid": { + "version": "2.0.30", + "from": "@types/uuid@>=2.0.29 <3.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-2.0.30.tgz", + "dependencies": { + "@types/node": { + "version": "8.0.24", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz" + } + } + }, + "adal-node": { + "version": "0.1.22", + "from": "adal-node@>=0.1.22 <0.2.0", + "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.22.tgz" + }, + "ajv": { + "version": "4.11.8", + "from": "ajv@>=4.9.1 <5.0.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz" + }, + "asn1": { + "version": "0.2.3", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "async": { + "version": "2.5.0", + "from": "async@>=0.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz" + }, + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + }, + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "azure-arm-resource": { + "version": "2.0.0-preview", + "from": "azure-arm-resource@>=2.0.0-preview <3.0.0", + "resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-2.0.0-preview.tgz" + }, + "base64url": { + "version": "2.0.0", + "from": "base64url@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "optional": true + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "from": "buffer-equal-constant-time@1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "caseless@>=0.12.0 <0.13.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "co": { + "version": "4.6.0", + "from": "co@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" + }, + "copy-paste": { + "version": "1.3.0", + "from": "copy-paste@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-1.3.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "date-utils": { + "version": "1.2.21", + "from": "date-utils@*", + "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz" + }, + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + }, + "duplexer": { + "version": "0.1.1", + "from": "duplexer@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "optional": true + }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "from": "ecdsa-sig-formatter@1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz" + }, + "extend": { + "version": "3.0.1", + "from": "extend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz" + }, + "extsprintf": { + "version": "1.3.0", + "from": "extsprintf@1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.4", + "from": "form-data@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" + }, + "getpass": { + "version": "0.1.7", + "from": "getpass@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "har-schema@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + }, + "har-validator": { + "version": "4.2.1", + "from": "har-validator@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz" + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@>=3.1.3 <3.2.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" + }, + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" + }, + "iconv-lite": { + "version": "0.4.18", + "from": "iconv-lite@>=0.4.8 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz" + }, + "is-buffer": { + "version": "1.1.5", + "from": "is-buffer@>=1.1.5 <2.0.0", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + }, + "is-stream": { + "version": "1.1.0", + "from": "is-stream@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "is-wsl": { + "version": "1.1.0", + "from": "is-wsl@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.1 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "jsonify": { + "version": "0.0.0", + "from": "jsonify@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + }, + "jsprim": { + "version": "1.4.1", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "jwa": { + "version": "1.1.5", + "from": "jwa@>=1.1.4 <2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz" + }, + "jws": { + "version": "3.1.4", + "from": "jws@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.14.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "mime-db": { + "version": "1.29.0", + "from": "mime-db@>=1.29.0 <1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz" + }, + "mime-types": { + "version": "2.1.16", + "from": "mime-types@>=2.1.7 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz" + }, + "moment": { + "version": "2.18.1", + "from": "moment@>=2.14.1 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz" + }, + "ms-rest": { + "version": "2.2.1", + "from": "ms-rest@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.2.1.tgz", + "dependencies": { + "@types/node": { + "version": "7.0.42", + "from": "@types/node@>=7.0.10 <8.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.42.tgz" + }, + "uuid": { + "version": "3.1.0", + "from": "uuid@^3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + } + }, + "ms-rest-azure": { + "version": "2.2.3", + "from": "ms-rest-azure@>=2.2.3 <3.0.0", + "resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.2.3.tgz", + "dependencies": { + "@types/node": { + "version": "7.0.42", + "from": "@types/node@^7.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.42.tgz" + }, + "async": { + "version": "0.2.7", + "from": "async@0.2.7", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.7.tgz" + }, + "uuid": { + "version": "3.1.0", + "from": "uuid@^3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + } + }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@1.4.7", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "opn": { + "version": "5.1.0", + "from": "opn@>=5.1.0 <6.0.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "performance-now@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "qs@>=6.4.0 <6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "request": { + "version": "2.81.0", + "from": "request@>=2.52.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "uuid": { + "version": "3.1.0", + "from": "uuid@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "from": "safe-buffer@>=5.0.1 <6.0.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + }, + "sshpk": { + "version": "1.13.1", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "sync-exec": { + "version": "0.6.2", + "from": "sync-exec@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.6.2.tgz", + "optional": true + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.4 <2.4.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz" + }, + "tunnel": { + "version": "0.0.5", + "from": "tunnel@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz" + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@>=0.14.0 <0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "optional": true + }, + "underscore": { + "version": "1.8.3", + "from": "underscore@>=1.3.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" + }, + "verror": { + "version": "1.10.0", + "from": "verror@1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "xmldom": { + "version": "0.1.27", + "from": "xmldom@>=0.1.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz" + }, + "xpath.js": { + "version": "1.0.7", + "from": "xpath.js@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.0.7.tgz" + } + } +} diff --git a/extensions/azure-account/package.json b/extensions/azure-account/package.json new file mode 100644 index 00000000000..d98aa059e74 --- /dev/null +++ b/extensions/azure-account/package.json @@ -0,0 +1,70 @@ +{ + "name": "azure-account", + "version": "0.1.0", + "publisher": "vscode", + "engines": { + "vscode": "*" + }, + "enableProposedApi": true, + "activationEvents": [ + "*" + ], + "main": "./out/extension", + "contributes": { + "commands": [ + { + "command": "azure-account.login", + "title": "%azure-account.commands.login%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.logout", + "title": "%azure-account.commands.logout%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.addFilter", + "title": "%azure-account.commands.addResourceFilter%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.removeFilter", + "title": "%azure-account.commands.removeResourceFilter%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.createAccount", + "title": "%azure-account.commands.createAccount%", + "category": "%azure-account.commands.azure%" + } + ], + "configuration": { + "type": "object", + "title": "Azure configuration", + "properties": { + "azure.resourceFilter": { + "type": "array", + "default": null, + "description": "The resource filter, each element is either a subscription id or a subscription id and a resource group name separated by a slash." + } + } + } + }, + "scripts": { + "compile": "gulp compile-extension:azure-account", + "watch": "gulp watch-extension:azure-account" + }, + "devDependencies": { + "@types/copy-paste": "^1.1.30", + "@types/node": "^6.0.40", + "@types/opn": "^3.0.28" + }, + "dependencies": { + "adal-node": "^0.1.22", + "azure-arm-resource": "^2.0.0-preview", + "copy-paste": "^1.3.0", + "ms-rest-azure": "^2.2.3", + "vscode-nls": "^2.0.2", + "opn": "^5.1.0" + } +} \ No newline at end of file diff --git a/extensions/azure-account/package.nls.json b/extensions/azure-account/package.nls.json new file mode 100644 index 00000000000..4857a376665 --- /dev/null +++ b/extensions/azure-account/package.nls.json @@ -0,0 +1,8 @@ +{ + "azure-account.commands.azure": "Azure", + "azure-account.commands.login": "Login", + "azure-account.commands.logout": "Logout", + "azure-account.commands.addResourceFilter": "Add Resource Filter", + "azure-account.commands.removeResourceFilter": "Remove Resource Filter", + "azure-account.commands.createAccount": "Create an Account" +} \ No newline at end of file diff --git a/extensions/azure-account/src/azure-account.ts b/extensions/azure-account/src/azure-account.ts new file mode 100644 index 00000000000..d5b4b3d9ed1 --- /dev/null +++ b/extensions/azure-account/src/azure-account.ts @@ -0,0 +1,472 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const adal = require('adal-node'); +const MemoryCache = adal.MemoryCache; +const AuthenticationContext = adal.AuthenticationContext; +const CacheDriver = require('adal-node/lib/cache-driver'); +const createLogContext = require('adal-node/lib/log').createLogContext; + +import { DeviceTokenCredentials, AzureEnvironment } from 'ms-rest-azure'; +import { SubscriptionClient, ResourceManagementClient, SubscriptionModels } from 'azure-arm-resource'; +import * as opn from 'opn'; +import * as copypaste from 'copy-paste'; +import * as nls from 'vscode-nls'; + +import { window, commands, credentials, EventEmitter, MessageItem, ExtensionContext, workspace, ConfigurationTarget } from 'vscode'; +import { AzureAccount, AzureSession, AzureLoginStatus, AzureResourceFilter } from './typings/azure-account.api'; + +const localize = nls.loadMessageBundle(); + +const defaultEnvironment = (AzureEnvironment).Azure; +const commonTenantId = 'common'; +const authorityHostUrl = defaultEnvironment.activeDirectoryEndpointUrl; +const clientId = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'; +const authorityUrl = `${authorityHostUrl}${commonTenantId}`; +const resource = defaultEnvironment.activeDirectoryResourceId; + +const credentialsService = 'VSCode Public Azure'; +const credentialsAccount = 'Refresh Token'; + +interface DeviceLogin { + userCode: string; + deviceCode: string; + verificationUrl: string; + expiresIn: number; + interval: number; + message: string; +} + +interface TokenResponse { + tokenType: string; + expiresIn: number; + expiresOn: string; + resource: string; + accessToken: string; + refreshToken: string; + userId: string; + isUserIdDisplayable: boolean; + familyName: string; + givenName: string; + oid: string; + tenantId: string; + isMRRT: boolean; + _clientId: string; + _authority: string; +} + +interface AzureAccountWriteable extends AzureAccount { + status: AzureLoginStatus; +} + +class AzureLoginError extends Error { + constructor(message: string, public _reason: any) { + super(message); + } +} + +export class AzureLoginHelper { + + private onStatusChanged = new EventEmitter(); + private onSessionsChanged = new EventEmitter(); + private onFiltersChanged = new EventEmitter(); + private tokenCache = new MemoryCache(); + private oldResourceFilter: string; + + constructor(context: ExtensionContext) { + const subscriptions = context.subscriptions; + subscriptions.push(commands.registerCommand('azure-account.login', () => this.login().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.logout', () => this.logout().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.askForLogin', () => this.askForLogin().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.addFilter', () => this.addFilter().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.removeFilter', () => this.removeFilter().catch(console.error))); + subscriptions.push(this.api.onSessionsChanged(() => this.updateFilters().catch(console.error))); + subscriptions.push(workspace.onDidChangeConfiguration(() => this.updateFilters(true).catch(console.error))); + this.initialize() + .catch(console.error); + } + + api: AzureAccount = { + status: 'Initializing', + onStatusChanged: this.onStatusChanged.event, + sessions: [], + onSessionsChanged: this.onSessionsChanged.event, + filters: [], + onFiltersChanged: this.onFiltersChanged.event, + credentials + }; + + async login() { + try { + this.beginLoggingIn(); + const deviceLogin = await deviceLogin1(); + const copyAndOpen: MessageItem = { title: localize('azure-account.copyAndOpen', "Copy & Open") }; + const close: MessageItem = { title: localize('azure-account.close', "Close"), isCloseAffordance: true }; + const response = await window.showInformationMessage(deviceLogin.message, copyAndOpen, close); + if (response === copyAndOpen) { + copypaste.copy(deviceLogin.userCode); + opn(deviceLogin.verificationUrl); + } + const tokenResponse = await deviceLogin2(deviceLogin); + const refreshToken = tokenResponse.refreshToken; + const tokenResponses = await tokensFromToken(tokenResponse); + await credentials.writeSecret(credentialsService, credentialsAccount, refreshToken); + await this.updateSessions(tokenResponses); + } finally { + this.updateStatus(); + } + } + + async logout() { + await credentials.deleteSecret(credentialsService, credentialsAccount); + await this.updateSessions([]); + this.updateStatus(); + } + + private async initialize() { + try { + const refreshToken = await credentials.readSecret(credentialsService, credentialsAccount); + if (refreshToken) { + this.beginLoggingIn(); + const tokenResponse = await tokenFromRefreshToken(refreshToken); + const tokenResponses = await tokensFromToken(tokenResponse); + await this.updateSessions(tokenResponses); + } + } catch (err) { + if (!(err instanceof AzureLoginError)) { + throw err; + } + } finally { + this.updateStatus(); + } + } + + private beginLoggingIn() { + if (this.api.status !== 'LoggedIn') { + (this.api).status = 'LoggingIn'; + this.onStatusChanged.fire(this.api.status); + } + } + + private updateStatus() { + const status = this.api.sessions.length ? 'LoggedIn' : 'LoggedOut'; + if (this.api.status !== status) { + (this.api).status = status; + this.onStatusChanged.fire(this.api.status); + } + } + + private async updateSessions(tokenResponses: TokenResponse[]) { + await clearTokenCache(this.tokenCache); + for (const tokenResponse of tokenResponses) { + await addTokenToCache(this.tokenCache, tokenResponse); + } + const sessions = this.api.sessions; + sessions.splice(0, sessions.length, ...tokenResponses.map(tokenResponse => ({ + environment: defaultEnvironment, + userId: tokenResponse.userId, + tenantId: tokenResponse.tenantId, + credentials: new DeviceTokenCredentials({ username: tokenResponse.userId, clientId, tokenCache: this.tokenCache, domain: tokenResponse.tenantId }) + }))); + this.onSessionsChanged.fire(); + } + + private async askForLogin() { + if (this.api.status === 'LoggedIn') { + return; + } + const login = { title: localize('azure-account.login', "Login") }; + const cancel = { title: 'Cancel', isCloseAffordance: true }; + const result = await window.showInformationMessage(localize('azure-account.loginFirst', "Not logged in, log in first."), login, cancel); + return result === login && commands.executeCommand('azure-account.login'); + } + + private async addFilter() { + if (this.api.status !== 'LoggedIn') { + return commands.executeCommand('azure-account.askForLogin'); + } + + const azureConfig = workspace.getConfiguration('azure'); + const resourceFilter = azureConfig.get('resourceFilter') || []; + + const subscriptionItems: { session: AzureSession; subscription: SubscriptionModels.Subscription }[] = []; + for (const session of this.api.sessions) { + const credentials = session.credentials; + const client = new SubscriptionClient(credentials); + const subscriptions = await listAll(client.subscriptions, client.subscriptions.list()); + subscriptionItems.push(...subscriptions.filter(subscription => resourceFilter.indexOf(`${session.tenantId}/${subscription.subscriptionId}`) === -1) + .map(subscription => ({ + session, + subscription + }))); + } + subscriptionItems.sort((a, b) => a.subscription.displayName!.localeCompare(b.subscription.displayName!)); + const subscriptionResult = await window.showQuickPick(subscriptionItems.map(subscription => ({ + label: subscription.subscription.displayName!, + description: subscription.subscription.subscriptionId!, + subscription + }))); + if (!subscriptionResult) { + return; + } + + const { session, subscription } = subscriptionResult.subscription; + const client = new ResourceManagementClient(session.credentials, subscription.subscriptionId!); + const resourceGroups = await listAll(client.resourceGroups, client.resourceGroups.list()); + const resourceGroupFilters: AzureResourceFilter[] = [ + { + ...subscriptionResult.subscription, + allResourceGroups: true, + resourceGroups + } + ]; + resourceGroupFilters.push(...resourceGroups.filter(resourceGroup => resourceFilter.indexOf(`${session.tenantId}/${subscription.subscriptionId}/${resourceGroup.name}`) === -1) + .map(resourceGroup => ({ + session, + subscription, + allResourceGroups: false, + resourceGroups: [resourceGroup] + }))); + resourceGroupFilters.sort((a, b) => (!a.allResourceGroups ? a.resourceGroups[0].name! : '').localeCompare(!b.allResourceGroups ? b.resourceGroups[0].name! : '')); + const resourceGroupResult = await window.showQuickPick(resourceGroupFilters.map(resourceGroup => (!resourceGroup.allResourceGroups ? { + label: resourceGroup.resourceGroups[0].name!, + description: resourceGroup.resourceGroups[0].location, + resourceGroup + } : { + label: localize('azure-account.entireSubscription', "Entire Subscription"), + description: '', + resourceGroup + }))); + if (!resourceGroupResult) { + return; + } + + const resourceGroup = resourceGroupResult.resourceGroup; + if (!resourceGroup.allResourceGroups) { + resourceFilter.push(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}/${resourceGroup.resourceGroups[0].name}`); + } else { + resourceFilter.splice(0, resourceFilter.length, ...resourceFilter.filter(c => !c.startsWith(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}/`))); + resourceFilter.push(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}`); + } + + const resourceFilterConfig = azureConfig.inspect('resourceFilter'); + let target = ConfigurationTarget.Global; + if (resourceFilterConfig) { + if (resourceFilterConfig.workspaceFolderValue) { + target = ConfigurationTarget.WorkspaceFolder; + } else if (resourceFilterConfig.workspaceValue) { + target = ConfigurationTarget.Workspace; + } else if (resourceFilterConfig.globalValue) { + target = ConfigurationTarget.Global; + } + } + await azureConfig.update('resourceFilter', resourceFilter, target); + } + + private async removeFilter() { + if (this.api.status !== 'LoggedIn') { + return commands.executeCommand('azure-account.askForLogin'); + } + + const azureConfig = workspace.getConfiguration('azure'); + let resourceFilter = azureConfig.get('resourceFilter') || []; + + const filters = resourceFilter.length ? this.api.filters.reduce((list, filter) => { + if (filter.allResourceGroups) { + list.push(filter); + } else { + list.push(...filter.resourceGroups.map(resourceGroup => ({ + ...filter, + resourceGroups: [resourceGroup] + }))); + } + return list; + }, []) : []; + filters.sort((a, b) => (!a.allResourceGroups ? a.resourceGroups[0].name! : `/${a.subscription.displayName}`).localeCompare(!b.allResourceGroups ? b.resourceGroups[0].name! : `/${b.subscription.displayName}`)); + const filterResult = await window.showQuickPick(filters.map(filter => (!filter.allResourceGroups ? { + label: filter.resourceGroups[0].name!, + description: filter.subscription.displayName!, + filter + } : { + label: filter.subscription.displayName!, + description: filter.subscription.subscriptionId!, + filter + }))); + if (!filterResult) { + return; + } + + const filter = filterResult.filter; + const remove = !filter.allResourceGroups ? + `${filter.session.tenantId}/${filter.subscription.subscriptionId}/${filter.resourceGroups[0].name}` : + `${filter.session.tenantId}/${filter.subscription.subscriptionId}`; + resourceFilter = resourceFilter.filter(e => e !== remove); + + const resourceFilterConfig = azureConfig.inspect('resourceFilter'); + let target = ConfigurationTarget.Global; + if (resourceFilterConfig) { + if (resourceFilterConfig.workspaceFolderValue) { + target = ConfigurationTarget.WorkspaceFolder; + } else if (resourceFilterConfig.workspaceValue) { + target = ConfigurationTarget.Workspace; + } else if (resourceFilterConfig.globalValue) { + target = ConfigurationTarget.Global; + } + } + await azureConfig.update('resourceFilter', resourceFilter.length ? resourceFilter : undefined, target); + } + + private async updateFilters(configChange = false) { + const azureConfig = workspace.getConfiguration('azure'); + let resourceFilter = azureConfig.get('resourceFilter'); + if (configChange && JSON.stringify(resourceFilter) === this.oldResourceFilter) { + return; + } + this.oldResourceFilter = JSON.stringify(resourceFilter); + if (resourceFilter && !Array.isArray(resourceFilter)) { + resourceFilter = []; + } + const filters = resourceFilter && resourceFilter.map(s => typeof s === 'string' ? s.split('/') : []) + .filter(s => s.length === 2 || s.length === 3) + .map(([tenantId, subscriptionId, resourceGroup]) => ({ tenantId, subscriptionId, resourceGroup })); + const tenantIds = filters && filters.reduce | boolean>>>((result, filter) => { + const tenant = result[filter.tenantId] || (result[filter.tenantId] = {}); + const resourceGroups = tenant[filter.subscriptionId] || (tenant[filter.subscriptionId] = (filter.resourceGroup ? {} : true)); + if (typeof resourceGroups === 'object' && filter.resourceGroup) { + resourceGroups[filter.resourceGroup] = true; + } + return result; + }, {}); + + const newFilters: AzureResourceFilter[] = []; + const sessions = tenantIds ? this.api.sessions.filter(session => tenantIds[session.tenantId]) : this.api.sessions; + for (const session of sessions) { + const client = new SubscriptionClient(session.credentials); + const subscriptionIds = tenantIds && tenantIds[session.tenantId]; + const subscriptions = await listAll(client.subscriptions, client.subscriptions.list()); + const filteredSubscriptions = subscriptionIds ? subscriptions.filter(subscription => subscriptionIds[subscription.subscriptionId!]) : subscriptions; + for (const subscription of filteredSubscriptions) { + const client = new ResourceManagementClient(session.credentials, subscription.subscriptionId!); + const resourceGroupNames = subscriptionIds && subscriptionIds[subscription.subscriptionId!]; + const allResourceGroups = !(resourceGroupNames && typeof resourceGroupNames === 'object'); + const unfilteredResourceGroups = await listAll(client.resourceGroups, client.resourceGroups.list()); + const resourceGroups = allResourceGroups ? unfilteredResourceGroups : unfilteredResourceGroups.filter(resourceGroup => (>resourceGroupNames!)[resourceGroup.name!]); + newFilters.push({ session, subscription, allResourceGroups, resourceGroups }); + } + } + this.api.filters.splice(0, this.api.filters.length, ...newFilters); + this.onFiltersChanged.fire(); + } +} + +async function deviceLogin1(): Promise { + return new Promise((resolve, reject) => { + const cache = new MemoryCache(); + const context = new AuthenticationContext(authorityUrl, null, cache); + context.acquireUserCode(resource, clientId, 'en-us', function (err: any, response: any) { + if (err) { + reject(new AzureLoginError(localize('azure-account.userCodeFailed', "Aquiring user code failed"), err)); + } else { + resolve(response); + } + }); + }); +} + +async function deviceLogin2(deviceLogin: DeviceLogin) { + return new Promise((resolve, reject) => { + const tokenCache = new MemoryCache(); + const context = new AuthenticationContext(authorityUrl, null, tokenCache); + context.acquireTokenWithDeviceCode(resource, clientId, deviceLogin, function (err: any, tokenResponse: TokenResponse) { + if (err) { + reject(new AzureLoginError(localize('azure-account.tokenFailed', "Aquiring token with device code"), err)); + } else { + resolve(tokenResponse); + } + }); + }); +} + +async function tokenFromRefreshToken(refreshToken: string, tenantId = commonTenantId) { + return new Promise((resolve, reject) => { + const tokenCache = new MemoryCache(); + const context = new AuthenticationContext(`${authorityHostUrl}${tenantId}`, null, tokenCache); + context.acquireTokenWithRefreshToken(refreshToken, clientId, null, function (err: any, tokenResponse: TokenResponse) { + if (err) { + reject(new AzureLoginError(localize('azure-account.tokenFromRefreshTokenFailed', "Aquiring token with refresh token"), err)); + } else { + resolve(tokenResponse); + } + }); + }); +} + +async function tokensFromToken(firstTokenResponse: TokenResponse) { + const tokenResponses = [firstTokenResponse]; + const tokenCache = new MemoryCache(); + await addTokenToCache(tokenCache, firstTokenResponse); + const credentials = new DeviceTokenCredentials({ username: firstTokenResponse.userId, clientId, tokenCache }); + const client = new SubscriptionClient(credentials); + const tenants = await listAll(client.tenants, client.tenants.list()); + for (const tenant of tenants) { + if (tenant.tenantId !== firstTokenResponse.tenantId) { + const tokenResponse = await tokenFromRefreshToken(firstTokenResponse.refreshToken, tenant.tenantId); + tokenResponses.push(tokenResponse); + } + } + return tokenResponses; +} + +async function addTokenToCache(tokenCache: any, tokenResponse: TokenResponse) { + return new Promise((resolve, reject) => { + const driver = new CacheDriver( + { _logContext: createLogContext('') }, + `${authorityHostUrl}${tokenResponse.tenantId}`, + tokenResponse.resource, + clientId, + tokenCache, + (entry: any, resource: any, callback: (err: any, response: any) => {}) => { + callback(null, entry); + } + ); + driver.add(tokenResponse, function (err: any) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +} + +async function clearTokenCache(tokenCache: any) { + await new Promise((resolve, reject) => { + tokenCache.find({}, (err: any, entries: any[]) => { + if (err) { + reject(err); + } else { + tokenCache.remove(entries, (err: any) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + } + }); + }); +} + +export interface PartialList extends Array { + nextLink?: string; +} + +export async function listAll(client: { listNext(nextPageLink: string): Promise>; }, first: Promise>): Promise { + const all: T[] = []; + for (let list = await first; list.length || list.nextLink; list = list.nextLink ? await client.listNext(list.nextLink) : []) { + all.push(...list); + } + return all; +} \ No newline at end of file diff --git a/extensions/azure-account/src/extension.ts b/extensions/azure-account/src/extension.ts new file mode 100644 index 00000000000..df46a761df1 --- /dev/null +++ b/extensions/azure-account/src/extension.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { window, ExtensionContext, commands, credentials } from 'vscode'; +import { AzureLoginHelper } from './azure-account'; +import { AzureAccount } from './typings/azure-account.api'; +import * as opn from 'opn'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export function activate(context: ExtensionContext) { + if (!credentials) { + return; // Proposed API not available. + } + const azureLogin = new AzureLoginHelper(context); + const subscriptions = context.subscriptions; + subscriptions.push(createStatusBarItem(azureLogin.api)); + subscriptions.push(commands.registerCommand('azure-account.createAccount', createAccount)); + return azureLogin.api; +} + +function createAccount() { + opn('https://azure.microsoft.com/en-us/free'); +} + +function createStatusBarItem(api: AzureAccount) { + const statusBarItem = window.createStatusBarItem(); + function updateStatusBar() { + switch (api.status) { + case 'LoggingIn': + statusBarItem.text = localize('azure-account.loggingIn', "Azure: Logging in..."); + statusBarItem.show(); + break; + case 'LoggedIn': + statusBarItem.text = localize('azure-account.loggedIn', "Azure: {0}", api.sessions[0].userId); + statusBarItem.show(); + break; + default: + statusBarItem.hide(); + break; + } + } + api.onStatusChanged(updateStatusBar); + api.onSessionsChanged(updateStatusBar); + updateStatusBar(); + return statusBarItem; +} + +export function deactivate() { +} \ No newline at end of file diff --git a/extensions/azure-account/src/typings/azure-account.api.d.ts b/extensions/azure-account/src/typings/azure-account.api.d.ts new file mode 100644 index 00000000000..089511dda45 --- /dev/null +++ b/extensions/azure-account/src/typings/azure-account.api.d.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vscode'; +import { ServiceClientCredentials } from 'ms-rest'; +import { AzureEnvironment } from 'ms-rest-azure'; +import { SubscriptionModels, ResourceModels } from 'azure-arm-resource'; + +export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut'; + +export interface AzureAccount { + readonly status: AzureLoginStatus; + readonly onStatusChanged: Event; + readonly sessions: AzureSession[]; + readonly onSessionsChanged: Event; + readonly filters: AzureResourceFilter[]; + readonly onFiltersChanged: Event; + readonly credentials: Credentials; +} + +export interface AzureSession { + readonly environment: AzureEnvironment; + readonly userId: string; + readonly tenantId: string; + readonly credentials: ServiceClientCredentials; +} + +export interface AzureResourceFilter { + readonly session: AzureSession; + readonly subscription: SubscriptionModels.Subscription; + readonly allResourceGroups: boolean; + readonly resourceGroups: ResourceModels.ResourceGroup[]; +} + +export interface Credentials { + readSecret(service: string, account: string): Thenable; + writeSecret(service: string, account: string, secret: string): Thenable; + deleteSecret(service: string, account: string): Thenable; +} diff --git a/extensions/azure-account/src/typings/ref.d.ts b/extensions/azure-account/src/typings/ref.d.ts new file mode 100644 index 00000000000..216911a680e --- /dev/null +++ b/extensions/azure-account/src/typings/ref.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// diff --git a/extensions/azure-account/src/typings/vscode.rejected.d.ts b/extensions/azure-account/src/typings/vscode.rejected.d.ts new file mode 100644 index 00000000000..7f6f64df560 --- /dev/null +++ b/extensions/azure-account/src/typings/vscode.rejected.d.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// This is the place for API experiments and proposal. + +declare module 'vscode' { + + /** + * Namespace for handling credentials. + */ + export namespace credentials { + + /** + * Read a previously stored secret from the credential store. + * + * @param service The service of the credential. + * @param account The account of the credential. + * @return A promise for the secret of the credential. + */ + export function readSecret(service: string, account: string): Thenable; + + /** + * Write a secret to the credential store. + * + * @param service The service of the credential. + * @param account The account of the credential. + * @param secret The secret of the credential to write to the credential store. + * @return A promise indicating completion of the operation. + */ + export function writeSecret(service: string, account: string, secret: string): Thenable; + + /** + * Delete a previously stored secret from the credential store. + * + * @param service The service of the credential. + * @param account The account of the credential. + * @return A promise resolving to true if there was a secret for that service and account. + */ + export function deleteSecret(service: string, account: string): Thenable; + } +} diff --git a/extensions/azure-account/tsconfig.json b/extensions/azure-account/tsconfig.json new file mode 100644 index 00000000000..61265d449a4 --- /dev/null +++ b/extensions/azure-account/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "strict": true, + "noUnusedLocals": true, + "outDir": "./out", + "lib": [ + "es6" + ], + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/css/client/src/cssMain.ts b/extensions/css/client/src/cssMain.ts index 1080628cad4..a13dabac0b9 100644 --- a/extensions/css/client/src/cssMain.ts +++ b/extensions/css/client/src/cssMain.ts @@ -9,6 +9,7 @@ import * as path from 'path'; import { languages, window, commands, workspace, ExtensionContext } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, Range, TextEdit } from 'vscode-languageclient'; import { activateColorDecorations, ColorProvider } from './colorDecorators'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -23,7 +24,7 @@ export function activate(context: ExtensionContext) { // The server is implemented in node let serverModule = context.asAbsolutePath(path.join('server', 'out', 'cssServerMain.js')); // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] }; + let debugOptions = { execArgv: ['--nolazy', '--inspect=6004'] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used @@ -44,6 +45,7 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); + client.registerFeature(new ConfigurationFeature(client)); let disposable = client.start(); // Push the disposable to the context's subscriptions so that the diff --git a/extensions/css/npm-shrinkwrap.json b/extensions/css/npm-shrinkwrap.json index 89c99f09cf0..6a497e0b691 100644 --- a/extensions/css/npm-shrinkwrap.json +++ b/extensions/css/npm-shrinkwrap.json @@ -13,19 +13,19 @@ "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz" }, "vscode-jsonrpc": { - "version": "3.2.0", - "from": "vscode-jsonrpc@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.2.0.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageclient": { - "version": "3.2.0", - "from": "vscode-languageclient@3.2.0", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.2.0.tgz" + "version": "3.4.0-next.10", + "from": "vscode-languageclient@next", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.10.tgz" }, "vscode-languageserver-types": { - "version": "3.2.0", - "from": "vscode-languageserver-types@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/css/package.json b/extensions/css/package.json index ff0bf9ac994..49b8f98ba2f 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -62,16 +62,19 @@ "properties": { "css.validate": { "type": "boolean", + "scope": "resource", "default": true, "description": "%css.validate.desc%" }, "css.colorDecorators.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%css.colorDecorators.enable.desc%" }, "css.lint.compatibleVendorPrefixes": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -82,6 +85,7 @@ }, "css.lint.vendorPrefix": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -92,6 +96,7 @@ }, "css.lint.duplicateProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -102,6 +107,7 @@ }, "css.lint.emptyRules": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -112,6 +118,7 @@ }, "css.lint.importStatement": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -122,6 +129,7 @@ }, "css.lint.boxModel": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -132,6 +140,7 @@ }, "css.lint.universalSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -142,6 +151,7 @@ }, "css.lint.zeroUnits": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -152,6 +162,7 @@ }, "css.lint.fontFaceProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -162,6 +173,7 @@ }, "css.lint.hexColorLength": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -172,6 +184,7 @@ }, "css.lint.argumentsInColorFunction": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -182,6 +195,7 @@ }, "css.lint.unknownProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -192,6 +206,7 @@ }, "css.lint.ieHack": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -202,6 +217,7 @@ }, "css.lint.unknownVendorSpecificProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -212,6 +228,7 @@ }, "css.lint.propertyIgnoredDueToDisplay": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -222,6 +239,7 @@ }, "css.lint.important": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -232,6 +250,7 @@ }, "css.lint.float": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -242,6 +261,7 @@ }, "css.lint.idSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -252,6 +272,7 @@ }, "css.trace.server": { "type": "string", + "scope": "window", "enum": [ "off", "messages", @@ -274,16 +295,19 @@ "properties": { "scss.validate": { "type": "boolean", + "scope": "resource", "default": true, "description": "%scss.validate.desc%" }, "scss.colorDecorators.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%scss.colorDecorators.enable.desc%" }, "scss.lint.compatibleVendorPrefixes": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -294,6 +318,7 @@ }, "scss.lint.vendorPrefix": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -304,6 +329,7 @@ }, "scss.lint.duplicateProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -314,6 +340,7 @@ }, "scss.lint.emptyRules": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -324,6 +351,7 @@ }, "scss.lint.importStatement": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -334,6 +362,7 @@ }, "scss.lint.boxModel": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -344,6 +373,7 @@ }, "scss.lint.universalSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -354,6 +384,7 @@ }, "scss.lint.zeroUnits": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -364,6 +395,7 @@ }, "scss.lint.fontFaceProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -374,6 +406,7 @@ }, "scss.lint.hexColorLength": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -384,6 +417,7 @@ }, "scss.lint.argumentsInColorFunction": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -394,6 +428,7 @@ }, "scss.lint.unknownProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -404,6 +439,7 @@ }, "scss.lint.ieHack": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -414,6 +450,7 @@ }, "scss.lint.unknownVendorSpecificProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -424,6 +461,7 @@ }, "scss.lint.propertyIgnoredDueToDisplay": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -434,6 +472,7 @@ }, "scss.lint.important": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -444,6 +483,7 @@ }, "scss.lint.float": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -454,6 +494,7 @@ }, "scss.lint.idSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -477,16 +518,19 @@ "properties": { "less.validate": { "type": "boolean", + "scope": "resource", "default": true, "description": "%less.validate.desc%" }, "less.colorDecorators.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%less.colorDecorators.enable.desc%" }, "less.lint.compatibleVendorPrefixes": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -497,6 +541,7 @@ }, "less.lint.vendorPrefix": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -507,6 +552,7 @@ }, "less.lint.duplicateProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -517,6 +563,7 @@ }, "less.lint.emptyRules": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -527,6 +574,7 @@ }, "less.lint.importStatement": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -537,6 +585,7 @@ }, "less.lint.boxModel": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -547,6 +596,7 @@ }, "less.lint.universalSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -557,6 +607,7 @@ }, "less.lint.zeroUnits": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -567,6 +618,7 @@ }, "less.lint.fontFaceProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -577,6 +629,7 @@ }, "less.lint.hexColorLength": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -587,6 +640,7 @@ }, "less.lint.argumentsInColorFunction": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -597,6 +651,7 @@ }, "less.lint.unknownProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -607,6 +662,7 @@ }, "less.lint.ieHack": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -617,6 +673,7 @@ }, "less.lint.unknownVendorSpecificProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -627,6 +684,7 @@ }, "less.lint.propertyIgnoredDueToDisplay": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -637,6 +695,7 @@ }, "less.lint.important": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -647,6 +706,7 @@ }, "less.lint.float": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -657,6 +717,7 @@ }, "less.lint.idSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -674,10 +735,10 @@ }, "dependencies": { "parse-color": "^1.0.0", - "vscode-languageclient": "^3.2.0", + "vscode-languageclient": "3.4.0-next.10", "vscode-nls": "^2.0.2" }, "devDependencies": { "@types/node": "^6.0.51" } -} \ No newline at end of file +} diff --git a/extensions/css/server/npm-shrinkwrap.json b/extensions/css/server/npm-shrinkwrap.json index 21329db6203..63bed9e999b 100644 --- a/extensions/css/server/npm-shrinkwrap.json +++ b/extensions/css/server/npm-shrinkwrap.json @@ -3,29 +3,34 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.2", + "version": "2.1.3", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.2.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.3.tgz" }, "vscode-jsonrpc": { - "version": "3.2.0", - "from": "vscode-jsonrpc@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.2.0.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageserver": { - "version": "3.2.0", - "from": "vscode-languageserver@3.2.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.2.0.tgz" + "version": "3.4.0-next.4", + "from": "vscode-languageserver@next", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.4.tgz" }, "vscode-languageserver-types": { - "version": "3.2.0", - "from": "vscode-languageserver-types@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", "from": "vscode-nls@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "vscode-uri": { + "version": "1.0.1", + "from": "vscode-uri@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz" } } } diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json index c7b93d1573f..d8112cc044a 100644 --- a/extensions/css/server/package.json +++ b/extensions/css/server/package.json @@ -8,8 +8,8 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.2", - "vscode-languageserver": "^3.2.0" + "vscode-css-languageservice": "^2.1.3", + "vscode-languageserver": "3.4.0-next.4" }, "devDependencies": { "@types/node": "^6.0.51" diff --git a/extensions/css/server/src/cssServerMain.ts b/extensions/css/server/src/cssServerMain.ts index 573b4b61c5f..4b5c4e4ad2c 100644 --- a/extensions/css/server/src/cssServerMain.ts +++ b/extensions/css/server/src/cssServerMain.ts @@ -8,6 +8,7 @@ import { createConnection, IConnection, Range, TextDocuments, TextDocument, InitializeParams, InitializeResult, RequestType } from 'vscode-languageserver'; +import { GetConfigurationRequest } from 'vscode-languageserver/lib/protocol.proposed'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -43,10 +44,20 @@ connection.onShutdown(() => { stylesheets.dispose(); }); +let scopedSettingsSupport = false; // After the server has started the client sends an initilize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { - let snippetSupport = params.capabilities && params.capabilities.textDocument && params.capabilities.textDocument.completion && params.capabilities.textDocument.completion.completionItem && params.capabilities.textDocument.completion.completionItem.snippetSupport; + function hasClientCapability(name: string) { + let keys = name.split('.'); + let c = params.capabilities; + for (let i = 0; c && i < keys.length; i++) { + c = c[keys[i]]; + } + return !!c; + } + let snippetSupport = hasClientCapability('textDocument.completion.completionItem.snippetSupport'); + scopedSettingsSupport = hasClientCapability('workspace.configuration'); return { capabilities: { // Tell the client that the server works in FULL text document sync mode @@ -78,6 +89,20 @@ function getLanguageService(document: TextDocument) { return service; } +let documentSettings: { [key: string]: Thenable } = {}; +function getDocumentSettings(textDocument: TextDocument): Thenable { + if (scopedSettingsSupport) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + let configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; + promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => s[0]); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return void 0; +} + // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration(change => { updateConfiguration(change.settings); @@ -87,6 +112,8 @@ function updateConfiguration(settings: Settings) { for (let languageId in languageServices) { languageServices[languageId].configure(settings[languageId]); } + // reset all document settings + documentSettings = {}; // Revalidate any open text documents documents.all().forEach(triggerValidation); } @@ -123,10 +150,13 @@ function triggerValidation(textDocument: TextDocument): void { } function validateTextDocument(textDocument: TextDocument): void { + let settingsPromise = getDocumentSettings(textDocument); let stylesheet = stylesheets.get(textDocument); - let diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet); - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + settingsPromise.then(settings => { + let diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + }); } connection.onCompletion(textDocumentPosition => { diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index 68b2886e6d4..d4dd5915a17 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -4,8 +4,8 @@ "dependencies": { "@emmetio/css-parser": { "version": "0.4.0", - "from": "@emmetio/css-parser@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.0.tgz" + "from": "ramya-rao-a/css-parser#vscode", + "resolved": "git://github.com/ramya-rao-a/css-parser.git#370c480ac103bd17c7bcfb34bf5d577dc40d3660" }, "@emmetio/extract-abbreviation": { "version": "0.1.3", @@ -38,9 +38,9 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" }, "vscode-emmet-helper": { - "version": "1.0.16", - "from": "vscode-emmet-helper@>=1.0.16 <2.0.0", - "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.0.16.tgz" + "version": "1.0.17", + "from": "vscode-emmet-helper@>=1.0.17 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.0.17.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index f9266ef067a..7bf1a1213b9 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -210,9 +210,9 @@ "dependencies": { "@emmetio/html-matcher": "^0.3.1", - "@emmetio/css-parser": "^0.4.0", + "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/math-expression": "^0.1.1", - "vscode-emmet-helper": "^1.0.16", + "vscode-emmet-helper": "^1.0.17", "vscode-languageserver-types": "^3.0.3", "image-size": "^0.5.2", "vscode-nls": "2.0.2" diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 399d3807833..624a486729c 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -95,10 +95,7 @@ export function expandEmmetAbbreviation(args): Thenable { const editor = vscode.window.activeTextEditor; - let rootNode = parseDocument(editor.document); - if (!rootNode) { - return fallbackTab(); - } + let rootNode = parseDocument(editor.document, false); // When tabbed on a non empty selection, do not treat it as an emmet abbreviation, and fallback to tab instead if (vscode.workspace.getConfiguration('emmet')['triggerExpansionOnTab'] === true && editor.selections.find(x => !x.isEmpty)) { diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index 28adbadfb7a..cadfd8ef380 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -72,9 +72,13 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi const currentHtmlNode = currentNode; if (currentHtmlNode && currentHtmlNode.close - && currentHtmlNode.name === 'style' && getInnerRange(currentHtmlNode).contains(position)) { - return 'css'; + if (currentHtmlNode.name === 'style') { + return 'css'; + } + if (currentHtmlNode.name === 'script') { + return; + } } } diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 3eee4a0dba9..1f6d03e2458 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -16,7 +16,7 @@ export const LANGUAGE_MODES: Object = { 'slim': ['!', '.', '}', ':', '*', '$'], 'haml': ['!', '.', '}', ':', '*', '$'], 'xml': ['.', '}', '*', '$'], - 'xsl': ['.', '}', '*', '$'], + 'xsl': ['!', '.', '}', '*', '$'], 'css': [':'], 'scss': [':'], 'sass': [':'], @@ -83,6 +83,10 @@ export function parseDocument(document: vscode.TextDocument, showError: boolean * @param includeNodeBoundary */ export function getNode(root: Node, position: vscode.Position, includeNodeBoundary: boolean = false) { + if (!root) { + return null; + } + let currentNode = root.firstChild; let foundNode: Node = null; diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index c0526029e91..ccba5abd79b 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,7 +5,7 @@ 'use strict'; -import { Uri, commands, scm, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn } from 'vscode'; +import { Uri, commands, scm, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation } from 'vscode'; import { Ref, RefType, Git, GitErrorCodes, Branch } from './git'; import { Model, Resource, Status, CommitOptions, WorkingTreeGroup, IndexGroup, MergeGroup } from './model'; import { toGitUri, fromGitUri } from './uri'; @@ -278,9 +278,12 @@ export class CommandCenter { } const clonePromise = this.git.clone(url, parentPath); - window.setStatusBarMessage(localize('cloning', "Cloning git repository..."), clonePromise); + try { + window.withProgress({ location: ProgressLocation.SourceControl, title: localize('cloning', "Cloning git repository...") }, () => clonePromise); + window.withProgress({ location: ProgressLocation.Window, title: localize('cloning', "Cloning git repository...") }, () => clonePromise); + const repositoryPath = await clonePromise; const open = localize('openrepo', "Open Repository"); diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts index 7f4c9fe3440..82a1d0ffc59 100644 --- a/extensions/html/client/src/htmlMain.ts +++ b/extensions/html/client/src/htmlMain.ts @@ -12,6 +12,8 @@ import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateColorDecorations } from './colorDecorators'; import TelemetryReporter from 'vscode-extension-telemetry'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; + import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -34,7 +36,7 @@ export function activate(context: ExtensionContext) { // The server is implemented in node let serverModule = context.asAbsolutePath(path.join('server', 'out', 'htmlServerMain.js')); // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] }; + let debugOptions = { execArgv: ['--nolazy', '--inspect=6004'] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used @@ -59,6 +61,8 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions); + client.registerFeature(new ConfigurationFeature(client)); + let disposable = client.start(); context.subscriptions.push(disposable); client.onReady().then(() => { diff --git a/extensions/html/npm-shrinkwrap.json b/extensions/html/npm-shrinkwrap.json index 7fee667332b..9dc0cf34176 100644 --- a/extensions/html/npm-shrinkwrap.json +++ b/extensions/html/npm-shrinkwrap.json @@ -8,24 +8,24 @@ "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" }, "vscode-extension-telemetry": { - "version": "0.0.7", + "version": "0.0.8", "from": "vscode-extension-telemetry@>=0.0.8 <0.0.9", "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" }, "vscode-jsonrpc": { - "version": "3.1.0-alpha.1", - "from": "vscode-jsonrpc@>=3.1.0-alpha.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.1.0-alpha.1.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageclient": { - "version": "3.1.0-alpha.1", + "version": "3.4.0-next.10", "from": "vscode-languageclient@next", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.1.0-alpha.1.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.10.tgz" }, "vscode-languageserver-types": { - "version": "3.0.3", - "from": "vscode-languageserver-types@>=3.0.2-beta.5 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.0.3.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", @@ -38,4 +38,4 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" } } -} \ No newline at end of file +} diff --git a/extensions/html/package.json b/extensions/html/package.json index f79ac361538..a06789f08a2 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -75,11 +75,13 @@ "properties": { "html.format.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%html.format.enable.desc%" }, "html.format.wrapLineLength": { "type": "integer", + "scope": "resource", "default": 120, "description": "%html.format.wrapLineLength.desc%" }, @@ -88,6 +90,7 @@ "string", "null" ], + "scope": "resource", "default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var", "description": "%html.format.unformatted.desc%" }, @@ -96,16 +99,19 @@ "string", "null" ], + "scope": "resource", "default": "pre", "description": "%html.format.contentUnformatted.desc%" }, "html.format.indentInnerHtml": { "type": "boolean", + "scope": "resource", "default": false, "description": "%html.format.indentInnerHtml.desc%" }, "html.format.preserveNewLines": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.format.preserveNewLines.desc%" }, @@ -114,16 +120,19 @@ "number", "null" ], + "scope": "resource", "default": null, "description": "%html.format.maxPreserveNewLines.desc%" }, "html.format.indentHandlebars": { "type": "boolean", + "scope": "resource", "default": false, "description": "%html.format.indentHandlebars.desc%" }, "html.format.endWithNewline": { "type": "boolean", + "scope": "resource", "default": false, "description": "%html.format.endWithNewline.desc%" }, @@ -132,11 +141,13 @@ "string", "null" ], + "scope": "resource", "default": "head, body, /html", "description": "%html.format.extraLiners.desc%" }, "html.format.wrapAttributes": { "type": "string", + "scope": "resource", "default": "auto", "enum": [ "auto", @@ -154,31 +165,37 @@ }, "html.suggest.angular1": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.suggest.angular1.desc%" }, "html.suggest.ionic": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.suggest.ionic.desc%" }, "html.suggest.html5": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.suggest.html5.desc%" }, "html.validate.scripts": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.validate.scripts%" }, "html.validate.styles": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.validate.styles%" }, "html.trace.server": { "type": "string", + "scope": "window", "enum": [ "off", "messages", @@ -192,12 +209,12 @@ }, "dependencies": { "vscode-extension-telemetry": "0.0.8", - "vscode-languageclient": "3.1.0-alpha.1", - "vscode-languageserver-types": "3.0.3", + "vscode-languageclient": "3.4.0-next.10", + "vscode-languageserver-types": "^3.3.0", "vscode-nls": "2.0.2" }, "devDependencies": { "@types/node": "^6.0.51", "@types/mocha": "^2.2.33" } -} \ No newline at end of file +} diff --git a/extensions/html/server/npm-shrinkwrap.json b/extensions/html/server/npm-shrinkwrap.json index ee83f51e71e..8853dfc48b4 100644 --- a/extensions/html/server/npm-shrinkwrap.json +++ b/extensions/html/server/npm-shrinkwrap.json @@ -3,16 +3,9 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.0", + "version": "2.1.3", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.0.tgz", - "dependencies": { - "vscode-languageserver-types": { - "version": "3.2.0", - "from": "vscode-languageserver-types@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz" - } - } + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.3.tgz" }, "vscode-html-languageservice": { "version": "2.0.5", @@ -20,19 +13,19 @@ "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.5.tgz" }, "vscode-jsonrpc": { - "version": "3.1.0-alpha.1", - "from": "vscode-jsonrpc@>=3.1.0-alpha.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.1.0-alpha.1.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageserver": { - "version": "3.1.0-alpha.1", + "version": "3.4.0-next.4", "from": "vscode-languageserver@next", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.1.0-alpha.1.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.4.tgz" }, "vscode-languageserver-types": { - "version": "3.0.3", - "from": "vscode-languageserver-types@>=3.0.3 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.0.3.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", @@ -40,9 +33,9 @@ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" }, "vscode-uri": { - "version": "1.0.0", - "from": "vscode-uri@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.0.tgz" + "version": "1.0.1", + "from": "vscode-uri@latest", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz" } } } diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json index 5284d547143..3fe6263139d 100644 --- a/extensions/html/server/package.json +++ b/extensions/html/server/package.json @@ -8,11 +8,12 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.0", + "vscode-css-languageservice": "^2.1.3", "vscode-html-languageservice": "^2.0.5", - "vscode-languageserver": "^3.1.0-alpha.1", + "vscode-languageserver": "3.4.0-next.4", + "vscode-languageserver-types": "^3.3.0", "vscode-nls": "^2.0.2", - "vscode-uri": "^1.0.0" + "vscode-uri": "^1.0.1" }, "devDependencies": { "@types/node": "^6.0.51", @@ -25,6 +26,6 @@ "install-service-local": "npm install ../../../../vscode-css-languageservice -f -S && npm install ../../../../vscode-html-languageservice -f -S", "install-server-next": "npm install vscode-languageserver@next -f -S", "install-server-local": "npm install ../../../../vscode-languageserver-node/server -f -S", - "test": "mocha" + "test": "../../../node_modules/.bin/mocha" } } diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 0ea5b627424..c29012bd8b1 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -4,10 +4,12 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector } from 'vscode-languageserver'; +import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, GetConfigurationParams } from 'vscode-languageserver'; import { DocumentContext } from 'vscode-html-languageservice'; import { TextDocument, Diagnostic, DocumentLink, Range, SymbolInformation } from 'vscode-languageserver-types'; -import { getLanguageModes, LanguageModes } from './modes/languageModes'; +import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; + +import { GetConfigurationRequest } from 'vscode-languageserver/lib/protocol.proposed'; import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; @@ -38,10 +40,27 @@ documents.listen(connection); let workspacePath: string; var languageModes: LanguageModes; -var settings: any = {}; let clientSnippetSupport = false; let clientDynamicRegisterSupport = false; +let scopedSettingsSupport = false; + +var globalSettings: Settings = {}; +let documentSettings: { [key: string]: Thenable } = {}; + +function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: () => boolean): Thenable { + if (scopedSettingsSupport && needsDocumentSettings()) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + let scopeUri = textDocument.uri; + let configRequestParam: GetConfigurationParams = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] }; + promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => ({ css: s[0], html: s[1], javascript: s[2] })); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return Promise.resolve(void 0); +} // After the server has started the client sends an initilize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilites @@ -68,6 +87,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport'); clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration'); + scopedSettingsSupport = hasClientCapability('workspace', 'configuration'); return { capabilities: { // Tell the client that the server works in FULL text document sync mode @@ -85,21 +105,13 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { }; }); -let validation = { - html: true, - css: true, - javascript: true -}; - let formatterRegistration: Thenable = null; // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { - settings = change.settings; - let validationSettings = settings && settings.html && settings.html.validate || {}; - validation.css = validationSettings.styles !== false; - validation.javascript = validationSettings.scripts !== false; + globalSettings = change.settings; + documentSettings = {}; // reset all document settings languageModes.getAllModes().forEach(m => { if (m.configure) { m.configure(change.settings); @@ -109,7 +121,7 @@ connection.onDidChangeConfiguration((change) => { // dynamically enable & disable the formatter if (clientDynamicRegisterSupport) { - let enableFormatter = settings && settings.html && settings.html.format && settings.html.format.enable; + let enableFormatter = globalSettings && globalSettings.html && globalSettings.html.format && globalSettings.html.format.enable; if (enableFormatter) { if (!formatterRegistration) { let documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }]; // don't register razor, the formatter does more harm than good @@ -154,26 +166,37 @@ function triggerValidation(textDocument: TextDocument): void { }, validationDelayMs); } -function validateTextDocument(textDocument: TextDocument): void { +function isValidationEnabled(languageId: string, settings: Settings = globalSettings) { + let validationSettings = settings && settings.html && settings.html.validate; + if (validationSettings) { + return languageId === 'css' && validationSettings.styles !== false || languageId === 'javascript' && validationSettings.scripts !== false; + } + return true; +} + +async function validateTextDocument(textDocument: TextDocument) { let diagnostics: Diagnostic[] = []; if (textDocument.languageId === 'html') { - languageModes.getAllModesInDocument(textDocument).forEach(mode => { - if (mode.doValidation && validation[mode.getId()]) { - pushAll(diagnostics, mode.doValidation(textDocument)); + let modes = languageModes.getAllModesInDocument(textDocument); + let settings = await getDocumentSettings(textDocument, () => modes.some(m => m.doValidation && m.doValidation.length > 1)); + modes.forEach(mode => { + if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) { + pushAll(diagnostics, mode.doValidation(textDocument, settings)); } }); } connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } -connection.onCompletion(textDocumentPosition => { +connection.onCompletion(async textDocumentPosition => { let document = documents.get(textDocumentPosition.textDocument.uri); let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); if (mode && mode.doComplete) { if (mode.getId() !== 'html') { connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } }); } - return mode.doComplete(document, textDocumentPosition.position); + let settings = await getDocumentSettings(document, () => mode.doComplete.length > 2); + return mode.doComplete(document, textDocumentPosition.position, settings); } return { isIncomplete: true, items: [] }; }); @@ -235,13 +258,16 @@ connection.onSignatureHelp(signatureHelpParms => { return null; }); -connection.onDocumentRangeFormatting(formatParams => { +connection.onDocumentRangeFormatting(async formatParams => { let document = documents.get(formatParams.textDocument.uri); - + let settings = await getDocumentSettings(document, () => true); + if (!settings) { + settings = globalSettings; + } let unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || ''; let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) }; - return format(languageModes, document, formatParams.range, formatParams.options, enabledModes); + return format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes); }); connection.onDocumentLinks(documentLinkParam => { diff --git a/extensions/html/server/src/modes/cssMode.ts b/extensions/html/server/src/modes/cssMode.ts index b813c59f572..bcaa48391d9 100644 --- a/extensions/html/server/src/modes/cssMode.ts +++ b/extensions/html/server/src/modes/cssMode.ts @@ -7,7 +7,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { TextDocument, Position } from 'vscode-languageserver-types'; import { getCSSLanguageService, Stylesheet } from 'vscode-css-languageservice'; -import { LanguageMode } from './languageModes'; +import { LanguageMode, Settings } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; export function getCSSMode(documentRegions: LanguageModelCache): LanguageMode { @@ -22,9 +22,9 @@ export function getCSSMode(documentRegions: LanguageModelCache(10, 60, document => htmlLanguageService.parseHTMLDocument(document)); return { getId() { return 'html'; }, configure(options: any) { - settings = options && options.html; + globalSettings = options; }, - doComplete(document: TextDocument, position: Position) { - let options = settings && settings.suggest; + doComplete(document: TextDocument, position: Position, settings: Settings = globalSettings) { + let options = settings && settings.html && settings.html.suggest; return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options); }, doHover(document: TextDocument, position: Position) { @@ -35,8 +35,8 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageM findDocumentSymbols(document: TextDocument) { return htmlLanguageService.findDocumentSymbols(document, htmlDocuments.get(document)); }, - format(document: TextDocument, range: Range, formatParams: FormattingOptions) { - let formatSettings = settings && settings.format; + format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings) { + let formatSettings = settings && settings.html && settings.html.format; if (!formatSettings) { formatSettings = formatParams; } else { diff --git a/extensions/html/server/src/modes/javascriptMode.ts b/extensions/html/server/src/modes/javascriptMode.ts index b9cd9075b20..128969d0140 100644 --- a/extensions/html/server/src/modes/javascriptMode.ts +++ b/extensions/html/server/src/modes/javascriptMode.ts @@ -6,7 +6,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types'; -import { LanguageMode } from './languageModes'; +import { LanguageMode, Settings } from './languageModes'; import { getWordAtText, startsWith, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; @@ -60,14 +60,14 @@ export function getJavascriptMode(documentRegions: LanguageModelCache; +} + export interface LanguageMode { getId(); - configure?: (options: any) => void; - doValidation?: (document: TextDocument) => Diagnostic[]; - doComplete?: (document: TextDocument, position: Position) => CompletionList; + configure?: (options: Settings) => void; + doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; + doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; doHover?: (document: TextDocument, position: Position) => Hover; doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp; @@ -29,7 +39,7 @@ export interface LanguageMode { findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[]; findDefinition?: (document: TextDocument, position: Position) => Definition; findReferences?: (document: TextDocument, position: Position) => Location[]; - format?: (document: TextDocument, range: Range, options: FormattingOptions) => TextEdit[]; + format?: (document: TextDocument, range: Range, options: FormattingOptions, settings: Settings) => TextEdit[]; findColorSymbols?: (document: TextDocument) => Range[]; onDocumentRemoved(document: TextDocument): void; dispose(): void; diff --git a/extensions/html/server/src/test/formatting.test.ts b/extensions/html/server/src/test/formatting.test.ts index ea4ecf33153..713d2943ed4 100644 --- a/extensions/html/server/src/test/formatting.test.ts +++ b/extensions/html/server/src/test/formatting.test.ts @@ -38,7 +38,7 @@ suite('HTML Embedded Formatting', () => { formatOptions = FormattingOptions.create(2, true); } - let result = format(languageModes, document, range, formatOptions, { css: true, javascript: true }); + let result = format(languageModes, document, range, formatOptions, void 0, { css: true, javascript: true }); let actual = applyEdits(document, result); assert.equal(actual, expected, message); diff --git a/extensions/html/server/src/test/javascriptMode.test.ts b/extensions/html/server/src/test/javascriptMode.test.ts index 8b231644ee5..c5d44e88cae 100644 --- a/extensions/html/server/src/test/javascriptMode.test.ts +++ b/extensions/html/server/src/test/javascriptMode.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { getJavascriptMode } from '../modes/javascriptMode'; -import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types'; +import { TextDocument } from 'vscode-languageserver-types'; import { getLanguageModelCache } from '../languageModelCache'; import { getLanguageService } from 'vscode-html-languageservice'; diff --git a/extensions/markdown/syntaxes/gulpfile.js b/extensions/markdown/syntaxes/gulpfile.js index 6bd6851cb01..e04c73fe1d1 100644 --- a/extensions/markdown/syntaxes/gulpfile.js +++ b/extensions/markdown/syntaxes/gulpfile.js @@ -68,7 +68,7 @@ const fencedCodeBlockDefinition = (name, identifiers, sourceScope) => { return `fenced_code_block_${name} begin - (^|\\G)(\\s*)(\`{3,}|~{3,})\\s*((${identifiers.join('|')})(\\s+[^\`~]*)?$) + (^|\\G)(\\s*)(\`{3,}|~{3,})\\s*(?i:(${identifiers.join('|')})(\\s+[^\`~]*)?$) name markup.fenced_code.block.markdown end diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index ede4f2a2d18..552aec21007 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -588,7 +588,7 @@ fenced_code_block_css begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((css|css.erb)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(css|css.erb)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -639,7 +639,7 @@ fenced_code_block_basic begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -690,7 +690,7 @@ fenced_code_block_ini begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((ini|conf)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(ini|conf)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -741,7 +741,7 @@ fenced_code_block_java begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((java|bsh)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(java|bsh)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -792,7 +792,7 @@ fenced_code_block_lua begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((lua)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(lua)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -843,7 +843,7 @@ fenced_code_block_makefile begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -894,7 +894,7 @@ fenced_code_block_perl begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((perl|pl|pm|pod|t|PL|psgi|vcl)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(perl|pl|pm|pod|t|PL|psgi|vcl)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -945,7 +945,7 @@ fenced_code_block_r begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((R|r|s|S|Rprofile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(R|r|s|S|Rprofile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -996,7 +996,7 @@ fenced_code_block_ruby begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1047,7 +1047,7 @@ fenced_code_block_php begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1102,7 +1102,7 @@ fenced_code_block_sql begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((sql|ddl|dml)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(sql|ddl|dml)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1153,7 +1153,7 @@ fenced_code_block_vs_net begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((vb)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(vb)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1204,7 +1204,7 @@ fenced_code_block_xml begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1255,7 +1255,7 @@ fenced_code_block_xsl begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((xsl|xslt)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(xsl|xslt)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1306,7 +1306,7 @@ fenced_code_block_yaml begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((yaml|yml)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(yaml|yml)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1357,7 +1357,7 @@ fenced_code_block_dosbatch begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((bat|batch)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(bat|batch)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1408,7 +1408,7 @@ fenced_code_block_clojure begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((clj|cljs|clojure)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(clj|cljs|clojure)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1459,7 +1459,7 @@ fenced_code_block_coffee begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((coffee|Cakefile|coffee.erb)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(coffee|Cakefile|coffee.erb)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1510,7 +1510,7 @@ fenced_code_block_c begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((c|h)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(c|h)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1561,7 +1561,7 @@ fenced_code_block_cpp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((cpp|c\+\+|cxx)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(cpp|c\+\+|cxx)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1612,7 +1612,7 @@ fenced_code_block_diff begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((patch|diff|rej)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(patch|diff|rej)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1663,7 +1663,7 @@ fenced_code_block_dockerfile begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((dockerfile|Dockerfile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(dockerfile|Dockerfile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1714,7 +1714,7 @@ fenced_code_block_git_commit begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((COMMIT_EDITMSG|MERGE_MSG)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(COMMIT_EDITMSG|MERGE_MSG)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1765,7 +1765,7 @@ fenced_code_block_git_rebase begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((git-rebase-todo)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(git-rebase-todo)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1816,7 +1816,7 @@ fenced_code_block_go begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((go|golang)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(go|golang)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1867,7 +1867,7 @@ fenced_code_block_groovy begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((groovy|gvy)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(groovy|gvy)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1918,7 +1918,7 @@ fenced_code_block_jade begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((jade|pug)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(jade|pug)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1969,7 +1969,7 @@ fenced_code_block_js begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((js|jsx|javascript|es6|mjs)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(js|jsx|javascript|es6|mjs)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2020,7 +2020,7 @@ fenced_code_block_js_regexp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((regexp)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(regexp)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2071,7 +2071,7 @@ fenced_code_block_json begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2122,7 +2122,7 @@ fenced_code_block_less begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((less)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(less)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2173,7 +2173,7 @@ fenced_code_block_objc begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((objectivec|objective-c|mm|objc|obj-c|m|h)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(objectivec|objective-c|mm|objc|obj-c|m|h)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2224,7 +2224,7 @@ fenced_code_block_scss begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((scss)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(scss)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2275,7 +2275,7 @@ fenced_code_block_perl6 begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((perl6|p6|pl6|pm6|nqp)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(perl6|p6|pl6|pm6|nqp)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2326,7 +2326,7 @@ fenced_code_block_powershell begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((powershell|ps1|psm1|psd1)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(powershell|ps1|psm1|psd1)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2377,7 +2377,7 @@ fenced_code_block_python begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2428,7 +2428,7 @@ fenced_code_block_regexp_python begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((re)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(re)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2479,7 +2479,7 @@ fenced_code_block_rust begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((rust|rs)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(rust|rs)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2530,7 +2530,7 @@ fenced_code_block_scala begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((scala|sbt)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(scala|sbt)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2581,7 +2581,7 @@ fenced_code_block_shell begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2632,7 +2632,7 @@ fenced_code_block_ts begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((typescript|ts)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(typescript|ts)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2683,7 +2683,7 @@ fenced_code_block_tsx begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((tsx)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(tsx)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2734,7 +2734,7 @@ fenced_code_block_csharp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((cs|csharp|c#)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(cs|csharp|c#)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2785,7 +2785,7 @@ fenced_code_block_fsharp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((fs|fsharp|f#)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(fs|fsharp|f#)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end diff --git a/extensions/npm-shrinkwrap.json b/extensions/npm-shrinkwrap.json index 4ffcfc354c6..149c711b4e7 100644 --- a/extensions/npm-shrinkwrap.json +++ b/extensions/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "0.0.1", "dependencies": { "typescript": { - "version": "2.4.2", - "from": "typescript@2.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz" + "version": "2.5.1-insiders.20170818", + "from": "typescript@2.5.1-insiders.20170818", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.1-insiders.20170818.tgz" } } } diff --git a/extensions/package.json b/extensions/package.json index 834e7ca2131..102d1ef27d5 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "2.4.2" + "typescript": "2.5.1-insiders.20170818" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index 3a9f24cc41e..812ddf97357 100644 --- a/extensions/python/syntaxes/MagicPython.tmLanguage.json +++ b/extensions/python/syntaxes/MagicPython.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/MagicStack/MagicPython/commit/976e59dcb78cb577e79c8f2117216c06718337e0", + "version": "https://github.com/MagicStack/MagicPython/commit/b453f26ed856c9b16a053517c41207e3a72cc7d5", "name": "MagicPython", "scopeName": "source.python", "fileTypes": [ @@ -260,14 +260,6 @@ } } }, - "codetags": { - "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", - "captures": { - "1": { - "name": "keyword.codetag.notation.python" - } - } - }, "statement-keyword": { "patterns": [ { @@ -392,8 +384,13 @@ ] }, "member-access": { - "begin": "\\.\\s*(?!\\.)", + "begin": "(\\.)\\s*(?!\\.)", "end": "(?x)\n # stop when you've just read non-whitespace followed by non-word\n # i.e. when finished reading an identifier or function call\n (?<=\\S)(?=\\W) |\n # stop when seeing the start of something that's not a word,\n # i.e. when seeing a non-identifier\n (^|(?<=\\s))(?=[^\\\\\\w\\s]) |\n $\n", + "beginCaptures": { + "1": { + "name": "punctuation.separator.period.python" + } + }, "patterns": [ { "include": "#function-call" @@ -918,29 +915,6 @@ } ] }, - "fstring-formatting-braces": { - "patterns": [ - { - "comment": "empty braces are illegal", - "match": "({)(\\s*?)(})", - "captures": { - "1": { - "name": "constant.character.format.placeholder.other.python" - }, - "2": { - "name": "invalid.illegal.brace.python" - }, - "3": { - "name": "constant.character.format.placeholder.other.python" - } - } - }, - { - "name": "constant.character.escape.python", - "match": "({{|}})" - } - ] - }, "fstring-formatting-singe-brace": { "name": "invalid.illegal.brace.python", "match": "(}(?!}))" @@ -949,11 +923,14 @@ "comment": "Import statements\n", "patterns": [ { - "match": "(?x)\n \\s* \\b(from)\\b (\\s*\\.+\\s*) (import)?\n", + "match": "(?x)\n \\s* \\b(from)\\b \\s*(\\.+)\\s* (import)?\n", "captures": { "1": { "name": "keyword.control.import.python" }, + "2": { + "name": "punctuation.separator.period.python" + }, "3": { "name": "keyword.control.import.python" } @@ -1077,8 +1054,13 @@ } }, "member-access-class": { - "begin": "\\.\\s*(?!\\.)", + "begin": "(\\.)\\s*(?!\\.)", "end": "(?<=\\S)(?=\\W)|$", + "beginCaptures": { + "1": { + "name": "punctuation.separator.period.python" + } + }, "patterns": [ { "include": "#call-wrapper-inheritance" @@ -1671,7 +1653,7 @@ }, "magic-variable-names": { "comment": "magic variables which a class/module may have.", - "match": "(?x)\n \\b(\n __(?:\n all | bases | builtins | class | code | debug | defaults | dict\n | doc | file | func | kwdefaults | members\n | metaclass | methods | module | mro | name\n | qualname | self | signature | slots | subclasses\n | version | weakref | wrapped | annotations | classcell\n | spec | path | package | future\n )__\n )\\b\n", + "match": "(?x)\n \\b(\n __(?:\n all | bases | builtins | class | code | debug | defaults | dict\n | doc | file | func | kwdefaults | members\n | metaclass | methods | module | mro | name\n | qualname | self | signature | slots | subclasses\n | version | weakref | wrapped | annotations | classcell\n | spec | path | package | future | traceback\n )__\n )\\b\n", "captures": { "1": { "name": "support.variable.magic.python" @@ -1769,6 +1751,29 @@ } ] }, + "fstring-formatting-braces": { + "patterns": [ + { + "comment": "empty braces are illegal", + "match": "({)(\\s*?)(})", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + }, + "2": { + "name": "invalid.illegal.brace.python" + }, + "3": { + "name": "constant.character.format.placeholder.other.python" + } + } + }, + { + "name": "constant.character.escape.python", + "match": "({{|}})" + } + ] + }, "regexp-base-common": { "patterns": [ { @@ -1891,6 +1896,14 @@ } ] }, + "codetags": { + "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", + "captures": { + "1": { + "name": "keyword.codetag.notation.python" + } + } + }, "comments-base": { "name": "comment.line.number-sign.python", "begin": "(\\#)", diff --git a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json index c6c74610836..0de066f3677 100644 --- a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json +++ b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/MagicStack/MagicPython/commit/df5bb18c64252f2e7b1aa87e2ed124666d314f1d", + "version": "https://github.com/MagicStack/MagicPython/commit/361a4964a559481330764a447e7bab88d4f1b01b", "name": "MagicRegExp", "scopeName": "source.regexp.python", "fileTypes": [ @@ -43,6 +43,29 @@ } ] }, + "fstring-formatting-braces": { + "patterns": [ + { + "comment": "empty braces are illegal", + "match": "({)(\\s*?)(})", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + }, + "2": { + "name": "invalid.illegal.brace.python" + }, + "3": { + "name": "constant.character.format.placeholder.other.python" + } + } + }, + { + "name": "constant.character.escape.python", + "match": "({{|}})" + } + ] + }, "regexp-base-common": { "patterns": [ { @@ -165,6 +188,14 @@ } ] }, + "codetags": { + "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", + "captures": { + "1": { + "name": "keyword.codetag.notation.python" + } + } + }, "regexp-expression": { "patterns": [ { diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json index 5d58f2c7297..b456114b1ed 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -407,7 +407,29 @@ } }, { - "c": "banana.size", + "c": "banana", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "size", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -2189,7 +2211,7 @@ } }, { - "c": " g.", + "c": " g", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -2199,6 +2221,17 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "next", "t": "source.python meta.function-call.python meta.function-call.generic.python", @@ -4499,7 +4532,18 @@ } }, { - "c": ".fn ", + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn ", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -4554,7 +4598,18 @@ } }, { - "c": ".memo ", + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo ", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -4829,7 +4884,18 @@ } }, { - "c": ".memo", + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -4874,7 +4940,7 @@ }, { "c": ".", - "t": "source.python", + "t": "source.python punctuation.separator.period.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4973,7 +5039,7 @@ }, { "c": ".", - "t": "source.python", + "t": "source.python punctuation.separator.period.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5083,7 +5149,7 @@ }, { "c": ".", - "t": "source.python", + "t": "source.python punctuation.separator.period.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5159,7 +5225,7 @@ } }, { - "c": " re.", + "c": " re", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -5169,6 +5235,17 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "search", "t": "source.python meta.function-call.python meta.function-call.generic.python", diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 02e12514b26..4dd90c71801 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -243,10 +243,8 @@ { "name": "JavaScript string interpolation ${}", "scope": [ - "punctuation.definition.template-expression.begin.js", - "punctuation.definition.template-expression.begin.ts", - "punctuation.definition.template-expression.end.ts", - "punctuation.definition.template-expression.end.js", + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", "punctuation.section.embedded.coffee" ], "settings": { @@ -256,8 +254,7 @@ { "name": "Reset JavaScript string interpolation expression", "scope": [ - "meta.template.expression.js", - "meta.template.expression.ts" + "meta.template.expression" ], "settings": { "foreground": "#d4d4d4" diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index fe0b672d312..986fa7cf615 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -32,7 +32,6 @@ "foreground": "#000080" } }, - { "scope": "comment", "settings": { @@ -230,10 +229,8 @@ { "name": "JavaScript string interpolation ${}", "scope": [ - "punctuation.definition.template-expression.begin.js", - "punctuation.definition.template-expression.begin.ts", - "punctuation.definition.template-expression.end.ts", - "punctuation.definition.template-expression.end.js", + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", "punctuation.section.embedded.coffee" ], "settings": { @@ -243,8 +240,7 @@ { "name": "Reset JavaScript string interpolation expression", "scope": [ - "meta.template.expression.js", - "meta.template.expression.ts" + "meta.template.expression" ], "settings": { "foreground": "#ffffff" diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index a1d9f797f64..162c32f6218 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -239,10 +239,8 @@ { "name": "JavaScript string interpolation ${}", "scope": [ - "punctuation.definition.template-expression.begin.js", - "punctuation.definition.template-expression.begin.ts", - "punctuation.definition.template-expression.end.ts", - "punctuation.definition.template-expression.end.js", + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", "punctuation.section.embedded.coffee" ], "settings": { @@ -252,8 +250,7 @@ { "name": "Reset JavaScript string interpolation expression", "scope": [ - "meta.template.expression.js", - "meta.template.expression.ts" + "meta.template.expression" ], "settings": { "foreground": "#000000" diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json index 49d28876e2e..b25953666f6 100644 --- a/extensions/typescript/package.nls.json +++ b/extensions/typescript/package.nls.json @@ -4,7 +4,7 @@ "configuration.typescript": "TypeScript", "typescript.useCodeSnippetsOnMethodSuggest.dec": "Complete functions with their parameter signature.", "typescript.tsdk.desc": "Specifies the folder path containing the tsserver and lib*.d.ts files to use.", - "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Requires TypeScript >= 2.0.6 and a restart after changing it.", + "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Requires TypeScript >= 2.0.6.", "typescript.tsserver.log": "Enables logging of the TS server to a file. This log can be used to diagnose TS Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.tsserver.trace": "Enables tracing of messages sent to the TS server. This trace can be used to diagnose TS Server issues. The trace may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.validate.enable": "Enable/disable TypeScript validation.", diff --git a/extensions/typescript/src/features/bufferSyncSupport.ts b/extensions/typescript/src/features/bufferSyncSupport.ts index e76816e5b8f..975d4e9f529 100644 --- a/extensions/typescript/src/features/bufferSyncSupport.ts +++ b/extensions/typescript/src/features/bufferSyncSupport.ts @@ -53,6 +53,15 @@ class SyncedBuffer { } } + if (this.client.apiVersion.has240Features()) { + const tsPluginsForDocument = this.client.plugins + .filter(x => x.languages.indexOf(this.document.languageId) >= 0); + + if (tsPluginsForDocument.length) { + (args as any).plugins = tsPluginsForDocument.map(plugin => plugin.name); + } + } + this.client.execute('open', args, false); } diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index 90a3f56b30e..db06b46cbc2 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -278,7 +278,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP // Don't complete function calls inside of destructive assigments or imports return this.client.execute('quickinfo', args).then(infoResponse => { const info = infoResponse.body; - switch (info && info.kind) { + switch (info && info.kind as string) { case 'var': case 'let': case 'const': diff --git a/extensions/typescript/src/features/documentSymbolProvider.ts b/extensions/typescript/src/features/documentSymbolProvider.ts index 9fbaf64fc80..456776cf3a6 100644 --- a/extensions/typescript/src/features/documentSymbolProvider.ts +++ b/extensions/typescript/src/features/documentSymbolProvider.ts @@ -71,7 +71,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP let key = `${realIndent}|${item.text}`; if (realIndent !== 0 && !foldingMap[key] && TypeScriptDocumentSymbolProvider.shouldInclueEntry(item.text)) { let result = new SymbolInformation(item.text, - outlineTypeTable[item.kind] || SymbolKind.Variable, + outlineTypeTable[item.kind as string] || SymbolKind.Variable, containerLabel ? containerLabel : '', new Location(resource, textSpan2Range(item.spans[0]))); foldingMap[key] = result; @@ -86,7 +86,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP private static convertNavTree(resource: Uri, bucket: SymbolInformation[], item: Proto.NavigationTree, containerLabel?: string): void { const result = new SymbolInformation(item.text, - outlineTypeTable[item.kind] || SymbolKind.Variable, + outlineTypeTable[item.kind as string] || SymbolKind.Variable, containerLabel ? containerLabel : '', new Location(resource, textSpan2Range(item.spans[0])) ); diff --git a/extensions/typescript/src/features/refactorProvider.ts b/extensions/typescript/src/features/refactorProvider.ts index 4e930e09d80..1b9062998b5 100644 --- a/extensions/typescript/src/features/refactorProvider.ts +++ b/extensions/typescript/src/features/refactorProvider.ts @@ -5,7 +5,7 @@ 'use strict'; -import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit, window, QuickPickItem } from 'vscode'; +import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit, window, QuickPickItem, Selection, Position } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; @@ -123,6 +123,18 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { } const edit = this.toWorkspaceEdit(response.body.edits); - return workspace.applyEdit(edit); + if (!(await workspace.applyEdit(edit))) { + return false; + } + + const renameLocation = response.body.renameLocation; + if (renameLocation) { + if (window.activeTextEditor && window.activeTextEditor.document.uri.fsPath === file) { + const pos = new Position(renameLocation.line - 1, renameLocation.offset - 1); + window.activeTextEditor.selection = new Selection(pos, pos); + await commands.executeCommand('editor.action.rename'); + } + } + return true; } } \ No newline at end of file diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index 9ece0fc2119..4eb9a3e4e43 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -78,10 +78,10 @@ class TscTaskProvider implements vscode.TaskProvider { const editor = vscode.window.activeTextEditor; if (editor) { if (path.basename(editor.document.fileName).match(/^tsconfig\.(.\.)?json$/)) { - const path = editor.document.uri; + const uri = editor.document.uri; return [{ - path: path.fsPath, - workspaceFolder: vscode.workspace.getWorkspaceFolder(path) + path: uri.fsPath, + workspaceFolder: vscode.workspace.getWorkspaceFolder(uri) }]; } } @@ -103,10 +103,11 @@ class TscTaskProvider implements vscode.TaskProvider { const { configFileName } = res.body; if (configFileName && !isImplicitProjectConfigFile(configFileName)) { - const path = vscode.Uri.file(configFileName); - const folder = vscode.workspace.getWorkspaceFolder(path); + const normalizedConfigPath = path.normalize(configFileName); + const uri = vscode.Uri.file(normalizedConfigPath); + const folder = vscode.workspace.getWorkspaceFolder(uri); return [{ - path: configFileName, + path: normalizedConfigPath, workspaceFolder: folder }]; } diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 5d4a89d93ba..036b130adab 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -300,8 +300,7 @@ class LanguageProvider { // ^(.*\*/)?\s*\}.*$ decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/, // ^.*\{[^}"']*$ - increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/, - indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/ + increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/ }, wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, onEnterRules: [ @@ -327,12 +326,6 @@ class LanguageProvider { // e.g. *-----*/| beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, action: { indentAction: IndentAction.None, removeText: 1 } - }, - { - // e.g. if (...) | {} - beforeText: /^\s*(for|while|if|else)\s*/, - afterText: /^\s*{/, - action: { indentAction: IndentAction.None } } ] })); diff --git a/extensions/typescript/src/typescriptService.ts b/extensions/typescript/src/typescriptService.ts index f0d5f5e2501..1a4d1340507 100644 --- a/extensions/typescript/src/typescriptService.ts +++ b/extensions/typescript/src/typescriptService.ts @@ -7,6 +7,7 @@ import { CancellationToken, Uri, Event } from 'vscode'; import * as Proto from './protocol'; import API from './utils/api'; +import { TypeScriptServerPlugin } from "./utils/plugins"; export interface ITypescriptServiceClientHost { syntaxDiagnosticsReceived(event: Proto.DiagnosticEvent): void; @@ -34,6 +35,8 @@ export interface ITypescriptServiceClient { apiVersion: API; + plugins: TypeScriptServerPlugin[]; + execute(command: 'configure', args: Proto.ConfigureRequestArguments, token?: CancellationToken): Promise; execute(command: 'open', args: Proto.OpenRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; execute(command: 'close', args: Proto.FileRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index bf68bc742f9..091abda60d1 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -159,7 +159,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient private readonly host: ITypescriptServiceClientHost, private readonly workspaceState: Memento, private readonly versionStatus: VersionStatus, - private readonly plugins: TypeScriptServerPlugin[] + public readonly plugins: TypeScriptServerPlugin[] ) { this.pathSeparator = path.sep; this.lastStart = Date.now(); @@ -332,7 +332,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient const args: string[] = []; if (this.apiVersion.has206Features()) { args.push('--useSingleInferredProject'); - if (workspace.getConfiguration().get('typescript.disableAutomaticTypeAcquisition', false)) { + if (this.configuration.disableAutomaticTypeAcquisition) { args.push('--disableAutomaticTypingAcquisition'); } } @@ -501,12 +501,12 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient } const compilerOptions: Proto.ExternalProjectCompilerOptions = { - module: 'CommonJS', - target: 'ES6', + module: Proto.ModuleKind.CommonJS, + target: Proto.ScriptTarget.ES6, allowSyntheticDefaultImports: true, allowNonTsExtensions: true, allowJs: true, - jsx: 'Preserve' + jsx: Proto.JsxEmit.Preserve }; if (this.apiVersion.has230Features()) { diff --git a/extensions/typescript/src/utils/api.ts b/extensions/typescript/src/utils/api.ts index 87749afc8e8..4fa9f1d5cb5 100644 --- a/extensions/typescript/src/utils/api.ts +++ b/extensions/typescript/src/utils/api.ts @@ -4,64 +4,65 @@ *--------------------------------------------------------------------------------------------*/ import * as semver from 'semver'; +import * as nls from 'vscode-nls'; +const localize = nls.loadMessageBundle(); export default class API { - public static readonly defaultVersion = new API('1.0.0'); + public static readonly defaultVersion = new API('1.0.0', '1.0.0'); - private readonly _version: string; - - constructor( - private readonly _versionString: string - ) { - this._version = semver.valid(_versionString); - if (!this._version) { - this._version = '1.0.0'; - } else { - // Cut of any prerelease tag since we sometimes consume those - // on purpose. - let index = _versionString.indexOf('-'); - if (index >= 0) { - this._version = this._version.substr(0, index); - } + public static fromVersionString(versionString: string): API { + let version = semver.valid(versionString); + if (!version) { + return new API(localize('invalidVersion', 'invalid version'), '1.0.0'); } + + // Cut of any prerelease tag since we sometimes consume those on purpose. + const index = versionString.indexOf('-'); + if (index >= 0) { + version = version.substr(0, index); + } + return new API(versionString, version); } - public get versionString(): string { - return this._versionString; - } + private constructor( + public readonly versionString: string, + private readonly version: string + ) { } + public has203Features(): boolean { - return semver.gte(this._version, '2.0.3'); + return semver.gte(this.version, '2.0.3'); } public has206Features(): boolean { - return semver.gte(this._version, '2.0.6'); + return semver.gte(this.version, '2.0.6'); } public has208Features(): boolean { - return semver.gte(this._version, '2.0.8'); + return semver.gte(this.version, '2.0.8'); } public has213Features(): boolean { - return semver.gte(this._version, '2.1.3'); + return semver.gte(this.version, '2.1.3'); } public has220Features(): boolean { - return semver.gte(this._version, '2.2.0'); + return semver.gte(this.version, '2.2.0'); } public has222Features(): boolean { - return semver.gte(this._version, '2.2.2'); + return semver.gte(this.version, '2.2.2'); } public has230Features(): boolean { - return semver.gte(this._version, '2.3.0'); + return semver.gte(this.version, '2.3.0'); } public has234Features(): boolean { - return semver.gte(this._version, '2.3.4'); + return semver.gte(this.version, '2.3.4'); } + public has240Features(): boolean { - return semver.gte(this._version, '2.4.0'); + return semver.gte(this.version, '2.4.0'); } } \ No newline at end of file diff --git a/extensions/typescript/src/utils/configuration.ts b/extensions/typescript/src/utils/configuration.ts index df3a8943643..3ff9ad9cc63 100644 --- a/extensions/typescript/src/utils/configuration.ts +++ b/extensions/typescript/src/utils/configuration.ts @@ -47,6 +47,7 @@ export class TypeScriptServiceConfiguration { public readonly npmLocation: string | null; public readonly tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; public readonly checkJs: boolean; + public readonly disableAutomaticTypeAcquisition: boolean; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -60,6 +61,7 @@ export class TypeScriptServiceConfiguration { this.npmLocation = TypeScriptServiceConfiguration.readNpmLocation(configuration); this.tsServerLogLevel = TypeScriptServiceConfiguration.readTsServerLogLevel(configuration); this.checkJs = TypeScriptServiceConfiguration.readCheckJs(configuration); + this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration); } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { @@ -67,7 +69,8 @@ export class TypeScriptServiceConfiguration { && this.localTsdk === other.localTsdk && this.npmLocation === other.npmLocation && this.tsServerLogLevel === other.tsServerLogLevel - && this.checkJs === other.checkJs; + && this.checkJs === other.checkJs + && this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition; } private static extractGlobalTsdk(configuration: WorkspaceConfiguration): string | null { @@ -98,4 +101,8 @@ export class TypeScriptServiceConfiguration { private static readNpmLocation(configuration: WorkspaceConfiguration): string | null { return configuration.get('typescript.npm', null); } -} \ No newline at end of file + + private static readDisableAutomaticTypeAcquisition(configuration: WorkspaceConfiguration): boolean { + return configuration.get('typescript.disableAutomaticTypeAcquisition', false); + } +} diff --git a/extensions/typescript/src/utils/versionProvider.ts b/extensions/typescript/src/utils/versionProvider.ts index 57edf0d5116..a0a61db3872 100644 --- a/extensions/typescript/src/utils/versionProvider.ts +++ b/extensions/typescript/src/utils/versionProvider.ts @@ -41,7 +41,7 @@ export class TypeScriptVersion { // Allow TS developers to provide custom version const tsdkVersion = workspace.getConfiguration().get('typescript.tsdk_version', undefined); if (tsdkVersion) { - return new API(tsdkVersion); + return API.fromVersionString(tsdkVersion); } return undefined; @@ -78,7 +78,7 @@ export class TypeScriptVersion { if (!desc || !desc.version) { return undefined; } - return desc.version ? new API(desc.version) : undefined; + return desc.version ? API.fromVersionString(desc.version) : undefined; } } diff --git a/extensions/typescript/test/colorize-results/test-issue5431_ts.json b/extensions/typescript/test/colorize-results/test-issue5431_ts.json index f249e06fe47..53b90902a23 100644 --- a/extensions/typescript/test/colorize-results/test-issue5431_ts.json +++ b/extensions/typescript/test/colorize-results/test-issue5431_ts.json @@ -355,11 +355,11 @@ "c": "${", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { @@ -368,8 +368,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -377,11 +377,11 @@ "c": "}", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { @@ -399,11 +399,11 @@ "c": "${", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { @@ -412,8 +412,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -421,11 +421,11 @@ "c": "}", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { diff --git a/extensions/typescript/test/colorize-results/test-strings_ts.json b/extensions/typescript/test/colorize-results/test-strings_ts.json index cbf4e9dfb66..136549cfeba 100644 --- a/extensions/typescript/test/colorize-results/test-strings_ts.json +++ b/extensions/typescript/test/colorize-results/test-strings_ts.json @@ -91,11 +91,11 @@ "c": "${", "t": "source.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { @@ -104,8 +104,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -113,11 +113,11 @@ "c": "}", "t": "source.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { @@ -344,22 +344,22 @@ "c": "${", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -368,8 +368,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -377,11 +377,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -399,11 +399,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -412,8 +412,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -421,22 +421,22 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { "c": "}", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { @@ -454,22 +454,22 @@ "c": "${", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -478,8 +478,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -487,11 +487,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -509,11 +509,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -522,8 +522,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -531,22 +531,22 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { "c": "}", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { diff --git a/extensions/xml/package.json b/extensions/xml/package.json index 9ae56b31dc5..c9210274b25 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -36,6 +36,7 @@ ".proj", ".props", ".pt", + ".publishsettings", ".pubxml", ".pubxml.user", ".rdf", diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1112f9fcfdb..32ae19faf07 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -296,9 +296,9 @@ "resolved": "https://registry.npmjs.org/native-keymap/-/native-keymap-1.2.4.tgz" }, "native-watchdog": { - "version": "0.1.0", - "from": "native-watchdog@0.1.0", - "resolved": "https://registry.npmjs.org/native-watchdog/-/native-watchdog-0.1.0.tgz" + "version": "0.2.0", + "from": "native-watchdog@0.2.0", + "resolved": "https://registry.npmjs.org/native-watchdog/-/native-watchdog-0.2.0.tgz" }, "node-pty": { "version": "0.6.9", @@ -567,9 +567,9 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz" }, "xterm": { - "version": "2.8.1", - "from": "Tyriar/xterm.js#vscode-release/1.15", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#75ffea5ebd5510ad0478b017c3946cb8d504855f" + "version": "2.9.1", + "from": "Tyriar/xterm.js#vscode-release/1.16", + "resolved": "git+https://github.com/Tyriar/xterm.js.git#cdf3177fd735ab64403a34778d69daa833e539a5" }, "yauzl": { "version": "2.8.0", diff --git a/package.json b/package.json index 7b42390564c..60932fa6fc4 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "keytar": "^4.0.3", "minimist": "1.2.0", "native-keymap": "1.2.4", - "native-watchdog": "0.1.0", + "native-watchdog": "0.2.0", "node-pty": "0.6.9", "nsfw": "1.0.16", "semver": "4.3.6", @@ -44,7 +44,7 @@ "vscode-ripgrep": "0.0.25", "vscode-textmate": "^3.1.5", "winreg": "1.2.0", - "xterm": "Tyriar/xterm.js#vscode-release/1.15", + "xterm": "Tyriar/xterm.js#vscode-release/1.16", "yauzl": "2.8.0" }, "devDependencies": { diff --git a/src/bootstrap.js b/src/bootstrap.js index 3f94abadd31..dad68bf5c80 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -103,13 +103,15 @@ if (!process.env['VSCODE_ALLOW_IO']) { process.__defineGetter__('stdin', function () { return writable; }); } -// Handle uncaught exceptions -process.on('uncaughtException', function (err) { - console.error('Uncaught Exception: ', err.toString()); - if (err.stack) { - console.error(err.stack); - } -}); +if (!process.env['VSCODE_HANDLES_UNCAUGHT_ERRORS']) { + // Handle uncaught exceptions + process.on('uncaughtException', function (err) { + console.error('Uncaught Exception: ', err.toString()); + if (err.stack) { + console.error(err.stack); + } + }); +} // Kill oneself if one's parent dies. Much drama. if (process.env['VSCODE_PARENT_PID']) { @@ -138,4 +140,4 @@ if (typeof crashReporterOptionsRaw === 'string') { } } -require('./bootstrap-amd').bootstrap(process.env['AMD_ENTRYPOINT']); \ No newline at end of file +require('./bootstrap-amd').bootstrap(process.env['AMD_ENTRYPOINT']); diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index 08c24dcd70c..f182fce6bdb 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -1,197 +1,455 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +/** + * @license MIT + * + * This contains the type declarations for the xterm.js library. Note that + * some interfaces differ between this file and the actual implementation in + * src/, that's because this file declares the *public* API which is intended + * to be stable and consumed by external programs. + */ -declare module 'xterm' { - type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void; +/** + * An object containing start up options for the terminal. + */ +interface ITerminalOptions { + /** + * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). + */ + bellSound?: string; - class Terminal { - cols: number; - rows: number; - ydisp: number; - element: HTMLElement; - textarea: HTMLTextAreaElement; + /** + * The type of the bell notification the terminal will use. + */ + bellStyle?: 'none' | 'visual' | 'sound' | 'both'; - /** - * Creates a new `Terminal` object. - * - * @param {object} options An object containing a set of options. - */ - constructor(options?: any); + /** + * The number of columns in the terminal. + */ + cols?: number; - /** - * Registers an event listener. - * @param eventName The name of the event. - * @param callback The callback. - */ - on(eventName: string, callback: (data: any) => void): void; + /** + * Whether the cursor blinks. + */ + cursorBlink?: boolean; - /** - * Resizes the terminal. - * - * @param x The number of columns to resize to. - * @param y The number of rows to resize to. - */ - resize(columns: number, rows: number): void; + /** + * The style of the cursor. + */ + cursorStyle?: 'block' | 'underline' | 'bar'; - /** - * Emits an event. - * @param eventName The name of the event. - * @param data The data attached to the event. - */ - emit(eventName: string, data: any): void; + /** + * Whether input should be disabled. + */ + disableStdin?: boolean; - /** - * Writes text to the terminal, followed by a break line character (\n). - * @param data The text to write to the terminal. - */ - writeln(data: string): void; + /** + * The number of rows in the terminal. + */ + rows?: number; - /** - * Opens the terminal within an element. - * @param parent The element to create the terminal within. - * @param focus Focus the terminal, after it gets instantiated in the - * DOM. - */ - open(parent: HTMLElement, focus: boolean): void; + /** + * The amount of scrollback in the terminal. Scrollback is the amount of rows + * that are retained when lines are scrolled beyond the initial viewport. + */ + scrollback?: number; - /** - * Attaches a custom key event handler which is run before keys are - * processed, giving consumers of xterm.js ultimate control as to what - * keys should be processed by the terminal and what keys should not. - * @param customKeyEventHandler The custom KeyboardEvent handler to - * attach. This is a function that takes a KeyboardEvent, allowing - * consumers to stop propogation and/or prevent the default action. The - * function returns whether the event should be processed by xterm.js. - */ - attachCustomKeyEventHandler(customKeyEventHandler: (...any) => boolean); + /** + * The size of tab stops in the terminal. + */ + tabStopWidth?: number; + } - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: string): any; + /** + * An object containing options for a link matcher. + */ + interface ILinkMatcherOptions { + /** + * The index of the link from the regex.match(text) call. This defaults to 0 + * (for regular expressions without capture groups). + */ + matchIndex?: number; - /** - * Registers a link matcher, allowing custom link patterns to be matched and - * handled. - * @param {RegExp} regex The regular expression to search for, specifically - * this searches the textContent of the rows. You will want to use \s to match - * a space ' ' character for example. - * @param {LinkMatcherHandler} handler The callback when the link is called. - * @param {LinkMatcherOptions} [options] Options for the link matcher. - * @return {number} The ID of the new matcher, this can be used to deregister. - */ - registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler , options?: any); + /** + * A callback that validates an individual link, returning true if valid and + * false if invalid. + */ + validationCallback?: (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void; - /** - * Deregisters a link matcher if it has been registered. - * @param matcherId The link matcher's ID (returned after register) - */ - deregisterLinkMatcher(matcherId: number): void; + /** + * The priority of the link matcher, this defines the order in which the link + * matcher is evaluated relative to others, from highest to lowest. The + * default value is 0. + */ + priority?: number; + } - /** - * Gets whether the terminal has an active selection. - */ - hasSelection(): boolean; + declare module 'xterm' { + /** + * The class that represents an xterm.js terminal. + */ + export class Terminal { + /** + * The element containing the terminal. + */ + element: HTMLElement; - /** - * Gets the terminal's current selection, this is useful for implementing copy - * behavior outside of xterm.js. - */ - getSelection(): string; + /** + * The textarea that accepts input for the terminal. + */ + textarea: HTMLTextAreaElement; - /** - * Clears the current terminal selection. - */ - clearSelection(): void; + /** + * The number of rows in the terminal's viewport. + */ + rows: number; - /** - * Selects all text within the terminal. - */ - selectAll(): void; + /** + * The number of columns in the terminal's viewport. + */ + cols: number; - /** - * Focus the terminal. Delegates focus handling to the terminal's DOM element. - */ - focus(): void; + /** + * Creates a new `Terminal` object. + * + * @param options An object containing a set of options. + */ + constructor(options?: ITerminalOptions); - /** - * Find the next instance of the term, then scroll to and select it. If it - * doesn't exist, do nothing. - * @param term Tne search term. - * @return Whether a result was found. - */ - findNext(term: string): boolean; + /** + * Unfocus the terminal. + */ + blur(): void; - /** - * Find the previous instance of the term, then scroll to and select it. If it - * doesn't exist, do nothing. - * @param term Tne search term. - * @return Whether a result was found. - */ - findPrevious(term: string): boolean; + /** + * Focus the terminal. + */ + focus(): void; - /** - * Destroys the terminal. - */ - destroy(): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'blur' | 'focus' | 'lineFeed', listener: () => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'data', listener: (data?: string) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'refresh', listener: (data?: {start: number, end: number}) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'resize', listener: (data?: {cols: number, rows: number}) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'scroll', listener: (ydisp?: number) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'title', listener: (title?: string) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: string, listener: (...args: any[]) => void): void; - /** - * Scroll the display of the terminal - * @param disp The number of lines to scroll down (negatives scroll up). - */ - scrollDisp(disp: number): void; + /** + * Deregisters an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + off(type: 'blur' | 'focus' | 'lineFeed' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; - /** - * Scroll the display of the terminal by a number of pages. - * @param {number} pageCount The number of pages to scroll (negative scrolls up). - */ - scrollPages(pageCount: number): void; + /** + * Resizes the terminal. + * @param x The number of columns to resize to. + * @param y The number of rows to resize to. + */ + resize(columns: number, rows: number): void; - /** - * Scrolls the display of the terminal to the top. - */ - scrollToTop(): void; + /** + * Writes text to the terminal, followed by a break line character (\n). + * @param data The text to write to the terminal. + */ + writeln(data: string): void; - /** - * Scrolls the display of the terminal to the bottom. - */ - scrollToBottom(): void; + /** + * Opens the terminal within an element. + * @param parent The element to create the terminal within. + */ + open(parent: HTMLElement): void; - /** - * Clears the entire buffer, making the prompt line the new first line. - */ - clear(): void; + /** + * Attaches a custom key event handler which is run before keys are + * processed, giving consumers of xterm.js ultimate control as to what keys + * should be processed by the terminal and what keys should not. + * @param customKeyEventHandler The custom KeyboardEvent handler to attach. + * This is a function that takes a KeyboardEvent, allowing consumers to stop + * propogation and/or prevent the default action. The function returns + * whether the event should be processed by xterm.js. + */ + attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; - /** - * Writes text to the terminal. - * @param data The text to write to the terminal. - */ - write(data: string): void; + /** + * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to + * be matched and handled. + * @param regex The regular expression to search for, specifically this + * searches the textContent of the rows. You will want to use \s to match a + * space ' ' character for example. + * @param handler The callback when the link is called. + * @param options Options for the link matcher. + * @return The ID of the new matcher, this can be used to deregister. + */ + registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void , options?: ILinkMatcherOptions): number; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: string, value: any): void; + /** + * (EXPERIMENTAL) Deregisters a link matcher if it has been registered. + * @param matcherId The link matcher's ID (returned after register) + */ + deregisterLinkMatcher(matcherId: number): void; - /** - * Tells the renderer to refresh terminal content between two rows (inclusive) at the next - * opportunity. - * @param start The row to start from (between 0 and this.rows - 1). - * @param end The row to end at (between start and this.rows - 1). - */ - refresh(start: number, end: number): void; + /** + * Gets whether the terminal has an active selection. + */ + hasSelection(): boolean; - /** - * Loads an addon, attaching it to the Terminal prototype. - * @param addon The addon to load. - */ - static loadAddon(addon: string): void; + /** + * Gets the terminal's current selection, this is useful for implementing + * copy behavior outside of xterm.js. + */ + getSelection(): string; + + /** + * Clears the current terminal selection. + */ + clearSelection(): void; + + /** + * Selects all text within the terminal. + */ + selectAll(): void; + + // /** + // * Find the next instance of the term, then scroll to and select it. If it + // * doesn't exist, do nothing. + // * @param term Tne search term. + // * @return Whether a result was found. + // */ + // findNext(term: string): boolean; + + // /** + // * Find the previous instance of the term, then scroll to and select it. If it + // * doesn't exist, do nothing. + // * @param term Tne search term. + // * @return Whether a result was found. + // */ + // findPrevious(term: string): boolean; + + /** + * Destroys the terminal and detaches it from the DOM. + */ + destroy(): void; + + /** + * Scroll the display of the terminal + * @param amount The number of lines to scroll down (negative scroll up). + */ + scrollDisp(amount: number): void; + + /** + * Scroll the display of the terminal by a number of pages. + * @param pageCount The number of pages to scroll (negative scrolls up). + */ + scrollPages(pageCount: number): void; + + /** + * Scrolls the display of the terminal to the top. + */ + scrollToTop(): void; + + /** + * Scrolls the display of the terminal to the bottom. + */ + scrollToBottom(): void; + + /** + * Clear the entire buffer, making the prompt line the new first line. + */ + clear(): void; + + /** + * Writes text to the terminal. + * @param data The text to write to the terminal. + */ + write(data: string): void; + + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'termName'): string; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'colors'): string[]; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'cols' | 'rows' | 'tabStopWidth' | 'scrollback'): number; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'geometry'): [number, number]; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'handler'): (data: string) => void; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: string): any; + + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'termName' | 'bellSound', value: string): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'bellStyle', value: null | 'none' | 'visual' | 'sound' | 'both'): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'cursorStyle', value: null | 'block' | 'underline' | 'bar'): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'colors', value: string[]): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'cols' | 'rows' | 'tabStopWidth' | 'scrollback', value: number): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'geometry', value: [number, number]): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'handler', value: (data: string) => void): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: string, value: any): void; + + /** + * Tells the renderer to refresh terminal content between two rows + * (inclusive) at the next opportunity. + * @param start The row to start from (between 0 and this.rows - 1). + * @param end The row to end at (between start and this.rows - 1). + */ + refresh(start: number, end: number): void; + + /** + * Perform a full reset (RIS, aka '\x1bc'). + */ + reset(): void + + /** + * Loads an addon, attaching it to the Terminal prototype and making it + * available to all newly created Terminals. + * @param addon The addon to load. + */ + static loadAddon(addon: 'attach' | 'fit' | 'fullscreen' | 'search' | 'terminado'): void; + + + + + + // Moficiations to official .d.ts below + + /** + * The viewport position. + */ + ydisp: number; + + /** + * Emit an event on the terminal. + */ + emit(type: string, data: any): void; + + /** + * Find the next instance of the term, then scroll to and select it. If it + * doesn't exist, do nothing. + * @param term Tne search term. + * @return Whether a result was found. + */ + findNext(term: string): boolean; + + /** + * Find the previous instance of the term, then scroll to and select it. If it + * doesn't exist, do nothing. + * @param term Tne search term. + * @return Whether a result was found. + */ + findPrevious(term: string): boolean; } - - export = Terminal; -} \ No newline at end of file + } diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 37b6cc08681..217dc4b426e 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -29,15 +29,15 @@ export class RGBA { readonly b: number; /** - * Alpha: integer in [0-255] + * Alpha: float in [0-1] */ readonly a: number; - constructor(r: number, g: number, b: number, a: number = 255) { + constructor(r: number, g: number, b: number, a: number = 1) { this.r = Math.min(255, Math.max(0, r)) | 0; this.g = Math.min(255, Math.max(0, g)) | 0; this.b = Math.min(255, Math.max(0, b)) | 0; - this.a = Math.min(255, Math.max(0, a)) | 0; + this.a = roundFloat(Math.max(Math.min(1, a), 0), 3); } static equals(a: RGBA, b: RGBA): boolean { @@ -90,7 +90,7 @@ export class HSLA { const r = rgba.r / 255; const g = rgba.g / 255; const b = rgba.b / 255; - const a = rgba.a / 255; + const a = rgba.a; const max = Math.max(r, g, b); const min = Math.min(r, g, b); @@ -154,7 +154,7 @@ export class HSLA { b = HSLA._hue2rgb(p, q, h - 1 / 3); } - return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), Math.round(a * 255)); + return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a); } } @@ -214,7 +214,7 @@ export class HSVA { m = ((r - g) / delta) + 4; } - return new HSVA(m * 60, s, cmax, rgba.a / 255); + return new HSVA(m * 60, s, cmax, rgba.a); } // from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm @@ -249,7 +249,7 @@ export class HSVA { g = Math.round((g + m) * 255); b = Math.round((b + m) * 255); - return new RGBA(r, g, b, Math.round(a * 255)); + return new RGBA(r, g, b, a); } } @@ -349,7 +349,7 @@ export class Color { transparent(factor: number): Color { const { r, g, b, a } = this.rgba; - return new Color(new RGBA(r, g, b, Math.round(a * factor))); + return new Color(new RGBA(r, g, b, a * factor)); } isTransparent(): boolean { @@ -357,7 +357,7 @@ export class Color { } isOpaque(): boolean { - return this.rgba.a === 255; + return this.rgba.a === 1; } opposite(): Color { @@ -368,8 +368,8 @@ export class Color { const rgba = c.rgba; // Convert to 0..1 opacity - const thisA = this.rgba.a / 255; - const colorA = rgba.a / 255; + const thisA = this.rgba.a; + const colorA = rgba.a; let a = thisA + colorA * (1 - thisA); if (a < 1.0e-6) { @@ -379,7 +379,6 @@ export class Color { const r = this.rgba.r * thisA / a + rgba.r * colorA * (1 - thisA) / a; const g = this.rgba.g * thisA / a + rgba.g * colorA * (1 - thisA) / a; const b = this.rgba.b * thisA / a + rgba.b * colorA * (1 - thisA) / a; - a *= 255; return new Color(new RGBA(r, g, b, a)); } @@ -410,13 +409,13 @@ export class Color { return of.darken(factor); } - static readonly white = new Color(new RGBA(255, 255, 255, 255)); - static readonly black = new Color(new RGBA(0, 0, 0, 255)); - static readonly red = new Color(new RGBA(255, 0, 0, 255)); - static readonly blue = new Color(new RGBA(0, 0, 255, 255)); - static readonly green = new Color(new RGBA(0, 255, 0, 255)); - static readonly cyan = new Color(new RGBA(0, 255, 255, 255)); - static readonly lightgrey = new Color(new RGBA(211, 211, 211, 255)); + static readonly white = new Color(new RGBA(255, 255, 255, 1)); + static readonly black = new Color(new RGBA(0, 0, 0, 1)); + static readonly red = new Color(new RGBA(255, 0, 0, 1)); + static readonly blue = new Color(new RGBA(0, 0, 255, 1)); + static readonly green = new Color(new RGBA(0, 255, 0, 1)); + static readonly cyan = new Color(new RGBA(0, 255, 255, 1)); + static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1)); static readonly transparent = new Color(new RGBA(0, 0, 0, 0)); } @@ -425,7 +424,7 @@ export namespace Color { export namespace CSS { export function formatRGB(color: Color): string { - if (color.rgba.a === 255) { + if (color.rgba.a === 1) { return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`; } @@ -433,7 +432,7 @@ export namespace Color { } export function formatRGBA(color: Color): string { - return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a / 255).toFixed(2)})`; + return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`; } export function formatHSL(color: Color): string { @@ -465,11 +464,11 @@ export namespace Color { * If 'compact' is set, colors without transparancy will be printed as #RRGGBB */ export function formatHexA(color: Color, compact = false): string { - if (compact && color.rgba.a === 0xFF) { + if (compact && color.rgba.a === 1) { return Color.Format.CSS.formatHex(color); } - return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(color.rgba.a)}`; + return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`; } /** @@ -515,7 +514,7 @@ export namespace Color { const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2)); const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4)); const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6)); - return new Color(new RGBA(r, g, b, 255)); + return new Color(new RGBA(r, g, b, 1)); } if (length === 9) { @@ -524,7 +523,7 @@ export namespace Color { const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4)); const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6)); const a = 16 * _parseHexDigit(hex.charCodeAt(7)) + _parseHexDigit(hex.charCodeAt(8)); - return new Color(new RGBA(r, g, b, a)); + return new Color(new RGBA(r, g, b, a / 255)); } if (length === 4) { @@ -541,7 +540,7 @@ export namespace Color { const g = _parseHexDigit(hex.charCodeAt(2)); const b = _parseHexDigit(hex.charCodeAt(3)); const a = _parseHexDigit(hex.charCodeAt(4)); - return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, 16 * a + a)); + return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, (16 * a + a) / 255)); } // Invalid color diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 743e88c5946..18d8cad519c 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -132,26 +132,35 @@ export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler); } -export function onUnexpectedError(e: any): void { - +export function onUnexpectedError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedError(e); } + return undefined; } -export function onUnexpectedExternalError(e: any): void { - +export function onUnexpectedExternalError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedExternalError(e); } + return undefined; } -export function onUnexpectedPromiseError(promise: TPromise): TPromise { - return promise.then(null, onUnexpectedError); +export function onUnexpectedPromiseError(promise: TPromise): TPromise { + return promise.then(null, onUnexpectedError); } +export interface SerializedError { + readonly $isError: true; + readonly name: string; + readonly message: string; + readonly stack: string; +} + +export function transformErrorForSerialization(error: Error): SerializedError; +export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { let { name, message } = error; @@ -168,6 +177,24 @@ export function transformErrorForSerialization(error: any): any { return error; } +// see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces +export interface V8CallSite { + getThis(): any; + getTypeName(): string; + getFunction(): string; + getFunctionName(): string; + getMethodName(): string; + getFileName(): string; + getLineNumber(): number; + getColumnNumber(): number; + getEvalOrigin(): string; + isToplevel(): boolean; + isEval(): boolean; + isNative(): boolean; + isConstructor(): boolean; + toString(): string; +} + const canceledName = 'Canceled'; /** diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index ca5661ad764..6687e3b9e2f 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -110,6 +110,7 @@ export function tildify(path: string, userHome: string): string { */ const ellipsis = '\u2026'; const unc = '\\\\'; +const home = '~'; export function shorten(paths: string[]): string[] { const shortenedPaths: string[] = new Array(paths.length); @@ -130,7 +131,7 @@ export function shorten(paths: string[]): string[] { match = true; - // trim for now and concatenate unc path (e.g. \\network) or root path (/etc) later + // trim for now and concatenate unc path (e.g. \\network) or root path (/etc, ~/etc) later let prefix = ''; if (path.indexOf(unc) === 0) { prefix = path.substr(0, path.indexOf(unc) + unc.length); @@ -138,6 +139,9 @@ export function shorten(paths: string[]): string[] { } else if (path.indexOf(nativeSep) === 0) { prefix = path.substr(0, path.indexOf(nativeSep) + nativeSep.length); path = path.substr(path.indexOf(nativeSep) + nativeSep.length); + } else if (path.indexOf(home) === 0) { + prefix = path.substr(0, path.indexOf(home) + home.length); + path = path.substr(path.indexOf(home) + home.length); } // pick the first shortest subpath found diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 560103a08c6..003e893e7f8 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -238,7 +238,7 @@ export class TrieMap { private readonly _splitter: (s: string) => string[]; private _root = new Node(); - constructor(splitter: (s: string) => string[]) { + constructor(splitter: (s: string) => string[] = TrieMap.PathSplitter) { this._splitter = s => splitter(s).filter(s => Boolean(s)); } diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index cd2254d3950..7af18aadcb6 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -182,18 +182,6 @@ export function endsWith(haystack: string, needle: string): boolean { } } -export function indexOfIgnoreCase(haystack: string, needle: string, position: number = 0): number { - let index = haystack.indexOf(needle, position); - if (index < 0) { - if (position > 0) { - haystack = haystack.substr(position); - } - needle = escapeRegExpCharacters(needle); - index = haystack.search(new RegExp(needle, 'i')); - } - return index; -} - export interface RegExpOptions { matchCase?: boolean; wholeWord?: boolean; diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts index bb8ebf440d5..434db0a5fa8 100644 --- a/src/vs/base/common/winjs.base.d.ts +++ b/src/vs/base/common/winjs.base.d.ts @@ -4,110 +4,47 @@ *--------------------------------------------------------------------------------------------*/ /// Interfaces for WinJS -export interface ValueCallback { - (value: any): any; -} +export type ErrorCallback = (error: any) => void; +export type ProgressCallback = (progress: TProgress) => void; -export interface EventCallback { - (value: any): void; -} +export declare class Promise { + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason: any) => void, + progress: (progress: TProgress) => void) => void, + oncancel?: () => void); -export interface ErrorCallback { - (error: any): any; -} + public then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null, + onprogress?: (progress: TProgress) => void): Promise; -export interface ProgressCallback { - (progress: any): any; -} + public done( + onfulfilled?: (value: T) => void, + onrejected?: (reason: any) => void, + onprogress?: (progress: TProgress) => void): void; -export declare class Promise { - // commented out because this conflicts with the native promise - // constructor(init: (complete: ValueCallback, error: ErrorCallback, progress: ProgressCallback) => void, oncancel?: any); - - // commented out to speed up adoption of TPromise - // static as(value:any):Promise; - - // static join(promises: { [name: string]: Promise; }): Promise; - static join(promises: Promise[]): Promise; - // static any(promises: Promise[]): Promise; - - // commented out to speed up adoption of TPromise - // static timeout(delay:number):Promise; - - // static wrapError(error: Error): Promise; - // static is(value: any): value is Thenable; - // static addEventListener(type: string, fn: EventCallback): void; - - public then(success?: ValueCallback, error?: ErrorCallback, progress?: ProgressCallback): Promise; - // public then(success?: ValueCallback, error?: ErrorCallback, progress?: ProgressCallback): TPromise; - public done(success?: ValueCallback, error?: ErrorCallback, progress?: ProgressCallback): void; - public cancel(): void; -} - -/** - * The value callback to complete a promise - */ -export interface TValueCallback { - (value: T | Thenable): void; -} - - -export interface TProgressCallback { - (progress: T): void; -} - -interface IPromiseErrorDetail { - parent: TPromise; - error: any; - id: number; - handler: Function; - exception: Error; -} - -interface IPromiseError { - detail: IPromiseErrorDetail; -} - -/** - * A Promise implementation that supports progress and cancelation. - */ -export declare class TPromise { - - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: ProgressCallback) => void, oncancel?: any); - - public then(success?: (value: V) => TPromise, error?: (err: any) => TPromise, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise, error?: (err: any) => TPromise | U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise, error?: (err: any) => U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise, error?: (err: any) => void, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => TPromise, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => TPromise | U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => void, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => TPromise, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => TPromise | U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => void, progress?: ProgressCallback): TPromise; - - public done(success?: (value: V) => void, error?: (err: any) => any, progress?: ProgressCallback): void; public cancel(): void; - public static as(value: null): TPromise; - public static as(value: undefined): TPromise; - public static as(value: TPromise): TPromise; - public static as(value: Thenable): Thenable; - public static as(value: ValueType): TPromise; + public static as(value: null): Promise; + public static as(value: undefined): Promise; + public static as>(value: TPromise): TPromise; + public static as(value: T): Promise; - public static is(value: any): value is Thenable; - public static timeout(delay: number): TPromise; - public static join(promises: TPromise[]): TPromise; - public static join(promises: Thenable[]): Thenable; - public static join(promises: { [n: string]: TPromise }): TPromise<{ [n: string]: ValueType }>; - public static any(promises: TPromise[]): TPromise<{ key: string; value: TPromise; }>; + public static is(value: any): value is PromiseLike; - public static wrap(value: Thenable): TPromise; - public static wrap(value: ValueType): TPromise; + public static timeout(delay: number): Promise; - public static wrapError(error: Error): TPromise; + public static join(promises: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; + public static join(promises: (T | PromiseLike)[]): Promise; + public static join(promises: { [n: string]: T | PromiseLike }): Promise<{ [n: string]: T }>; + + public static any(promises: (T | PromiseLike)[]): Promise<{ key: string; value: Promise; }>; + + public static wrap(value: T | PromiseLike): Promise; + + public static wrapError(error: Error): Promise; /** * @internal @@ -115,25 +52,23 @@ export declare class TPromise { public static addEventListener(event: 'error', promiseErrorHandler: (e: IPromiseError) => void); } -// --- Generic promise with generic progress value -export declare class PPromise extends TPromise { +export type TValueCallback = (value: T | PromiseLike) => void; - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: TProgressCallback

) => void, oncancel?: any); +export { + Promise as TPromise, + Promise as PPromise, + TValueCallback as ValueCallback, + ProgressCallback as TProgressCallback +}; - public then(success?: (value: C) => PPromise, error?: (err: any) => PPromise, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => PPromise, error?: (err: any) => U, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => PPromise, error?: (err: any) => void, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => U, error?: (err: any) => PPromise, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => U, error?: (err: any) => U, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => U, error?: (err: any) => void, progress?: (value: P) => void): PPromise; - - public done(success?: (value: C) => void, error?: (err: any) => any, progress?: (value: P) => void): void; - public cancel(): void; - - public static as(value: V): TPromise; - public static timeout(delay: number): PPromise; - public static join(promises: PPromise[]): PPromise; - public static join(promises: { [n: string]: PPromise }): PPromise<{ [n: string]: C }, P>; - public static any(promises: PPromise[]): PPromise<{ key: string; value: PPromise; }, P>; - public static wrapError(error: Error): TPromise; +export interface IPromiseErrorDetail { + parent: Promise; + error: any; + id: number; + handler: Function; + exception: Error; +} + +export interface IPromiseError { + detail: IPromiseErrorDetail; } diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts index d6d05396fc6..d46e8b6e58c 100644 --- a/src/vs/base/node/request.ts +++ b/src/vs/base/node/request.ts @@ -58,59 +58,61 @@ async function getNodeRequest(options: IRequestOptions): TPromise { let req: http.ClientRequest; - return new TPromise(async (c, e) => { - const endpoint = parseUrl(options.url); - const rawRequest = options.getRawRequest - ? options.getRawRequest(options) - : await getNodeRequest(options); + const rawRequestPromise = options.getRawRequest + ? TPromise.as(options.getRawRequest(options)) + : getNodeRequest(options); - const opts: https.RequestOptions = { - hostname: endpoint.hostname, - port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), - protocol: endpoint.protocol, - path: endpoint.path, - method: options.type || 'GET', - headers: options.headers, - agent: options.agent, - rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true - }; + return rawRequestPromise.then(rawRequest => { + return new TPromise((c, e) => { + const endpoint = parseUrl(options.url); - if (options.user && options.password) { - opts.auth = options.user + ':' + options.password; - } + const opts: https.RequestOptions = { + hostname: endpoint.hostname, + port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), + protocol: endpoint.protocol, + path: endpoint.path, + method: options.type || 'GET', + headers: options.headers, + agent: options.agent, + rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true + }; - req = rawRequest(opts, (res: http.ClientResponse) => { - const followRedirects = isNumber(options.followRedirects) ? options.followRedirects : 3; - - if (res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { - request(assign({}, options, { - url: res.headers['location'], - followRedirects: followRedirects - 1 - })).done(c, e); - } else { - let stream: Stream = res; - - if (res.headers['content-encoding'] === 'gzip') { - stream = stream.pipe(createGunzip()); - } - - c({ res, stream }); + if (options.user && options.password) { + opts.auth = options.user + ':' + options.password; } - }); - req.on('error', e); + req = rawRequest(opts, (res: http.ClientResponse) => { + const followRedirects = isNumber(options.followRedirects) ? options.followRedirects : 3; - if (options.timeout) { - req.setTimeout(options.timeout); - } + if (res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { + request(assign({}, options, { + url: res.headers['location'], + followRedirects: followRedirects - 1 + })).done(c, e); + } else { + let stream: Stream = res; - if (options.data) { - req.write(options.data); - } + if (res.headers['content-encoding'] === 'gzip') { + stream = stream.pipe(createGunzip()); + } - req.end(); - }, - () => req && req.abort()); + c({ res, stream }); + } + }); + + req.on('error', e); + + if (options.timeout) { + req.setTimeout(options.timeout); + } + + if (options.data) { + req.write(options.data); + } + + req.end(); + }, () => req && req.abort()); + }); } function isSuccess(context: IRequestContext): boolean { diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index ac08aeef4e5..63d07bf6c35 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -340,6 +340,10 @@ export class QuickOpenWidget implements IModelProvider { // Allows focus to switch to next/previous entry after tab into an actionbar item DOM.addDisposableListener(this.treeContainer.getHTMLElement(), DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); + // Only handle when not in quick navigation mode + if (this.quickNavigateConfiguration) { + return; + } if (keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { DOM.EventHelper.stop(e, true); this.navigateInTree(keyboardEvent.keyCode, keyboardEvent.shiftKey); diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index bf926d4db7a..a76c98a0333 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -51,133 +51,133 @@ suite('Color', () => { }); test('luminance', function () { - assert.deepEqual(0, new Color(new RGBA(0, 0, 0, 255)).getRelativeLuminance()); - assert.deepEqual(1, new Color(new RGBA(255, 255, 255, 255)).getRelativeLuminance()); + assert.deepEqual(0, new Color(new RGBA(0, 0, 0, 1)).getRelativeLuminance()); + assert.deepEqual(1, new Color(new RGBA(255, 255, 255, 1)).getRelativeLuminance()); - assert.deepEqual(0.2126, new Color(new RGBA(255, 0, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.7152, new Color(new RGBA(0, 255, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.0722, new Color(new RGBA(0, 0, 255, 255)).getRelativeLuminance()); + assert.deepEqual(0.2126, new Color(new RGBA(255, 0, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.7152, new Color(new RGBA(0, 255, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.0722, new Color(new RGBA(0, 0, 255, 1)).getRelativeLuminance()); - assert.deepEqual(0.9278, new Color(new RGBA(255, 255, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.7874, new Color(new RGBA(0, 255, 255, 255)).getRelativeLuminance()); - assert.deepEqual(0.2848, new Color(new RGBA(255, 0, 255, 255)).getRelativeLuminance()); + assert.deepEqual(0.9278, new Color(new RGBA(255, 255, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.7874, new Color(new RGBA(0, 255, 255, 1)).getRelativeLuminance()); + assert.deepEqual(0.2848, new Color(new RGBA(255, 0, 255, 1)).getRelativeLuminance()); - assert.deepEqual(0.5271, new Color(new RGBA(192, 192, 192, 255)).getRelativeLuminance()); + assert.deepEqual(0.5271, new Color(new RGBA(192, 192, 192, 1)).getRelativeLuminance()); - assert.deepEqual(0.2159, new Color(new RGBA(128, 128, 128, 255)).getRelativeLuminance()); - assert.deepEqual(0.0459, new Color(new RGBA(128, 0, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.2003, new Color(new RGBA(128, 128, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.1544, new Color(new RGBA(0, 128, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.0615, new Color(new RGBA(128, 0, 128, 255)).getRelativeLuminance()); - assert.deepEqual(0.17, new Color(new RGBA(0, 128, 128, 255)).getRelativeLuminance()); - assert.deepEqual(0.0156, new Color(new RGBA(0, 0, 128, 255)).getRelativeLuminance()); + assert.deepEqual(0.2159, new Color(new RGBA(128, 128, 128, 1)).getRelativeLuminance()); + assert.deepEqual(0.0459, new Color(new RGBA(128, 0, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.2003, new Color(new RGBA(128, 128, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.1544, new Color(new RGBA(0, 128, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.0615, new Color(new RGBA(128, 0, 128, 1)).getRelativeLuminance()); + assert.deepEqual(0.17, new Color(new RGBA(0, 128, 128, 1)).getRelativeLuminance()); + assert.deepEqual(0.0156, new Color(new RGBA(0, 0, 128, 1)).getRelativeLuminance()); }); test('blending', function () { assert.deepEqual(new Color(new RGBA(0, 0, 0, 0)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(243, 34, 43))); assert.deepEqual(new Color(new RGBA(255, 255, 255)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(255, 255, 255))); - assert.deepEqual(new Color(new RGBA(122, 122, 122, 178.5)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(158, 95, 98))); - assert.deepEqual(new Color(new RGBA(0, 0, 0, 147.9)).blend(new Color(new RGBA(255, 255, 255, 84.15))), new Color(new RGBA(49, 49, 49, 182))); + assert.deepEqual(new Color(new RGBA(122, 122, 122, 0.7)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(158, 95, 98))); + assert.deepEqual(new Color(new RGBA(0, 0, 0, 0.58)).blend(new Color(new RGBA(255, 255, 255, 0.33))), new Color(new RGBA(49, 49, 49, 0.719))); }); suite('HSLA', () => { test('HSLA.toRGBA', function () { assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 0)), new RGBA(0, 0, 0, 0)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 1)), new RGBA(0, 0, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 1, 1)), new RGBA(255, 255, 255, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 1)), new RGBA(0, 0, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 1, 1)), new RGBA(255, 255, 255, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.5, 1)), new RGBA(255, 0, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.5, 1)), new RGBA(0, 255, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.5, 1)), new RGBA(0, 0, 255, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.5, 1)), new RGBA(255, 0, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.5, 1)), new RGBA(0, 255, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.5, 1)), new RGBA(0, 0, 255, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.5, 1)), new RGBA(255, 255, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.5, 1)), new RGBA(0, 255, 255, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.5, 1)), new RGBA(255, 0, 255, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.5, 1)), new RGBA(255, 255, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.5, 1)), new RGBA(0, 255, 255, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.5, 1)), new RGBA(255, 0, 255, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.251, 1)), new RGBA(128, 0, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.251, 1)), new RGBA(128, 128, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.251, 1)), new RGBA(0, 128, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.251, 1)), new RGBA(128, 0, 128, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.251, 1)), new RGBA(0, 128, 128, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.251, 1)), new RGBA(0, 0, 128, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.251, 1)), new RGBA(128, 0, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.251, 1)), new RGBA(128, 128, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.251, 1)), new RGBA(0, 128, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.251, 1)), new RGBA(128, 0, 128, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.251, 1)), new RGBA(0, 128, 128, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.251, 1)), new RGBA(0, 0, 128, 1)); }); test('HSLA.fromRGBA', function () { assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 0)), new HSLA(0, 0, 0, 0)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 255)), new HSLA(0, 0, 0, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 255, 255)), new HSLA(0, 0, 1, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 1)), new HSLA(0, 0, 0, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 255, 1)), new HSLA(0, 0, 1, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 0, 255)), new HSLA(0, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 0, 255)), new HSLA(120, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 255, 255)), new HSLA(240, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 0, 1)), new HSLA(0, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 0, 1)), new HSLA(120, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 255, 1)), new HSLA(240, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 0, 255)), new HSLA(60, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 255, 255)), new HSLA(180, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 255, 255)), new HSLA(300, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 0, 1)), new HSLA(60, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 255, 1)), new HSLA(180, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 255, 1)), new HSLA(300, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(192, 192, 192, 255)), new HSLA(0, 0, 0.753, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(192, 192, 192, 1)), new HSLA(0, 0, 0.753, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 128, 255)), new HSLA(0, 0, 0.502, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 0, 255)), new HSLA(0, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 0, 255)), new HSLA(60, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 0, 255)), new HSLA(120, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 128, 255)), new HSLA(300, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 128, 255)), new HSLA(180, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 128, 255)), new HSLA(240, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 128, 1)), new HSLA(0, 0, 0.502, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 0, 1)), new HSLA(0, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 0, 1)), new HSLA(60, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 0, 1)), new HSLA(120, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 128, 1)), new HSLA(300, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 128, 1)), new HSLA(180, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 128, 1)), new HSLA(240, 1, 0.251, 1)); }); }); suite('HSVA', () => { test('HSVA.toRGBA', function () { assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 0)), new RGBA(0, 0, 0, 0)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 1)), new RGBA(0, 0, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 1, 1)), new RGBA(255, 255, 255, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 1)), new RGBA(0, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 1, 1)), new RGBA(255, 255, 255, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 1, 1)), new RGBA(255, 0, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 1, 1)), new RGBA(0, 255, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 1, 1)), new RGBA(0, 0, 255, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 1, 1)), new RGBA(255, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 1, 1)), new RGBA(0, 255, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 1, 1)), new RGBA(0, 0, 255, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 1, 1)), new RGBA(255, 255, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 1, 1)), new RGBA(0, 255, 255, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 1, 1)), new RGBA(255, 0, 255, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 1, 1)), new RGBA(255, 255, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 1, 1)), new RGBA(0, 255, 255, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 1, 1)), new RGBA(255, 0, 255, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 0.502, 1)), new RGBA(128, 0, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 0.502, 1)), new RGBA(128, 128, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 0.502, 1)), new RGBA(0, 128, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 0.502, 1)), new RGBA(128, 0, 128, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 0.502, 1)), new RGBA(0, 128, 128, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 0.502, 1)), new RGBA(0, 0, 128, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 0.502, 1)), new RGBA(128, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 0.502, 1)), new RGBA(128, 128, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 0.502, 1)), new RGBA(0, 128, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 0.502, 1)), new RGBA(128, 0, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 0.502, 1)), new RGBA(0, 128, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 0.502, 1)), new RGBA(0, 0, 128, 1)); }); test('HSVA.fromRGBA', () => { assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 0)), new HSVA(0, 0, 0, 0)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 255)), new HSVA(0, 0, 0, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 255, 255)), new HSVA(0, 0, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 1)), new HSVA(0, 0, 0, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 255, 1)), new HSVA(0, 0, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 0, 255)), new HSVA(0, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 0, 255)), new HSVA(120, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 255, 255)), new HSVA(240, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 0, 1)), new HSVA(0, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 0, 1)), new HSVA(120, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 255, 1)), new HSVA(240, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 0, 255)), new HSVA(60, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 255, 255)), new HSVA(180, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 255, 255)), new HSVA(300, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 0, 1)), new HSVA(60, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 255, 1)), new HSVA(180, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 255, 1)), new HSVA(300, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(192, 192, 192, 255)), new HSVA(0, 0, 0.753, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(192, 192, 192, 1)), new HSVA(0, 0, 0.753, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 128, 255)), new HSVA(0, 0, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 0, 255)), new HSVA(0, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 0, 255)), new HSVA(60, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 0, 255)), new HSVA(120, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 128, 255)), new HSVA(300, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 128, 255)), new HSVA(180, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 128, 255)), new HSVA(240, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 128, 1)), new HSVA(0, 0, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 0, 1)), new HSVA(0, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 0, 1)), new HSVA(60, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 0, 1)), new HSVA(120, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 128, 1)), new HSVA(300, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 128, 1)), new HSVA(180, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 128, 1)), new HSVA(240, 1, 0.502, 1)); }); }); @@ -192,44 +192,44 @@ suite('Color', () => { assert.deepEqual(Color.Format.CSS.parseHex('#0102030'), null); // somewhat valid - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0').rgba, new RGBA(255, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0').rgba, new RGBA(255, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00').rgba, new RGBA(15, 255, 0, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0').rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0').rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00').rgba, new RGBA(15, 255, 0, 1)); // valid - assert.deepEqual(Color.Format.CSS.parseHex('#000000').rgba, new RGBA(0, 0, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF').rgba, new RGBA(255, 255, 255, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#000000').rgba, new RGBA(0, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF').rgba, new RGBA(255, 255, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF0000').rgba, new RGBA(255, 0, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FF00').rgba, new RGBA(0, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0000FF').rgba, new RGBA(0, 0, 255, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF0000').rgba, new RGBA(255, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FF00').rgba, new RGBA(0, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0000FF').rgba, new RGBA(0, 0, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00').rgba, new RGBA(255, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF').rgba, new RGBA(0, 255, 255, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF').rgba, new RGBA(255, 0, 255, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00').rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF').rgba, new RGBA(0, 255, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF').rgba, new RGBA(255, 0, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0').rgba, new RGBA(192, 192, 192, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0').rgba, new RGBA(192, 192, 192, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#808080').rgba, new RGBA(128, 128, 128, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#800000').rgba, new RGBA(128, 0, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#808000').rgba, new RGBA(128, 128, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#008000').rgba, new RGBA(0, 128, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#800080').rgba, new RGBA(128, 0, 128, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#008080').rgba, new RGBA(0, 128, 128, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#000080').rgba, new RGBA(0, 0, 128, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#808080').rgba, new RGBA(128, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800000').rgba, new RGBA(128, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#808000').rgba, new RGBA(128, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008000').rgba, new RGBA(0, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800080').rgba, new RGBA(128, 0, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008080').rgba, new RGBA(0, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#000080').rgba, new RGBA(0, 0, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#010203').rgba, new RGBA(1, 2, 3, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#040506').rgba, new RGBA(4, 5, 6, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#070809').rgba, new RGBA(7, 8, 9, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a').rgba, new RGBA(10, 10, 10, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b').rgba, new RGBA(11, 11, 11, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c').rgba, new RGBA(12, 12, 12, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d').rgba, new RGBA(13, 13, 13, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e').rgba, new RGBA(14, 14, 14, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f').rgba, new RGBA(15, 15, 15, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0').rgba, new RGBA(160, 160, 160, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA').rgba, new RGBA(204, 255, 170, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA8').rgba, new RGBA(204, 255, 170, 136)); + assert.deepEqual(Color.Format.CSS.parseHex('#010203').rgba, new RGBA(1, 2, 3, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#040506').rgba, new RGBA(4, 5, 6, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#070809').rgba, new RGBA(7, 8, 9, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a').rgba, new RGBA(10, 10, 10, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b').rgba, new RGBA(11, 11, 11, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c').rgba, new RGBA(12, 12, 12, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d').rgba, new RGBA(13, 13, 13, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e').rgba, new RGBA(14, 14, 14, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f').rgba, new RGBA(15, 15, 15, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0').rgba, new RGBA(160, 160, 160, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA').rgba, new RGBA(204, 255, 170, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA8').rgba, new RGBA(204, 255, 170, 0.533)); }); }); }); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index b07f2458744..2e26cb50b0c 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -338,34 +338,16 @@ suite('Event utils', () => { }); }); - test('should emit when done - setTimeout', () => { + test('should emit when done - setTimeout', async () => { let count = 0; - const event = fromPromise(TPromise.timeout(5)); + const promise = TPromise.timeout(5); + const event = fromPromise(promise); event(() => count++); assert.equal(count, 0); - - return TPromise.timeout(10).then(() => { - assert.equal(count, 1); - }); - }); - - test('should emit when done - setTimeout (#2)', () => { - let count = 0; - - const event = fromPromise(TPromise.timeout(30)); - event(() => count++); - - assert.equal(count, 0); - - return TPromise.timeout(0).then(() => { - assert.equal(count, 0); - - return TPromise.timeout(35).then(() => { - assert.equal(count, 1); - }); - }); + await promise; + assert.equal(count, 1); }); }); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 58c8413ba84..b26a2079943 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -5,6 +5,7 @@ 'use strict'; + import { BoundedMap, TrieMap, ResourceMap } from 'vs/base/common/map'; import * as assert from 'assert'; import URI from 'vs/base/common/uri'; @@ -313,7 +314,7 @@ suite('Map', () => { test('TrieMap - basics', function () { - const map = new TrieMap(TrieMap.PathSplitter); + const map = new TrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); @@ -331,7 +332,7 @@ suite('Map', () => { test('TrieMap - lookup', function () { - const map = new TrieMap(TrieMap.PathSplitter); + const map = new TrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); map.insert('/user/foo/flip/flop', 3); @@ -345,7 +346,7 @@ suite('Map', () => { test('TrieMap - superstr', function () { - const map = new TrieMap(TrieMap.PathSplitter); + const map = new TrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); map.insert('/user/foo/flip/flop', 3); diff --git a/src/vs/code/electron-browser/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcessMain.ts index 646098f8b6f..ef7a31319cf 100644 --- a/src/vs/code/electron-browser/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcessMain.ts @@ -138,7 +138,7 @@ function main(server: Server, initData: ISharedProcessInitData): void { function setupIPC(hook: string): TPromise { function setup(retry: boolean): TPromise { - return serve(hook).then(null, err => { + return serve(hook).then(null, err => { if (!retry || platform.isWindows || err.code !== 'EADDRINUSE') { return TPromise.wrapError(err); } diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 0d1b2c076e1..34170dc4e2d 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -511,10 +511,14 @@ export class CodeWindow implements ICodeWindow { } } - public reload(cli?: ParsedArgs): void { + public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void { - // Inherit current properties but overwrite some - const configuration: IWindowConfiguration = objects.mixin({}, this.currentConfig); + // If config is not provided, copy our current one + if (!configuration) { + configuration = objects.mixin({}, this.currentConfig); + } + + // Delete some properties we do not want during reload delete configuration.filesToOpen; delete configuration.filesToCreate; delete configuration.filesToDiff; diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 6e821e3a8c0..441a142d55e 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -19,7 +19,7 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/pa import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState } from 'vs/platform/windows/common/windows'; import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderPath } from 'vs/code/node/windowsFinder'; import CommonEvent, { Emitter } from 'vs/base/common/event'; import product from 'vs/platform/node/product'; @@ -937,16 +937,16 @@ export class WindowsManager implements IWindowsMainService { configuration.backupPath = path.join(this.environmentService.backupHome, options.emptyWindowBackupFolder); } - let codeWindow: CodeWindow; + let window: CodeWindow; if (!options.forceNewWindow) { - codeWindow = options.windowToUse || this.getLastActiveWindow(); - if (codeWindow) { - codeWindow.focus(); + window = options.windowToUse || this.getLastActiveWindow(); + if (window) { + window.focus(); } } // New window - if (!codeWindow) { + if (!window) { const windowConfig = this.configurationService.getConfiguration('window'); const state = this.getNewWindowState(configuration); @@ -965,27 +965,27 @@ export class WindowsManager implements IWindowsMainService { state.mode = WindowMode.Normal; } - codeWindow = this.instantiationService.createInstance(CodeWindow, { + window = this.instantiationService.createInstance(CodeWindow, { state, extensionDevelopmentPath: configuration.extensionDevelopmentPath, isExtensionTestHost: !!configuration.extensionTestsPath }); // Add to our list of windows - WindowsManager.WINDOWS.push(codeWindow); + WindowsManager.WINDOWS.push(window); // Indicate number change via event this._onWindowsCountChanged.fire({ oldCount: WindowsManager.WINDOWS.length - 1, newCount: WindowsManager.WINDOWS.length }); // Window Events - codeWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own - codeWindow.win.webContents.on('devtools-reload-page', () => this.reload(codeWindow)); - codeWindow.win.webContents.on('crashed', () => this.onWindowError(codeWindow, WindowError.CRASHED)); - codeWindow.win.on('unresponsive', () => this.onWindowError(codeWindow, WindowError.UNRESPONSIVE)); - codeWindow.win.on('closed', () => this.onWindowClosed(codeWindow)); + window.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own + window.win.webContents.on('devtools-reload-page', () => this.reload(window)); + window.win.webContents.on('crashed', () => this.onWindowError(window, WindowError.CRASHED)); + window.win.on('unresponsive', () => this.onWindowError(window, WindowError.UNRESPONSIVE)); + window.win.on('closed', () => this.onWindowClosed(window)); // Lifecycle - this.lifecycleService.registerWindow(codeWindow); + this.lifecycleService.registerWindow(window); } // Existing window @@ -993,7 +993,7 @@ export class WindowsManager implements IWindowsMainService { // Some configuration things get inherited if the window is being reused and we are // in extension development host mode. These options are all development related. - const currentWindowConfig = codeWindow.config; + const currentWindowConfig = window.config; if (!configuration.extensionDevelopmentPath && currentWindowConfig && !!currentWindowConfig.extensionDevelopmentPath) { configuration.extensionDevelopmentPath = currentWindowConfig.extensionDevelopmentPath; configuration.verbose = currentWindowConfig.verbose; @@ -1005,7 +1005,7 @@ export class WindowsManager implements IWindowsMainService { } // Only load when the window has not vetoed this - this.lifecycleService.unload(codeWindow, UnloadReason.LOAD).done(veto => { + this.lifecycleService.unload(window, UnloadReason.LOAD).done(veto => { if (!veto) { // Register window for backups @@ -1020,11 +1020,11 @@ export class WindowsManager implements IWindowsMainService { } // Load it - codeWindow.load(configuration); + window.load(configuration); } }); - return codeWindow; + return window; } private getNewWindowState(configuration: IWindowConfiguration): INewWindowState { @@ -1156,7 +1156,7 @@ export class WindowsManager implements IWindowsMainService { // Only reload when the window has not vetoed this this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => { if (!veto) { - win.reload(cli); + win.reload(void 0, cli); // Emit this._onWindowReload.fire(win.id); @@ -1171,19 +1171,44 @@ export class WindowsManager implements IWindowsMainService { }); } - public newWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { - const folders = dialog.showOpenDialog(window ? window.win : void 0, { - buttonLabel: mnemonicLabel(localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), - title: localize('selectWorkspace', "Select Folders for Workspace"), - properties: ['multiSelections', 'openDirectory', 'createDirectory'], - defaultPath: this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0) - }); - - if (folders && folders.length) { - this.workspacesService.createWorkspace(folders.map(folder => URI.file(folder).toString(true /* encoding */))).then(workspace => { - this.open({ context: OpenContext.DIALOG, cli: this.environmentService.args, pathsToOpen: [workspace.configPath] }); - }); + public createAndOpenWorkspace(window: CodeWindow, folders?: string[], path?: string): TPromise { + if (!window || !window.win || window.readyState !== ReadyState.READY) { + return TPromise.as(null); // return early if the window is not ready or disposed } + + return this.workspacesService.createWorkspace(folders).then(workspace => { + let savePromise: TPromise; + if (path) { + savePromise = this.workspacesService.saveWorkspace(workspace, path); + } else { + savePromise = TPromise.as(workspace); + } + + return savePromise.then(workspace => { + window.focus(); + + // Only open workspace when the window has not vetoed this + return this.lifecycleService.unload(window, UnloadReason.RELOAD).done(veto => { + if (!veto) { + + // Register window for backups and migrate current backups over + let backupPath: string; + if (window.config && !window.config.extensionDevelopmentPath) { + backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath); + } + + // Craft a new window configuration to use for the transition + const configuration: IWindowConfiguration = mixin({}, window.config); + configuration.folderPath = void 0; + configuration.workspace = workspace; + configuration.backupPath = backupPath; + + // Reload + window.reload(configuration); + } + }); + }); + }); } public openWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { @@ -1395,12 +1420,12 @@ export class WindowsManager implements IWindowsMainService { return WindowsManager.WINDOWS.length; } - private onWindowError(codeWindow: CodeWindow, error: WindowError): void { + private onWindowError(window: CodeWindow, error: WindowError): void { this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); // Unresponsive if (error === WindowError.UNRESPONSIVE) { - dialog.showMessageBox(codeWindow.win, { + dialog.showMessageBox(window.win, { title: product.nameLong, type: 'warning', buttons: [localize('reopen', "Reopen"), localize('wait', "Keep Waiting"), localize('close', "Close")], @@ -1408,22 +1433,22 @@ export class WindowsManager implements IWindowsMainService { detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), noLink: true }, result => { - if (!codeWindow.win) { + if (!window.win) { return; // Return early if the window has been going down already } if (result === 0) { - codeWindow.reload(); + window.reload(); } else if (result === 2) { - this.onBeforeWindowClose(codeWindow); // 'close' event will not be fired on destroy(), so run it manually - codeWindow.win.destroy(); // make sure to destroy the window as it is unresponsive + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it is unresponsive } }); } // Crashed else { - dialog.showMessageBox(codeWindow.win, { + dialog.showMessageBox(window.win, { title: product.nameLong, type: 'warning', buttons: [localize('reopen', "Reopen"), localize('close', "Close")], @@ -1431,15 +1456,15 @@ export class WindowsManager implements IWindowsMainService { detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true }, result => { - if (!codeWindow.win) { + if (!window.win) { return; // Return early if the window has been going down already } if (result === 0) { - codeWindow.reload(); + window.reload(); } else if (result === 1) { - this.onBeforeWindowClose(codeWindow); // 'close' event will not be fired on destroy(), so run it manually - codeWindow.win.destroy(); // make sure to destroy the window as it has crashed + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it has crashed } }); } @@ -1508,9 +1533,9 @@ export class WindowsManager implements IWindowsMainService { // If the user selected to exit from an extension development host window, do not quit, but just // close the window unless this is the last window that is opened. - const codeWindow = this.getFocusedWindow(); - if (codeWindow && codeWindow.isExtensionDevelopmentHost && this.getWindowCount() > 1) { - codeWindow.win.close(); + const window = this.getFocusedWindow(); + if (window && window.isExtensionDevelopmentHost && this.getWindowCount() > 1) { + window.win.close(); } // Otherwise: normal quit diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index c60b9b2dbca..206351eb6da 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -20,7 +20,7 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IDisposable } from 'vs/base/common/lifecycle'; import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer'; import { Range } from 'vs/editor/common/core/range'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from "vs/editor/common/core/rgba"; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; import * as platform from 'vs/base/common/platform'; @@ -386,7 +386,7 @@ class MinimapBuffers { private readonly _buffers: [ImageData, ImageData]; private _lastUsedBuffer: number; - constructor(ctx: CanvasRenderingContext2D, WIDTH: number, HEIGHT: number, background: RGBA) { + constructor(ctx: CanvasRenderingContext2D, WIDTH: number, HEIGHT: number, background: RGBA8) { this._backgroundFillData = MinimapBuffers._createBackgroundFillData(WIDTH, HEIGHT, background); this._buffers = [ ctx.createImageData(WIDTH, HEIGHT), @@ -406,7 +406,7 @@ class MinimapBuffers { return result; } - private static _createBackgroundFillData(WIDTH: number, HEIGHT: number, background: RGBA): Uint8ClampedArray { + private static _createBackgroundFillData(WIDTH: number, HEIGHT: number, background: RGBA8): Uint8ClampedArray { const backgroundR = background.r; const backgroundG = background.g; const backgroundB = background.b; @@ -830,7 +830,7 @@ export class Minimap extends ViewPart { private static _renderLine( target: ImageData, - backgroundColor: RGBA, + backgroundColor: RGBA8, useLighterFont: boolean, renderMinimap: RenderMinimap, colorTracker: MinimapTokensColorTracker, diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 209e93d7fcb..e4074200469 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -112,28 +112,30 @@ export class ShiftCommand implements ICommand { if (contentStartVisibleColumn % tabSize !== 0) { // The current line is "miss-aligned", so let's see if this is expected... // This can only happen when it has trailing commas in the indent - let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); - if (enterAction) { - extraSpaces = previousLineExtraSpaces; - if (enterAction.appendText) { - for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { - if (enterAction.appendText.charCodeAt(j) === CharCode.Space) { - extraSpaces++; - } else { - break; + if (model.isCheapToTokenize(lineNumber - 1)) { + let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); + if (enterAction) { + extraSpaces = previousLineExtraSpaces; + if (enterAction.appendText) { + for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { + if (enterAction.appendText.charCodeAt(j) === CharCode.Space) { + extraSpaces++; + } else { + break; + } } } - } - if (enterAction.removeText) { - extraSpaces = Math.max(0, extraSpaces - enterAction.removeText); - } - - // Act as if `prefixSpaces` is not part of the indentation - for (let j = 0; j < extraSpaces; j++) { - if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== CharCode.Space) { - break; + if (enterAction.removeText) { + extraSpaces = Math.max(0, extraSpaces - enterAction.removeText); + } + + // Act as if `prefixSpaces` is not part of the indentation + for (let j = 0; j < extraSpaces; j++) { + if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== CharCode.Space) { + break; + } + indentationEndIndex--; } - indentationEndIndex--; } } } diff --git a/src/vs/editor/common/controller/coreCommands.ts b/src/vs/editor/common/controller/coreCommands.ts index 4f47fc04bbc..f3eaebb76c4 100644 --- a/src/vs/editor/common/controller/coreCommands.ts +++ b/src/vs/editor/common/controller/coreCommands.ts @@ -1687,7 +1687,7 @@ namespace Config { return this._runEditorHandler(focusedEditor, args); } - // Ignore this action when user is focussed on an element that allows for entering text + // Ignore this action when user is focused on an element that allows for entering text let activeElement = document.activeElement; if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { document.execCommand(this._inputHandler); diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 40272ecf2d7..763d7731ff6 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -223,7 +223,7 @@ export class TypeOperations { let lineText = model.getLineContent(selection.startLineNumber); - if (/^\s*$/.test(lineText)) { + if (/^\s*$/.test(lineText) && model.isCheapToTokenize(selection.startLineNumber)) { let goodIndent = this._goodIndentForLine(config, model, selection.startLineNumber); goodIndent = goodIndent || '\t'; let possibleTypeText = config.normalizeIndentation(goodIndent); @@ -286,7 +286,7 @@ export class TypeOperations { } private static _enter(config: CursorConfiguration, model: ITokenizedModel, keepPosition: boolean, range: Range): ICommand { - if (model.getFirstInvalidLineNumber() < range.getStartPosition().lineNumber) { + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { let lineText = model.getLineContent(range.startLineNumber); let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); @@ -380,9 +380,8 @@ export class TypeOperations { return false; } - let firstInvalidLineNumber = model.getFirstInvalidLineNumber(); for (let i = 0, len = selections.length; i < len; i++) { - if (firstInvalidLineNumber < selections[i].getEndPosition().lineNumber) { + if (!model.isCheapToTokenize(selections[i].getEndPosition().lineNumber)) { return false; } } @@ -528,6 +527,11 @@ export class TypeOperations { } } + if (!model.isCheapToTokenize(position.lineNumber)) { + // Do not force tokenization + return false; + } + model.forceTokenization(position.lineNumber); const lineTokens = model.getLineTokens(position.lineNumber); @@ -607,8 +611,7 @@ export class TypeOperations { } private static _isTypeInterceptorElectricChar(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]) { - let firstInvalidLineNumber = model.getFirstInvalidLineNumber(); - if (selections.length === 1 && firstInvalidLineNumber >= selections[0].getEndPosition().lineNumber) { + if (selections.length === 1 && model.isCheapToTokenize(selections[0].getEndPosition().lineNumber)) { return true; } return false; diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/rgba.ts new file mode 100644 index 00000000000..ad6070ce13c --- /dev/null +++ b/src/vs/editor/common/core/rgba.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +/** + * A very VM friendly rgba datastructure. + * Please don't touch unless you take a look at the IR. + */ +export class RGBA8 { + _rgba8Brand: void; + + /** + * Red: integer in [0-255] + */ + public readonly r: number; + /** + * Green: integer in [0-255] + */ + public readonly g: number; + /** + * Blue: integer in [0-255] + */ + public readonly b: number; + /** + * Alpha: integer in [0-255] + */ + public readonly a: number; + + constructor(r: number, g: number, b: number, a: number) { + this.r = RGBA8._clampInt_0_255(r); + this.g = RGBA8._clampInt_0_255(g); + this.b = RGBA8._clampInt_0_255(b); + this.a = RGBA8._clampInt_0_255(a); + } + + public static equals(a: RGBA8, b: RGBA8): boolean { + return ( + a.r === b.r + && a.g === b.g + && a.b === b.b + && a.a === b.a + ); + } + + private static _clampInt_0_255(c: number): number { + if (c < 0) { + return 0; + } + if (c > 255) { + return 255; + } + return c | 0; + } +} diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index cc864bcb222..b2a8bde84ba 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -820,10 +820,19 @@ export interface ITokenizedModel extends ITextModel { forceTokenization(lineNumber: number): void; /** - * Get the line number of the first line whose tokens might be inaccurate. + * If it is cheap, force tokenization information for `lineNumber` to be accurate. + * This is based on a heuristic. * @internal */ - getFirstInvalidLineNumber(): number; + tokenizeIfCheap(lineNumber: number): void; + + /** + * Check if calling `forceTokenization` for this `lineNumber` will be cheap (time-wise). + * This is based on a heuristic. + * @internal + */ + isCheapToTokenize(lineNumber: number): boolean; + /** * Get the tokens for the line `lineNumber`. * The tokens might be inaccurate. Use `forceTokenization` to ensure accurate tokens. diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index eeeab138210..d4f31ffcc58 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -169,10 +169,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return result; } - public getFirstInvalidLineNumber(): number { - return this._invalidLineStartIndex + 1; - } - public forceTokenization(lineNumber: number): void { if (lineNumber < 1 || lineNumber > this.getLineCount()) { throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); @@ -183,6 +179,17 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke }); } + public isCheapToTokenize(lineNumber: number): boolean { + const firstInvalidLineNumber = this._invalidLineStartIndex + 1; + return (firstInvalidLineNumber >= lineNumber); + } + + public tokenizeIfCheap(lineNumber: number): void { + if (this.isCheapToTokenize(lineNumber)) { + this.forceTokenization(lineNumber); + } + } + public getLineTokens(lineNumber: number): LineTokens { if (lineNumber < 1 || lineNumber > this.getLineCount()) { throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); diff --git a/src/vs/editor/common/model/tokenIterator.ts b/src/vs/editor/common/model/tokenIterator.ts deleted file mode 100644 index ad19467d666..00000000000 --- a/src/vs/editor/common/model/tokenIterator.ts +++ /dev/null @@ -1,182 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LineToken } from 'vs/editor/common/core/lineTokens'; -import { Position } from 'vs/editor/common/core/position'; -import { StandardTokenType } from 'vs/editor/common/modes'; - -export interface ITokenInfo { - readonly type: StandardTokenType; - readonly lineNumber: number; - readonly startColumn: number; - readonly endColumn: number; -} - -export interface ITokenIterator { - hasNext(): boolean; - next(): ITokenInfo; - hasPrev(): boolean; - prev(): ITokenInfo; -} - -class TokenInfo implements ITokenInfo { - _tokenInfoBrand: void; - - readonly _actual: LineToken; - public readonly lineNumber: number; - public readonly startColumn: number; - public readonly endColumn: number; - public readonly type: StandardTokenType; - - constructor(actual: LineToken, lineNumber: number) { - this._actual = actual; - this.lineNumber = lineNumber; - this.startColumn = this._actual.startOffset + 1; - this.endColumn = this._actual.endOffset + 1; - this.type = this._actual.tokenType; - } -} - -function findClosestNonEmptyLine(model: editorCommon.ITokenizedModel, position: Position): Position { - const lineNumber = position.lineNumber; - if (model.getLineMaxColumn(lineNumber) !== 1) { - return position; - } - - const lineCount = model.getLineCount(); - - // we need to go up or down - let distance = 1; - while (true) { - let aboveLineNumber = lineNumber - distance; - let belowLineNumber = lineNumber + distance; - - if (aboveLineNumber < 1 && belowLineNumber > lineCount) { - // No more lines above or below - break; - } - - if (aboveLineNumber >= 1) { - let aboveMaxColumn = model.getLineMaxColumn(aboveLineNumber); - if (aboveMaxColumn !== 1) { - // bingo! - return new Position(aboveLineNumber, aboveMaxColumn); - } - } - - if (belowLineNumber <= lineCount) { - let belowMaxColumn = model.getLineMaxColumn(belowLineNumber); - if (belowMaxColumn !== 1) { - // bingo! - return new Position(belowLineNumber, 1); - } - } - - distance++; - } - return null; -} - -export class TokenIterator implements ITokenIterator { - - private _model: editorCommon.ITokenizedModel; - private _lineCount: number; - private _prev: TokenInfo; - private _next: TokenInfo; - - constructor(model: editorCommon.ITokenizedModel, position: Position) { - this._model = model; - this._lineCount = this._model.getLineCount(); - this._prev = null; - this._next = null; - - position = findClosestNonEmptyLine(model, position); - if (position) { - this._model.forceTokenization(position.lineNumber); - let lineTokens = this._model.getLineTokens(position.lineNumber); - let currentToken = lineTokens.findTokenAtOffset(position.column - 1); - if (currentToken) { - this._prev = this._next = new TokenInfo(currentToken, position.lineNumber); - } - } - } - - private _advanceNext(): void { - if (!this._next) { - return; - } - - let lineNumber = this._next.lineNumber; - let next = this._next._actual.next(); - while (!next && lineNumber < this._lineCount) { - lineNumber++; - this._model.forceTokenization(lineNumber); - let currentLineTokens = this._model.getLineTokens(lineNumber); - next = currentLineTokens.firstToken(); - } - - this._prev = this._next; - if (next) { - this._next = new TokenInfo(next, lineNumber); - } else { - this._next = null; - } - } - - private _advancePrev(): void { - if (!this._prev) { - return; - } - - let lineNumber = this._prev.lineNumber; - let prev = this._prev._actual.prev(); - while (!prev && lineNumber > 1) { - lineNumber--; - this._model.forceTokenization(lineNumber); - let currentLineTokens = this._model.getLineTokens(lineNumber); - prev = currentLineTokens.lastToken(); - } - - this._next = this._prev; - if (prev) { - this._prev = new TokenInfo(prev, lineNumber); - } else { - this._prev = null; - } - } - - public hasNext(): boolean { - return this._next !== null; - } - - public next(): ITokenInfo { - const result = this._next; - this._advanceNext(); - return result; - } - - public hasPrev(): boolean { - return this._prev !== null; - } - - public prev(): ITokenInfo { - const result = this._prev; - this._advancePrev(); - return result; - } - - public _invalidate() { - // replace all public functions with errors - var errorFn = function (): any { - throw new Error('iteration isn\'t valid anymore'); - }; - this.hasNext = errorFn; - this.next = errorFn; - this.hasPrev = errorFn; - this.prev = errorFn; - } -} diff --git a/src/vs/editor/common/view/minimapCharRenderer.ts b/src/vs/editor/common/view/minimapCharRenderer.ts index 63f98b0c351..6a9fcdf2042 100644 --- a/src/vs/editor/common/view/minimapCharRenderer.ts +++ b/src/vs/editor/common/view/minimapCharRenderer.ts @@ -6,7 +6,7 @@ import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes'; import Event, { Emitter } from 'vs/base/common/event'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from "vs/editor/common/core/rgba"; export class MinimapTokensColorTracker { private static _INSTANCE: MinimapTokensColorTracker = null; @@ -17,7 +17,7 @@ export class MinimapTokensColorTracker { return this._INSTANCE; } - private _colors: RGBA[]; + private _colors: RGBA8[]; private _backgroundIsLight: boolean; private _onDidChange = new Emitter(); @@ -41,14 +41,16 @@ export class MinimapTokensColorTracker { } this._colors = [null]; for (let colorId = 1; colorId < colorMap.length; colorId++) { - this._colors[colorId] = colorMap[colorId].rgba; + const source = colorMap[colorId].rgba; + // Use a VM friendly data-type + this._colors[colorId] = new RGBA8(source.r, source.g, source.b, Math.round(source.a * 255)); } let backgroundLuminosity = colorMap[ColorId.DefaultBackground].getRelativeLuminance(); this._backgroundIsLight = (backgroundLuminosity >= 0.5); this._onDidChange.fire(void 0); } - public getColor(colorId: ColorId): RGBA { + public getColor(colorId: ColorId): RGBA8 { if (colorId < 1 || colorId >= this._colors.length) { // background color (basically invisible) colorId = ColorId.DefaultBackground; @@ -121,7 +123,7 @@ export class MinimapCharRenderer { return (chCode % Constants.CHAR_COUNT); } - public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; @@ -198,7 +200,7 @@ export class MinimapCharRenderer { } } - public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; @@ -235,7 +237,7 @@ export class MinimapCharRenderer { } } - public x2BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x2BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; @@ -307,7 +309,7 @@ export class MinimapCharRenderer { } } - public x1BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x1BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index c933c2d7916..2474d37d322 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -47,7 +47,7 @@ export class ColorPickerHeader extends Disposable { private onDidChangeColor(color: Color): void { this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color); - dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 128 ? this.backgroundColor.isLighter() : color.isLighter()); + dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter()); this.onDidChangeFormatter(); } @@ -277,7 +277,7 @@ class OpacityStrip extends Strip { private onDidChangeColor(color: Color): void { const { r, g, b } = color.rgba; - const opaque = new Color(new RGBA(r, g, b, 255)); + const opaque = new Color(new RGBA(r, g, b, 1)); const transparent = new Color(new RGBA(r, g, b, 0)); this.overlay.style.background = `linear-gradient(to bottom, ${opaque} 0%, ${transparent} 100%)`; diff --git a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts index b9cf5912404..337262b8436 100644 --- a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts +++ b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts @@ -32,7 +32,7 @@ function getPropertyValue(color: Color, variable: string): number | undefined { case 'blue': return color.rgba.b / 255; case 'alpha': - return color.rgba.a / 255; + return color.rgba.a; case 'hue': return color.hsla.h / 360; case 'saturation': @@ -56,7 +56,7 @@ function createPropertyNode(variable: string, fractionDigits: number, type: stri min = typeof min === 'number' ? min : 0; max = typeof max === 'number' ? max : 255; - return (normalize(value, min, max) | 0).toString(); + return (normalize(value, min, max).toFixed(0)).toString(); } else if (type === 'x' || type === 'X') { min = typeof min === 'number' ? min : 0; max = typeof max === 'number' ? max : 255; diff --git a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts index b3ab48b1d17..361c23262ac 100644 --- a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts +++ b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { Color, RGBA } from 'vs/base/common/color'; +import { Color, RGBA, HSLA } from 'vs/base/common/color'; import { ColorFormatter } from 'vs/editor/contrib/colorPicker/common/colorFormatter'; suite('ColorFormatter', () => { @@ -63,4 +63,12 @@ suite('ColorFormatter', () => { const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); assert.equal(hsla.format(color), 'hsla(30, 100%, 50%, 1)'); }); + + test('bug#32323', () => { + const color = new Color(new HSLA(121, 0.45, 0.29, 0.61)); + const rgba = color.rgba; + const color2 = new Color(new RGBA(rgba.r, rgba.g, rgba.b, rgba.a)); + const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); + assert.equal(hsla.format(color2), 'hsla(121, 45%, 29%, 0.61)'); + }); }); \ No newline at end of file diff --git a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts index 4db4eff0bac..ffefd02ed8c 100644 --- a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts @@ -135,7 +135,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { var endLineNumber = this._selection.endLineNumber; var endColumn = this._selection.endColumn; - model.forceTokenization(startLineNumber); + model.tokenizeIfCheap(startLineNumber); let languageId = model.getLanguageIdAtPosition(startLineNumber, startColumn); let config = LanguageConfigurationRegistry.getComments(languageId); if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts index a73a333f268..8dd881a6c40 100644 --- a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -64,7 +64,7 @@ export class LineCommentCommand implements editorCommon.ICommand { */ public static _gatherPreflightCommentStrings(model: editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] { - model.forceTokenization(startLineNumber); + model.tokenizeIfCheap(startLineNumber); const languageId = model.getLanguageIdAtPosition(startLineNumber, 1); const config = LanguageConfigurationRegistry.getComments(languageId); @@ -266,7 +266,7 @@ export class LineCommentCommand implements editorCommon.ICommand { * Given an unsuccessful analysis, delegate to the block comment command */ private _executeBlockComment(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void { - model.forceTokenization(s.startLineNumber); + model.tokenizeIfCheap(s.startLineNumber); let languageId = model.getLanguageIdAtPosition(s.startLineNumber, s.startColumn); let config = LanguageConfigurationRegistry.getComments(languageId); if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index dcc9bf926da..b0063b424ba 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -25,7 +25,7 @@ import { FIND_IDS, MATCHES_LIMIT } from 'vs/editor/contrib/find/common/findModel import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/common/findState'; import { Range } from 'vs/editor/common/core/range'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { CONTEXT_FIND_INPUT_FOCUSSED } from 'vs/editor/contrib/find/common/findController'; +import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/common/findController'; import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; @@ -105,7 +105,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _isReplaceVisible: boolean; private _focusTracker: dom.IFocusTracker; - private _findInputFocussed: IContextKey; + private _findInputFocused: IContextKey; private _viewZone: FindWidgetViewZone; private _viewZoneId: number; @@ -192,10 +192,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._updateToggleSelectionFindButton(); } })); - this._findInputFocussed = CONTEXT_FIND_INPUT_FOCUSSED.bindTo(contextKeyService); + this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService); this._focusTracker = this._register(dom.trackFocus(this._findInput.inputBox.inputElement)); this._focusTracker.addFocusListener(() => { - this._findInputFocussed.set(true); + this._findInputFocused.set(true); if (this._toggleSelectionFind.checked) { let selection = this._codeEditor.getSelection(); @@ -212,7 +212,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } }); this._focusTracker.addBlurListener(() => { - this._findInputFocussed.set(false); + this._findInputFocused.set(false); }); this._codeEditor.addOverlayWidget(this); diff --git a/src/vs/editor/contrib/find/common/findController.ts b/src/vs/editor/contrib/find/common/findController.ts index 78cd27c8d0f..f1ed34cc1c7 100644 --- a/src/vs/editor/contrib/find/common/findController.ts +++ b/src/vs/editor/contrib/find/common/findController.ts @@ -39,7 +39,7 @@ export interface IFindStartOptions { export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey('findWidgetVisible', false); export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); -export const CONTEXT_FIND_INPUT_FOCUSSED = new RawContextKey('findInputFocussed', false); +export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey('findInputFocussed', false); export class CommonFindController extends Disposable implements editorCommon.IEditorContribution { @@ -1131,7 +1131,7 @@ export class ShowNextFindTermAction extends MatchFindAction { precondition: CONTEXT_FIND_WIDGET_VISIBLE, kbOpts: { weight: CommonEditorRegistry.commandWeight(5), - kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSSED, EditorContextKeys.focus), + kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus), primary: ShowNextFindTermKeybinding.primary, mac: ShowNextFindTermKeybinding.mac, win: ShowNextFindTermKeybinding.win, @@ -1156,7 +1156,7 @@ export class ShpwPreviousFindTermAction extends MatchFindAction { precondition: CONTEXT_FIND_WIDGET_VISIBLE, kbOpts: { weight: CommonEditorRegistry.commandWeight(5), - kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSSED, EditorContextKeys.focus), + kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus), primary: ShowPreviousFindTermKeybinding.primary, mac: ShowPreviousFindTermKeybinding.mac, win: ShowPreviousFindTermKeybinding.win, diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts index 182404712f7..a006d5eb1da 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts @@ -41,10 +41,9 @@ function getDefinitions( const promises = provider.map((provider, idx) => { return asWinJsPromise((token) => { return provide(provider, model, position, token); - }).then(result => { - return result; - }, err => { + }).then(undefined, err => { onUnexpectedExternalError(err); + return null; }); }); return outputResults(promises); diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts index 0115099afdc..17e7a051baa 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts @@ -103,7 +103,6 @@ export class DefinitionAction extends EditorAction { }, (err) => { // report an error messageService.show(Severity.Error, err); - return false; }); } diff --git a/src/vs/editor/contrib/hover/browser/hoverOperation.ts b/src/vs/editor/contrib/hover/browser/hoverOperation.ts index 59ea0eff077..71c151f9fa1 100644 --- a/src/vs/editor/contrib/hover/browser/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/browser/hoverOperation.ts @@ -99,7 +99,7 @@ export class HoverOperation { this._asyncComputationPromise = this._computer.computeAsync().then((asyncResult: Result) => { this._asyncComputationPromiseDone = true; this._withAsyncResult(asyncResult); - }, () => this._onError); + }, (e) => this._onError(e)); } else { this._asyncComputationPromiseDone = true; } diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts index 7030def9416..323f55d5fc6 100644 --- a/src/vs/editor/contrib/hover/browser/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts @@ -338,7 +338,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { }); } else { const { red, green, blue, alpha } = msg.color; - const rgba = new RGBA(red * 255, green * 255, blue * 255, alpha * 255); + const rgba = new RGBA(red * 255, green * 255, blue * 255, alpha); const color = new Color(rgba); const formatters = [...msg.formatters]; diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts index 9c5873fc4bc..47322484a43 100644 --- a/src/vs/editor/contrib/indentation/common/indentation.ts +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -423,7 +423,7 @@ export class AutoIndentOnPaste implements IEditorContribution { } const model = this.editor.getModel(); - if (model.getFirstInvalidLineNumber() < range.getStartPosition().lineNumber) { + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { return; } const { tabSize, insertSpaces } = model.getOptions(); diff --git a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts index c829b36d87b..8def37367b3 100644 --- a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts @@ -98,7 +98,7 @@ export class MoveLinesCommand implements ICommand { let insertingText = movingLineText; - if (this.isAutoIndent(model, s)) { + if (this.shouldAutoIndent(model, s)) { let movingLineMatchResult = this.matchEnterRule(model, indentConverter, tabSize, movingLineNumber, s.startLineNumber - 1); // if s.startLineNumber - 1 matches onEnter rule, we still honor that. if (movingLineMatchResult !== null) { @@ -178,7 +178,7 @@ export class MoveLinesCommand implements ICommand { // Insert line that needs to be moved after builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + movingLineText); - if (this.isAutoIndent(model, s)) { + if (this.shouldAutoIndent(model, s)) { virtualModel.getLineContent = (lineNumber: number) => { if (lineNumber === movingLineNumber) { return model.getLineContent(s.startLineNumber); @@ -297,10 +297,14 @@ export class MoveLinesCommand implements ICommand { return str.replace(/^\s+/, ''); } - private isAutoIndent(model: ITokenizedModel, selection: Selection) { + private shouldAutoIndent(model: ITokenizedModel, selection: Selection) { if (!this._autoIndent) { return false; } + // if it's not easy to tokenize, we stop auto indent. + if (!model.isCheapToTokenize(selection.startLineNumber)) { + return false; + } let languageAtSelectionStart = model.getLanguageIdAtPosition(selection.startLineNumber, 1); let languageAtSelectionEnd = model.getLanguageIdAtPosition(selection.endLineNumber, 1); diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index d5ee521195b..fd83ce1c00e 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -91,7 +91,7 @@ export class ParameterHintsModel extends Disposable { private doTrigger(): void { provideSignatureHelp(this.editor.getModel(), this.editor.getPosition()) - .then(null, onUnexpectedError) + .then(null, onUnexpectedError) .then(result => { if (!result || !result.signatures || result.signatures.length === 0) { this.cancel(); diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index f70b0d709ab..62af9d881d1 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -293,7 +293,7 @@ export class SuggestModel implements IDisposable { } else if (quickSuggestions === true) { // all good } else { - model.forceTokenization(pos.lineNumber); + model.tokenizeIfCheap(pos.lineNumber); const { tokenType } = model .getLineTokens(pos.lineNumber) .findTokenAtOffset(pos.column - 1); diff --git a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts index 52b6d50d151..289dde559d2 100644 --- a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts @@ -44,6 +44,7 @@ export function getOccurrencesAtPosition(model: editorCommon.IReadOnlyModel, pos return undefined; }, err => { onUnexpectedExternalError(err); + return undefined; }); } return undefined; diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 3c37032c2c7..afd14619a58 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -195,7 +195,7 @@ export function setModelMarkers(model: editorCommon.IModel, owner: string, marke } /** - * Get markers for owner ant/or resource + * Get markers for owner and/or resource * @returns {IMarker[]} list of markers * @param filter */ diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.ts b/src/vs/editor/test/browser/view/minimapFontCreator.ts index 7819e4cd8a3..66b46dd65ac 100644 --- a/src/vs/editor/test/browser/view/minimapFontCreator.ts +++ b/src/vs/editor/test/browser/view/minimapFontCreator.ts @@ -7,7 +7,7 @@ import { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer'; import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from "vs/editor/common/core/rgba"; let canvas = document.getElementById('my-canvas'); let ctx = canvas.getContext('2d'); @@ -41,8 +41,8 @@ function createFakeImageData(width: number, height: number): ImageData { function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y: number): void { - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); { let x2 = createFakeImageData(Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x2_CHAR_HEIGHT); diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index c2776da191d..4b18c7cdc14 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -3471,6 +3471,7 @@ suite('autoClosingPairs', () => { const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); } else { @@ -3513,6 +3514,7 @@ suite('autoClosingPairs', () => { const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { assertType(model, cursor, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); } else if (autoCloseColumns[column] === ColumnType.Special2) { @@ -3555,42 +3557,50 @@ suite('autoClosingPairs', () => { } // First gif + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste1 = teste\' ok'); assert.equal(model.getLineContent(1), 'teste1 = teste\' ok'); cursor.setSelections('test', [new Selection(1, 1000, 1, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste2 = teste \'ok'); assert.equal(model.getLineContent(2), 'teste2 = teste \'ok\''); cursor.setSelections('test', [new Selection(2, 1000, 2, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste3 = teste" ok'); assert.equal(model.getLineContent(3), 'teste3 = teste" ok'); cursor.setSelections('test', [new Selection(3, 1000, 3, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste4 = teste "ok'); assert.equal(model.getLineContent(4), 'teste4 = teste "ok"'); // Second gif cursor.setSelections('test', [new Selection(4, 1000, 4, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste \''); assert.equal(model.getLineContent(5), 'teste \'\''); cursor.setSelections('test', [new Selection(5, 1000, 5, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste "'); assert.equal(model.getLineContent(6), 'teste ""'); cursor.setSelections('test', [new Selection(6, 1000, 6, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste\''); assert.equal(model.getLineContent(7), 'teste\''); cursor.setSelections('test', [new Selection(7, 1000, 7, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste"'); assert.equal(model.getLineContent(8), 'teste"'); }); diff --git a/src/vs/editor/test/common/model/model.modes.test.ts b/src/vs/editor/test/common/model/model.modes.test.ts index 474c259b0c3..5d970ea6c69 100644 --- a/src/vs/editor/test/common/model/model.modes.test.ts +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -13,7 +13,6 @@ import { Model } from 'vs/editor/common/model/model'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { TokenIterator, ITokenInfo } from 'vs/editor/common/model/tokenIterator'; // --------- utils @@ -289,242 +288,3 @@ suite('Editor Model - Model Modes 2', () => { statesEqual(thisModel, ['', 'ne3', 'Line4', 'Line5']); }); }); - - -suite('Editor Model - Token Iterator', () => { - - const tokenizationSupport: modes.ITokenizationSupport = { - getInitialState: (): modes.IState => NULL_STATE, - tokenize: undefined, - tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { - if (line.length % 3 !== 0) { - throw new Error('Unexpected line length in ' + line); - } - let tokensCount = line.length / 3; - let tokens = new Uint32Array(tokensCount << 1); - for (let i = 0; i < tokensCount; i++) { - tokens[(i << 1)] = 3 * i; - tokens[(i << 1) + 1] = ( - i << modes.MetadataConsts.FOREGROUND_OFFSET - ) >>> 0; - } - return new TokenizationResult2(tokens, state); - } - }; - - let thisModel: Model = null; - let languageRegistration: IDisposable = null; - - setup(() => { - const TEXT = - 'foobarfoobar' + '\r\n' + - 'foobarfoobar' + '\r\n' + - 'foobarfoobar' + '\r\n'; - const LANGUAGE_ID = 'modelModeTestTokenIterator'; - languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); - thisModel = Model.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); - }); - - teardown(() => { - thisModel.dispose(); - thisModel = null; - languageRegistration.dispose(); - languageRegistration = null; - }); - - function tokenIterator(model: Model, position: Position, callback: (it: TokenIterator) => any): any { - let iter = new TokenIterator(model, model.validatePosition(position)); - let result = callback(iter); - iter._invalidate(); - return result; - } - - test('all tokens with ranges', () => { - var calls = 0; - var ranges = [ - [1, 4, 4, 7, 7, 10, 10, 13], - [1, 4, 4, 7, 7, 10, 10, 13], - [1, 4, 4, 7, 7, 10, 10, 13], - ]; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - var a: number[] = [], line = 0; - while (iter.hasNext()) { - calls++; - if (a.length === 0) { - a = ranges.shift(); - line += 1; - } - var next = iter.next(); - assert.equal(next.lineNumber, line); - assert.equal(next.startColumn, a.shift()); - assert.equal(next.endColumn, a.shift()); - } - }); - assert.equal(calls, 12, 'calls'); - }); - - test('all tokens from beginning with next', () => { - var n = 0; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - while (iter.hasNext()) { - iter.next(); - n++; - } - }); - assert.equal(n, 12); - }); - - test('all tokens from beginning with prev', () => { - var n = 0; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - while (iter.hasPrev()) { - iter.prev(); - n++; - } - }); - assert.equal(n, 1); - }); - - test('all tokens from end with prev', () => { - var n = 0; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - while (iter.hasPrev()) { - iter.prev(); - n++; - } - }); - assert.equal(n, 12); - }); - - test('all tokens from end with next', () => { - var n = 0; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - while (iter.hasNext()) { - iter.next(); - n++; - } - }); - assert.equal(n, 1); - }); - - test('prev and next are assert.equal at start', () => { - var calls = 0; - tokenIterator(thisModel, new Position(1, 2), (iter) => { - calls++; - var next = iter.next(); - var prev = iter.prev(); - assert.deepEqual(next, prev); - }); - assert.equal(calls, 1, 'calls'); - }); - - test('position variance within token', () => { - var calls = 0; - - tokenIterator(thisModel, new Position(1, 4), (iter) => { - calls++; - var next = iter.next(); - assert.equal(next.lineNumber, 1); - assert.equal(next.startColumn, 4); - assert.equal(next.endColumn, 7); - }); - - tokenIterator(thisModel, new Position(1, 5), (iter) => { - calls++; - var next = iter.next(); - assert.equal(next.lineNumber, 1); - assert.equal(next.startColumn, 4); - assert.equal(next.endColumn, 7); - }); - - tokenIterator(thisModel, new Position(1, 6), (iter) => { - calls++; - var next = iter.next(); - assert.equal(next.lineNumber, 1); - assert.equal(next.startColumn, 4); - assert.equal(next.endColumn, 7); - }); - - assert.equal(calls, 3, 'calls'); - }); - - test('iterator allows next/prev', () => { - var n = 0; - var up: ITokenInfo[] = [], down: ITokenInfo[] = []; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - while (iter.hasNext()) { - var next = iter.next(); - up.push(next); - n++; - } - while (iter.hasPrev()) { - var prev = iter.prev(); - down.push(prev); - n++; - } - }); - assert.equal(n, 24); - assert.equal(up.length, 12); - assert.equal(down.length, 12); - while (up.length) { - assert.deepEqual(up.pop(), down.shift()); - } - }); - - test('iterator allows prev/next', () => { - var n = 0; - var up: ITokenInfo[] = [], down: ITokenInfo[] = []; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - while (iter.hasPrev()) { - var prev = iter.prev(); - down.push(prev); - n++; - } - while (iter.hasNext()) { - var next = iter.next(); - up.push(next); - n++; - } - }); - assert.equal(n, 24); - assert.equal(up.length, 12); - assert.equal(down.length, 12); - while (up.length) { - assert.deepEqual(up.pop(), down.shift()); - } - }); - - - test('iterator can not be used outside of callback', () => { - var illegalIterReference: TokenIterator; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - illegalIterReference = iter; - }); - - - try { - illegalIterReference.hasNext(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - try { - illegalIterReference.next(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - try { - illegalIterReference.hasPrev(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - try { - illegalIterReference.prev(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - }); -}); diff --git a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts index 0612103a1c9..b59fb4f7bad 100644 --- a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { Constants } from 'vs/editor/common/view/minimapCharRenderer'; import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from 'vs/editor/common/core/rgba'; suite('MinimapCharRenderer', () => { @@ -79,8 +79,8 @@ suite('MinimapCharRenderer', () => { setSampleData('d'.charCodeAt(0), sampleD); let renderer = MinimapCharRendererFactory.create(sampleData); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { @@ -106,8 +106,8 @@ suite('MinimapCharRenderer', () => { test('letter d @ 2x at runtime', () => { let renderer = getOrCreateMinimapCharRenderer(); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { @@ -135,8 +135,8 @@ suite('MinimapCharRenderer', () => { setSampleData('d'.charCodeAt(0), sampleD); let renderer = MinimapCharRendererFactory.create(sampleData); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { @@ -161,8 +161,8 @@ suite('MinimapCharRenderer', () => { test('letter d @ 1x at runtime', () => { let renderer = getOrCreateMinimapCharRenderer(); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8fff0f3d350..97c8a13a94d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5,16 +5,7 @@ declare module monaco { - interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; - } + type Thenable = PromiseLike; export interface IDisposable { dispose(): void; @@ -43,59 +34,49 @@ declare module monaco { - /** - * The value callback to complete a promise - */ - export interface TValueCallback { - (value: T | Thenable): void; - } + export type TValueCallback = (value: T | PromiseLike) => void; + + export type ProgressCallback = (progress: TProgress) => void; - export interface ProgressCallback { - (progress: any): any; - } + export class Promise { + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason: any) => void, + progress: (progress: TProgress) => void) => void, + oncancel?: () => void); + public then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null, + onprogress?: (progress: TProgress) => void): Promise; - /** - * A Promise implementation that supports progress and cancelation. - */ - export class Promise { + public done( + onfulfilled?: (value: T) => void, + onrejected?: (reason: any) => void, + onprogress?: (progress: TProgress) => void): void; - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: ProgressCallback) => void, oncancel?: any); - - public then(success?: (value: V) => Promise, error?: (err: any) => Promise, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise, error?: (err: any) => Promise | U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise, error?: (err: any) => U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise, error?: (err: any) => void, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => Promise, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => Promise | U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => void, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => Promise, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => Promise | U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => void, progress?: ProgressCallback): Promise; - - public done(success?: (value: V) => void, error?: (err: any) => any, progress?: ProgressCallback): void; public cancel(): void; public static as(value: null): Promise; public static as(value: undefined): Promise; - public static as(value: Promise): Promise; - public static as(value: Thenable): Thenable; - public static as(value: ValueType): Promise; + public static as>(value: TPromise): TPromise; + public static as(value: T): Promise; + + public static is(value: any): value is PromiseLike; - public static is(value: any): value is Thenable; public static timeout(delay: number): Promise; - public static join(promises: Promise[]): Promise; - public static join(promises: Thenable[]): Thenable; - public static join(promises: { [n: string]: Promise }): Promise<{ [n: string]: ValueType }>; - public static any(promises: Promise[]): Promise<{ key: string; value: Promise; }>; - public static wrap(value: Thenable): Promise; - public static wrap(value: ValueType): Promise; + public static join(promises: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; + public static join(promises: (T | PromiseLike)[]): Promise; + public static join(promises: { [n: string]: T | PromiseLike }): Promise<{ [n: string]: T }>; - public static wrapError(error: Error): Promise; + public static any(promises: (T | PromiseLike)[]): Promise<{ key: string; value: Promise; }>; + + public static wrap(value: T | PromiseLike): Promise; + + public static wrapError(error: Error): Promise; } export class CancellationTokenSource { @@ -834,7 +815,7 @@ declare module monaco.editor { export function setModelMarkers(model: IModel, owner: string, markers: IMarkerData[]): void; /** - * Get markers for owner ant/or resource + * Get markers for owner and/or resource * @returns {IMarker[]} list of markers * @param filter */ diff --git a/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts b/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts index 401e489ee0f..7801cc6e557 100644 --- a/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts +++ b/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts @@ -329,7 +329,8 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM let alt = item.alt && MenuRegistry.getCommand(item.alt); if (!command) { - collector.warn(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command)); + collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command)); + continue; } if (item.alt && !alt) { collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", item.alt)); diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/common/backup.ts index 42e3e3a529f..70287412b0a 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/common/backup.ts @@ -23,7 +23,7 @@ export interface IBackupMainService { getFolderBackupPaths(): string[]; getEmptyWindowBackupPaths(): string[]; - registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier): string; + registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string; registerFolderBackupSync(folderPath: string): string; registerEmptyWindowBackupSync(backupFolder?: string): string; } \ No newline at end of file diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index fb871afcd3e..2bc6dfb3dbf 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -97,10 +97,28 @@ export class BackupMainService implements IBackupMainService { return this.backups.emptyWorkspaces.slice(0); // return a copy } - public registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier): string { + public registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string { this.pushBackupPathsSync(workspace, this.backups.rootWorkspaces); - return path.join(this.backupHome, workspace.id); + const backupPath = path.join(this.backupHome, workspace.id); + + if (migrateFrom) { + this.moveBackupFolderSync(backupPath, migrateFrom); + } + + return backupPath; + } + + private moveBackupFolderSync(backupPath: string, moveFromPath: string): void { + if (!fs.existsSync(moveFromPath)) { + return; + } + + try { + fs.renameSync(moveFromPath, backupPath); + } catch (ex) { + this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`); + } } public registerFolderBackupSync(folderPath: string): string { diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index bec82b2d595..5cee24b6743 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -196,6 +196,21 @@ suite('BackupMainService', () => { done(); }); + test('service supports to migrate backup data from another location', done => { + const backupPathToMigrate = service.toBackupPath(fooFile.fsPath); + fs.mkdirSync(backupPathToMigrate); + fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); + service.registerFolderBackupSync(backupPathToMigrate); + + const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate); + + assert.ok(fs.existsSync(workspaceBackupPath)); + assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt'))); + assert.ok(!fs.existsSync(backupPathToMigrate)); + + done(); + }); + suite('loadSync', () => { test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => { assert.deepEqual(service.getFolderBackupPaths(), []); diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index edd06713cf0..e7f948d824c 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -17,7 +17,7 @@ export class TestConfigurationService extends EventEmitter implements IConfigura private configuration = Object.create(null); - private configurationByRoot: TrieMap = new TrieMap(TrieMap.PathSplitter); + private configurationByRoot: TrieMap = new TrieMap(); public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index eebeee7101a..3e241e50808 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -195,12 +195,17 @@ export interface IQueryOptions { sortOrder?: SortOrder; } +export enum StatisticType { + Uninstall = 'uninstall' +} + export interface IExtensionGalleryService { _serviceBrand: any; isEnabled(): boolean; getRequestHeaders(): TPromise<{ [key: string]: string; }>; query(options?: IQueryOptions): TPromise>; download(extension: IGalleryExtension): TPromise; + reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise; getReadme(extension: IGalleryExtension): TPromise; getManifest(extension: IGalleryExtension): TPromise; getChangelog(extension: IGalleryMetadata): TPromise; diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 6b5d9a67ca1..cd396127d1e 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as uuid from 'vs/base/common/uuid'; import { distinct } from 'vs/base/common/arrays'; import { getErrorMessage } from 'vs/base/common/errors'; -import { IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { assign, getOrDefault } from 'vs/base/common/objects'; import { IRequestService } from 'vs/platform/request/node/request'; @@ -385,6 +385,27 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return { galleryExtensions, total }; } + async reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise { + if (!this.isEnabled()) { + return; + } + + try { + const headers = { + ...await this.commonHTTPHeaders, + Accept: '*/*;api-version=4.0-preview.1' + }; + + await this.requestService.request({ + type: 'POST', + url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`), + headers + }); + } catch (err) { + // noop + } + } + download(extension: IGalleryExtension): TPromise { return this.loadCompatibleVersion(extension).then(extension => { const zipPath = path.join(tmpdir(), uuid.generateUuid()); @@ -515,7 +536,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const firstOptions = assign({}, options, { url: asset.uri }); return this.requestService.request(firstOptions) - .then(context => context.res.statusCode === 200 ? context : TPromise.wrapError(new Error('expected 200'))) + .then(context => context.res.statusCode === 200 ? context : TPromise.wrapError(new Error('expected 200'))) .then(null, err => { this.telemetryService.publicLog('galleryService:requestError', { cdn: true, message: getErrorMessage(err) }); this.telemetryService.publicLog('galleryService:cdnFallback', { url: asset.uri }); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index ebe55654b90..e019353ebce 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -13,11 +13,12 @@ import { assign } from 'vs/base/common/objects'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { flatten, distinct } from 'vs/base/common/arrays'; import { extract, buffer } from 'vs/base/node/zip'; -import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionManifest, IGalleryMetadata, - InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, LocalExtensionType + InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, LocalExtensionType, + StatisticType } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest, getGalleryExtensionIdFromLocal, getIdAndVersionFromLocalExtensionId, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; @@ -55,7 +56,7 @@ function readManifest(extensionPath: string): TPromise<{ manifest: IExtensionMan pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8') .then(raw => parseManifest(raw)), pfs.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8') - .then(null, err => err.code !== 'ENOENT' ? TPromise.wrapError(err) : '{}') + .then(null, err => err.code !== 'ENOENT' ? TPromise.wrapError(err) : '{}') .then(raw => JSON.parse(raw)) ]; @@ -223,9 +224,9 @@ export class ExtensionManagementService implements IExtensionManagementService { } private rollback(localExtension: ILocalExtension, dependecies: IGalleryExtension[]): TPromise { - return this.doUninstall(localExtension.id) + return this.doUninstall(localExtension) .then(() => this.filterOutUninstalled(dependecies)) - .then(installed => TPromise.join(installed.map((i) => this.doUninstall(i.id)))) + .then(installed => TPromise.join(installed.map((i) => this.doUninstall(i)))) .then(() => null); } @@ -294,21 +295,21 @@ export class ExtensionManagementService implements IExtensionManagementService { uninstall(extension: ILocalExtension, force = false): TPromise { return this.removeOutdatedExtensions().then(() => { - return this.scanUserExtensions().then(installed => { + return this.scanUserExtensions().then(installed => { const promises = installed .filter(e => e.manifest.publisher === extension.manifest.publisher && e.manifest.name === extension.manifest.name) .map(e => this.checkForDependenciesAndUninstall(e, installed, force)); return TPromise.join(promises); }); - }); + }).then(() => { /* drop resolved value */ }); } private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise { - return this.preUninstallExtension(extension.id) + return this.preUninstallExtension(extension) .then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force)) - .then(() => this.postUninstallExtension(extension.id), + .then(() => this.postUninstallExtension(extension), error => { - this.postUninstallExtension(extension.id, error); + this.postUninstallExtension(extension, error); return TPromise.wrapError(error); }); } @@ -370,7 +371,7 @@ export class ExtensionManagementService implements IExtensionManagementService { if (dependents.length) { return TPromise.wrapError(new Error(this.getDependentsErrorMessage(extension, dependents))); } - return TPromise.join([this.uninstallExtension(extension.id), ...dependenciesToUninstall.map(d => this.doUninstall(d.id))]).then(() => null); + return TPromise.join([this.uninstallExtension(extension.id), ...dependenciesToUninstall.map(d => this.doUninstall(d))]).then(() => null); } private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string { @@ -419,21 +420,21 @@ export class ExtensionManagementService implements IExtensionManagementService { return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.indexOf(getGalleryExtensionIdFromLocal(extension)) !== -1); } - private doUninstall(id: string): TPromise { - return this.preUninstallExtension(id) - .then(() => this.uninstallExtension(id)) - .then(() => this.postUninstallExtension(id), + private doUninstall(extension: ILocalExtension): TPromise { + return this.preUninstallExtension(extension) + .then(() => this.uninstallExtension(extension.id)) + .then(() => this.postUninstallExtension(extension), error => { - this.postUninstallExtension(id, error); + this.postUninstallExtension(extension, error); return TPromise.wrapError(error); }); } - private preUninstallExtension(id: string): TPromise { - const extensionPath = path.join(this.extensionsPath, id); + private preUninstallExtension(extension: ILocalExtension): TPromise { + const extensionPath = path.join(this.extensionsPath, extension.id); return pfs.exists(extensionPath) .then(exists => exists ? null : TPromise.wrapError(new Error(nls.localize('notExists', "Could not find extension")))) - .then(() => this._onUninstallExtension.fire(id)); + .then(() => this._onUninstallExtension.fire(extension.id)); } private uninstallExtension(id: string): TPromise { @@ -443,8 +444,12 @@ export class ExtensionManagementService implements IExtensionManagementService { .then(() => this.unsetObsolete(id)); } - private postUninstallExtension(id: string, error?: any): TPromise { - return this._onDidUninstallExtension.fire({ id, error }); + private async postUninstallExtension(extension: ILocalExtension, error?: any): TPromise { + if (!error) { + await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); + } + + this._onDidUninstallExtension.fire({ id: extension.id, error }); } getInstalled(type: LocalExtensionType = null): TPromise { @@ -476,7 +481,7 @@ export class ExtensionManagementService implements IExtensionManagementService { const limiter = new Limiter(10); return this.scanExtensionFolders(root) - .then(extensionIds => Promise.join(extensionIds.map(id => { + .then(extensionIds => TPromise.join(extensionIds.map(id => { const extensionPath = path.join(root, id); const each = () => pfs.readdir(extensionPath).then(children => { @@ -575,7 +580,7 @@ export class ExtensionManagementService implements IExtensionManagementService { return this.obsoleteFileLimiter.queue(() => { let result: T = null; return pfs.readFile(this.obsoletePath, 'utf8') - .then(null, err => err.code === 'ENOENT' ? TPromise.as('{}') : TPromise.wrapError(err)) + .then(null, err => err.code === 'ENOENT' ? TPromise.as('{}') : TPromise.wrapError(err)) .then<{ [id: string]: boolean }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) .then(obsolete => { result = fn(obsolete); return obsolete; }) .then(obsolete => { diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index 30f10f310fa..ac9c9267168 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -59,9 +59,9 @@ export interface ILifecycleService { onBeforeWindowUnload: Event; ready(): void; - registerWindow(codeWindow: ICodeWindow): void; + registerWindow(window: ICodeWindow): void; - unload(codeWindow: ICodeWindow, reason: UnloadReason): TPromise; + unload(window: ICodeWindow, reason: UnloadReason): TPromise; relaunch(options?: { addArgs?: string[], removeArgs?: string[] }); @@ -148,11 +148,11 @@ export class LifecycleService implements ILifecycleService { }); } - public registerWindow(codeWindow: ICodeWindow): void { + public registerWindow(window: ICodeWindow): void { // Window Before Closing: Main -> Renderer - codeWindow.win.on('close', e => { - const windowId = codeWindow.id; + window.win.on('close', e => { + const windowId = window.id; this.logService.log('Lifecycle#window-before-close', windowId); // The window already acknowledged to be closed @@ -166,11 +166,11 @@ export class LifecycleService implements ILifecycleService { // Otherwise prevent unload and handle it from window e.preventDefault(); - this.unload(codeWindow, UnloadReason.CLOSE).done(veto => { + this.unload(window, UnloadReason.CLOSE).done(veto => { if (!veto) { this.windowToCloseRequest[windowId] = true; - this._onBeforeWindowClose.fire(codeWindow); - codeWindow.close(); + this._onBeforeWindowClose.fire(window); + window.close(); } else { this.quitRequested = false; delete this.windowToCloseRequest[windowId]; @@ -179,25 +179,25 @@ export class LifecycleService implements ILifecycleService { }); } - public unload(codeWindow: ICodeWindow, reason: UnloadReason): TPromise { + public unload(window: ICodeWindow, reason: UnloadReason): TPromise { // Always allow to unload a window that is not yet ready - if (codeWindow.readyState !== ReadyState.READY) { + if (window.readyState !== ReadyState.READY) { return TPromise.as(false); } - this.logService.log('Lifecycle#unload()', codeWindow.id); + this.logService.log('Lifecycle#unload()', window.id); const windowUnloadReason = this.quitRequested ? UnloadReason.QUIT : reason; // first ask the window itself if it vetos the unload - return this.doUnloadWindowInRenderer(codeWindow, windowUnloadReason).then(veto => { + return this.doUnloadWindowInRenderer(window, windowUnloadReason).then(veto => { if (veto) { return this.handleVeto(veto); } // then check for vetos in the main side - return this.doUnloadWindowInMain(codeWindow, windowUnloadReason).then(veto => this.handleVeto(veto)); + return this.doUnloadWindowInMain(window, windowUnloadReason).then(veto => this.handleVeto(veto)); }); } @@ -213,7 +213,7 @@ export class LifecycleService implements ILifecycleService { return veto; } - private doUnloadWindowInRenderer(codeWindow: ICodeWindow, reason: UnloadReason): TPromise { + private doUnloadWindowInRenderer(window: ICodeWindow, reason: UnloadReason): TPromise { return new TPromise((c) => { const oneTimeEventToken = this.oneTimeListenerTokenGenerator++; const okChannel = `vscode:ok${oneTimeEventToken}`; @@ -227,7 +227,7 @@ export class LifecycleService implements ILifecycleService { c(true); // veto }); - codeWindow.send('vscode:beforeUnload', { okChannel, cancelChannel, reason }); + window.send('vscode:beforeUnload', { okChannel, cancelChannel, reason }); }); } diff --git a/src/vs/platform/message/common/message.ts b/src/vs/platform/message/common/message.ts index 8a04042fff4..627bbe0900b 100644 --- a/src/vs/platform/message/common/message.ts +++ b/src/vs/platform/message/common/message.ts @@ -13,6 +13,7 @@ import { Action } from 'vs/base/common/actions'; export interface IMessageWithAction { message: string; actions: Action[]; + source?: string; } export interface IConfirmation { @@ -77,4 +78,4 @@ export interface IChoiceService { choose(severity: Severity, message: string, options: string[], cancelId: number, modal?: boolean): TPromise; } -export import Severity = Severity; \ No newline at end of file +export import Severity = Severity; diff --git a/src/vs/platform/telemetry/common/experiments.ts b/src/vs/platform/telemetry/common/experiments.ts index b338b4518db..36961817a48 100644 --- a/src/vs/platform/telemetry/common/experiments.ts +++ b/src/vs/platform/telemetry/common/experiments.ts @@ -6,31 +6,46 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -export interface ITelemetryExperiments { +export interface IExperiments { + deployToAzureQuickLink: boolean; } -const defaultExperiments: ITelemetryExperiments = { -}; +export const IExperimentService = createDecorator('experimentService'); -export function loadExperiments(accessor?: ServicesAccessor): ITelemetryExperiments { +export interface IExperimentService { - // shortcut since there are currently no experiments (should introduce separate service to load only once) - if (!accessor) { - return {}; + _serviceBrand: any; + + getExperiments(): IExperiments; +} + +export class ExperimentService implements IExperimentService { + + _serviceBrand: any; + + private experiments: IExperiments; + + constructor( + @IStorageService private storageService: IStorageService, + @IConfigurationService private configurationService: IConfigurationService, + ) { } + + getExperiments() { + if (!this.experiments) { + this.experiments = loadExperiments(this.storageService, this.configurationService); + } + return this.experiments; } - - const storageService = accessor.get(IStorageService); - const configurationService = accessor.get(IConfigurationService); - - let { - } = splitExperimentsRandomness(storageService); - - return applyOverrides(defaultExperiments, configurationService); } -function applyOverrides(experiments: ITelemetryExperiments, configurationService: IConfigurationService): ITelemetryExperiments { +function loadExperiments(storageService: IStorageService, configurationService: IConfigurationService): IExperiments { + const experiments = splitExperimentsRandomness(storageService); + return applyOverrides(experiments, configurationService); +} + +function applyOverrides(experiments: IExperiments, configurationService: IConfigurationService): IExperiments { const experimentsConfig = getExperimentsOverrides(configurationService); Object.keys(experiments).forEach(key => { if (key in experimentsConfig) { @@ -40,14 +55,14 @@ function applyOverrides(experiments: ITelemetryExperiments, configurationService return experiments; } -function splitExperimentsRandomness(storageService: IStorageService): ITelemetryExperiments { +function splitExperimentsRandomness(storageService: IStorageService): IExperiments { const random1 = getExperimentsRandomness(storageService); const [random2, /* showTaskDocumentation */] = splitRandom(random1); - const [random3, /* openUntitledFile */] = splitRandom(random2); - const [random4, /* mergeQuickLinks */] = splitRandom(random3); - // tslint:disable-next-line:no-unused-variable (https://github.com/Microsoft/TypeScript/issues/16628) - const [random5, /* enableWelcomePage */] = splitRandom(random4); + const [/* random3 */, deployToAzureQuickLink] = splitRandom(random2); + // const [random4, /* mergeQuickLinks */] = splitRandom(random3); + // const [random5, /* enableWelcomePage */] = splitRandom(random4); return { + deployToAzureQuickLink }; } @@ -68,7 +83,7 @@ function splitRandom(random: number): [number, boolean] { return [scaled - i, i === 1]; } -function getExperimentsOverrides(configurationService: IConfigurationService): ITelemetryExperiments { +function getExperimentsOverrides(configurationService: IConfigurationService): IExperiments { const config: any = configurationService.getConfiguration('telemetry'); return config && config.experiments || {}; } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index cd8ce27509c..2ac3d924e5c 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -264,8 +264,8 @@ export const editorActiveLinkForeground = registerColor('editorLink.activeForegr /** * Diff Editor Colors */ -export const defaultInsertColor = new Color(new RGBA(155, 185, 85, 255 * 0.2)); -export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, 255 * 0.2)); +export const defaultInsertColor = new Color(new RGBA(155, 185, 85, 0.2)); +export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, 0.2)); export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: defaultInsertColor, light: defaultInsertColor, hc: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted.')); export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hc: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed.')); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index ecc0b6643cb..d3850792ef3 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -42,7 +42,7 @@ export interface IWindowsService { toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; openWorkspace(windowId: number): TPromise; - newWorkspace(windowId: number): TPromise; + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise; toggleFullScreen(windowId: number): TPromise; setRepresentedFilename(windowId: number, fileName: string): TPromise; addRecentlyOpened(files: string[]): TPromise; @@ -98,7 +98,7 @@ export interface IWindowService { toggleDevTools(): TPromise; closeWorkspace(): TPromise; openWorkspace(): TPromise; - newWorkspace(): TPromise; + createAndOpenWorkspace(folders?: string[], path?: string): TPromise; toggleFullScreen(): TPromise; setRepresentedFilename(fileName: string): TPromise; getRecentlyOpened(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index dd1f6f599f0..0ea79af8707 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -23,7 +23,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; call(command: 'openWorkspace', arg: number): TPromise; - call(command: 'newWorkspace', arg: number): TPromise; + call(command: 'createAndOpenWorkspace', arg: [number, string[], string]): TPromise; call(command: 'toggleFullScreen', arg: number): TPromise; call(command: 'setRepresentedFilename', arg: [number, string]): TPromise; call(command: 'addRecentlyOpened', arg: string[]): TPromise; @@ -79,7 +79,7 @@ export class WindowsChannel implements IWindowsChannel { case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); case 'openWorkspace': return this.service.openWorkspace(arg); - case 'newWorkspace': return this.service.newWorkspace(arg); + case 'createAndOpenWorkspace': return this.service.createAndOpenWorkspace(arg[0], arg[1], arg[2]); case 'toggleFullScreen': return this.service.toggleFullScreen(arg); case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]); case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg); @@ -159,8 +159,8 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('openWorkspace', windowId); } - newWorkspace(windowId: number): TPromise { - return this.channel.call('newWorkspace', windowId); + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { + return this.channel.call('createAndOpenWorkspace', [windowId, folders, path]); } toggleFullScreen(windowId: number): TPromise { diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 3ebc232734c..cab6b6e4425 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -68,8 +68,8 @@ export class WindowService implements IWindowService { return this.windowsService.openWorkspace(this.windowId); } - newWorkspace(): TPromise { - return this.windowsService.newWorkspace(this.windowId); + createAndOpenWorkspace(folders?: string[], path?: string): TPromise { + return this.windowsService.createAndOpenWorkspace(this.windowId, folders, path); } closeWindow(): TPromise { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 1e4f61f7e6a..13ed2583fd3 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -57,8 +57,8 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; - newWorkspace(win?: ICodeWindow): void; openWorkspace(win?: ICodeWindow): void; + createAndOpenWorkspace(win: ICodeWindow, folders?: string[], path?: string): void; closeWorkspace(win: ICodeWindow): void; open(openConfig: IOpenConfiguration): ICodeWindow[]; openExtensionDevelopmentHostWindow(openConfig: IOpenConfiguration): void; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 73a1b67d44f..9a698579518 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -124,11 +124,11 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } - newWorkspace(windowId: number): TPromise { + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); if (codeWindow) { - this.windowsMainService.newWorkspace(codeWindow); + this.windowsMainService.createAndOpenWorkspace(codeWindow, folders, path); } return TPromise.as(null); diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 473099f12b8..7997a686697 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -142,7 +142,7 @@ export class LegacyWorkspace implements ILegacyWorkspace { export class Workspace implements IWorkspace { - private _rootsMap: TrieMap = new TrieMap(TrieMap.PathSplitter); + private _rootsMap: TrieMap = new TrieMap(); constructor( public readonly id: string, @@ -187,7 +187,7 @@ export class Workspace implements IWorkspace { } private updateRootsMap(): void { - this._rootsMap = new TrieMap(TrieMap.PathSplitter); + this._rootsMap = new TrieMap(); for (const root of this.roots) { this._rootsMap.insert(root.fsPath, root); } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index d237dad287e..ab976f0f57b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -55,40 +55,6 @@ declare module 'vscode' { export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; } - /** - * Namespace for handling credentials. - */ - export namespace credentials { - - /** - * Read a previously stored secret from the credential store. - * - * @param service The service of the credential. - * @param account The account of the credential. - * @return A promise for the secret of the credential. - */ - export function readSecret(service: string, account: string): Thenable; - - /** - * Write a secret to the credential store. - * - * @param service The service of the credential. - * @param account The account of the credential. - * @param secret The secret of the credential to write to the credential store. - * @return A promise indicating completion of the operation. - */ - export function writeSecret(service: string, account: string, secret: string): Thenable; - - /** - * Delete a previously stored secret from the credential store. - * - * @param service The service of the credential. - * @param account The account of the credential. - * @return A promise resolving to true if there was a secret for that service and account. - */ - export function deleteSecret(service: string, account: string): Thenable; - } - /** * Represents a color in RGBA space. */ diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index b22f7c6df8b..0a09b6598d0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -85,7 +85,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape { if (process) { return process.getId(); } - return TPromise.wrapError(new Error('cannot create debug session')); + return TPromise.wrapError(new Error('cannot create debug session')); }, err => { return TPromise.wrapError(err && err.message ? err.message : 'cannot start debug session'); }); diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts index 1c5186a7144..d0fcfb8b158 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts @@ -99,10 +99,10 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._modelIsSynced = {}; this._toDispose = []; - this._toDispose.push(this._modelReferenceCollection); this._toDispose.push(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this))); this._toDispose.push(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this))); - modelService.onModelModeChanged(this._onModelModeChanged, this, this._toDispose); + this._toDispose.push(this._modelReferenceCollection); + this._toDispose.push(modelService.onModelModeChanged(this._onModelModeChanged, this)); this._toDispose.push(textFileService.models.onModelSaved(e => { if (this._shouldHandleFileEvent(e)) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts index 13a6cb85560..eb9246d74b4 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts @@ -113,20 +113,22 @@ class MainThreadDocumentAndEditorStateComputer { private _toDispose: IDisposable[] = []; private _toDisposeOnEditorRemove = new Map(); - private _onDidChangeState = new Emitter(); private _currentState: DocumentAndEditorState; - readonly onDidChangeState: Event = this._onDidChangeState.event; - constructor( + private readonly _onDidChangeState: (delta: DocumentAndEditorStateDelta) => void, @IModelService private _modelService: IModelService, @ICodeEditorService private _codeEditorService: ICodeEditorService, @IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService ) { - this._modelService.onModelAdded(this.updateState, this, this._toDispose); - this._modelService.onModelRemoved(this.updateState, this, this._toDispose); + this._modelService.onModelAdded(this._updateState, this, this._toDispose); + this._modelService.onModelRemoved(this._updateState, this, this._toDispose); + this._codeEditorService.onCodeEditorAdd(this._onDidAddEditor, this, this._toDispose); this._codeEditorService.onCodeEditorRemove(this._onDidRemoveEditor, this, this._toDispose); + this._codeEditorService.listCodeEditors().forEach(this._onDidAddEditor, this); + + this._updateState(); } dispose(): void { @@ -134,10 +136,10 @@ class MainThreadDocumentAndEditorStateComputer { } private _onDidAddEditor(e: ICommonCodeEditor): void { - this._toDisposeOnEditorRemove.set(e.getId(), e.onDidChangeModel(() => this.updateState())); - this._toDisposeOnEditorRemove.set(e.getId(), e.onDidFocusEditor(() => this.updateState())); - this._toDisposeOnEditorRemove.set(e.getId(), e.onDidBlurEditor(() => this.updateState())); - this.updateState(); + this._toDisposeOnEditorRemove.set(e.getId(), e.onDidChangeModel(() => this._updateState())); + this._toDisposeOnEditorRemove.set(e.getId(), e.onDidFocusEditor(() => this._updateState())); + this._toDisposeOnEditorRemove.set(e.getId(), e.onDidBlurEditor(() => this._updateState())); + this._updateState(); } private _onDidRemoveEditor(e: ICommonCodeEditor): void { @@ -145,11 +147,11 @@ class MainThreadDocumentAndEditorStateComputer { if (sub) { this._toDisposeOnEditorRemove.delete(e.getId()); sub.dispose(); - this.updateState(); + this._updateState(); } } - updateState(): void { + private _updateState(): void { // models: ignore too large models const models = this._modelService.getModels(); @@ -207,7 +209,7 @@ class MainThreadDocumentAndEditorStateComputer { const delta = DocumentAndEditorState.compute(this._currentState, newState); if (!delta.isEmpty) { this._currentState = newState; - this._onDidChangeState.fire(delta); + this._onDidChangeState(delta); } } } @@ -244,7 +246,6 @@ export class MainThreadDocumentsAndEditors { @ITelemetryService telemetryService: ITelemetryService ) { this._proxy = extHostContext.get(ExtHostContext.ExtHostDocumentsAndEditors); - this._stateComputer = new MainThreadDocumentAndEditorStateComputer(_modelService, codeEditorService, _workbenchEditorService); const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService); extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments); @@ -252,18 +253,18 @@ export class MainThreadDocumentsAndEditors { const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService); extHostContext.set(MainContext.MainThreadEditors, mainThreadEditors); + // It is expected that the ctor of the state computer calls our `_onDelta`. + this._stateComputer = new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, _workbenchEditorService); + this._toDispose = [ mainThreadDocuments, mainThreadEditors, this._stateComputer, - this._stateComputer.onDidChangeState(this._onDelta, this), this._onTextEditorAdd, this._onTextEditorRemove, this._onDocumentAdd, this._onDocumentRemove, ]; - - this._stateComputer.updateState(); } dispose(): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadErrors.ts b/src/vs/workbench/api/electron-browser/mainThreadErrors.ts index c2ee7a0baa2..035895cc5d0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadErrors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadErrors.ts @@ -4,17 +4,25 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as errors from 'vs/base/common/errors'; +import { SerializedError, onUnexpectedError } from 'vs/base/common/errors'; import { MainThreadErrorsShape, MainContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; @extHostNamedCustomer(MainContext.MainThreadErrors) export class MainThreadErrors implements MainThreadErrorsShape { - public dispose(): void { + dispose(): void { + // } - public $onUnexpectedExtHostError(err: any): void { - errors.onUnexpectedError(err); + $onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void { + if (err.$isError) { + const { name, message, stack } = err; + err = new Error(); + err.message = extensionId ? `[${extensionId}] ${message}` : message; + err.name = name; + err.stack = stack; + } + onUnexpectedError(err); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 17a6ee5926b..1638a01d7f2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -265,7 +265,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise { this._registrations[handle] = modes.LinkProviderRegistry.register(selector, { provideLinks: (model, token) => { - return wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri)); + return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri))); }, resolveLink: (link, token) => { return wireCancellationToken(token, this._proxy.$resolveDocumentLink(handle, link)); diff --git a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts index abda4e128e8..656064fc982 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts @@ -9,32 +9,35 @@ import { IMessageService, IChoiceService } from 'vs/platform/message/common/mess import Severity from 'vs/base/common/severity'; import { Action } from 'vs/base/common/actions'; import { TPromise as Promise } from 'vs/base/common/winjs.base'; -import { MainThreadMessageServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import * as vscode from 'vscode'; +import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../node/extHost.protocol'; import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; +import { IExtensionService } from "vs/platform/extensions/common/extensions"; @extHostNamedCustomer(MainContext.MainThreadMessageService) export class MainThreadMessageService implements MainThreadMessageServiceShape { constructor( extHostContext: IExtHostContext, - @IMessageService private _messageService: IMessageService, - @IChoiceService private _choiceService: IChoiceService + @IExtensionService private readonly _extensionService: IExtensionService, + @IMessageService private readonly _messageService: IMessageService, + @IChoiceService private readonly _choiceService: IChoiceService ) { + // } - public dispose(): void { + dispose(): void { + // } - $showMessage(severity: Severity, message: string, options: vscode.MessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { + $showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { if (options.modal) { - return this.showModalMessage(severity, message, commands); + return this._showModalMessage(severity, message, commands); } else { - return this.showMessage(severity, message, commands); + return this._showMessage(severity, message, commands, options.extensionId); } } - private showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { + private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extensionId: string): Thenable { return new Promise(resolve => { @@ -68,12 +71,13 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { messageHide = this._messageService.show(severity, { message, - actions + actions, + source: extensionId }); }); } - private showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { + private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { let cancelId: number | undefined = void 0; const options = commands.map((command, index) => { diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index 52b1ba27df4..ed31354268d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -62,7 +62,10 @@ class TreeViewDataProvider implements ITreeViewDataProvider { .then(elements => { this.postGetElements(null, elements); return elements; - }, err => this.messageService.show(Severity.Error, err)); + }, err => { + this.messageService.show(Severity.Error, err); + return null; + }); } getChildren(treeItem: ITreeItem): TPromise { @@ -73,7 +76,10 @@ class TreeViewDataProvider implements ITreeViewDataProvider { .then(children => { this.postGetElements(treeItem.handle, children); return children; - }, err => this.messageService.show(Severity.Error, err)); + }, err => { + this.messageService.show(Severity.Error, err); + return null; + }); } refresh(treeItemHandles: number[]) { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 7234a7a1cdd..c410c99b202 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -47,7 +47,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as vscode from 'vscode'; import * as paths from 'vs/base/common/paths'; -import { realpath } from 'fs'; import { MainContext, ExtHostContext, IInitData } from './extHost.protocol'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; @@ -326,13 +325,13 @@ export function createApiFactory( return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables); }, showInformationMessage(message, first, ...rest) { - return extHostMessageService.showMessage(Severity.Info, message, first, rest); + return extHostMessageService.showMessage(extension.id, Severity.Info, message, first, rest); }, showWarningMessage(message, first, ...rest) { - return extHostMessageService.showMessage(Severity.Warning, message, first, rest); + return extHostMessageService.showMessage(extension.id, Severity.Warning, message, first, rest); }, showErrorMessage(message, first, ...rest) { - return extHostMessageService.showMessage(Severity.Error, message, first, rest); + return extHostMessageService.showMessage(extension.id, Severity.Error, message, first, rest); }, showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) { return extHostQuickOpen.showQuickPick(items, options, token); @@ -367,7 +366,7 @@ export function createApiFactory( }, // proposed API sampleFunction: proposedApiFunction(extension, () => { - return extHostMessageService.showMessage(Severity.Info, 'Hello Proposed Api!', {}, []); + return extHostMessageService.showMessage(extension.id, Severity.Info, 'Hello Proposed Api!', {}, []); }), }; @@ -507,7 +506,7 @@ export function createApiFactory( }; // namespace: credentials - const credentials: typeof vscode.credentials = { + const credentials = { readSecret(service: string, account: string): Thenable { return extHostCredentials.readSecret(service, account); }, @@ -531,7 +530,6 @@ export function createApiFactory( workspace, scm, debug, - credentials, // types CancellationTokenSource: CancellationTokenSource, CodeLens: extHostTypes.CodeLens, @@ -585,8 +583,8 @@ export function createApiFactory( Task: extHostTypes.Task, ConfigurationTarget: extHostTypes.ConfigurationTarget }; - if (!extension.enableProposedApi) { - delete api.credentials; // Instead of error to avoid #31854 + if (extension.enableProposedApi && extension.isBuiltin) { + api['credentials'] = credentials; } return api; }; @@ -621,30 +619,7 @@ class Extension implements vscode.Extension { } export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise { - return createExtensionPathIndex(extensionService).then(trie => defineAPI(apiFactory, trie)); -} - -function createExtensionPathIndex(extensionService: ExtHostExtensionService): TPromise> { - - // create trie to enable fast 'filename -> extension id' look up - const trie = new TrieMap(TrieMap.PathSplitter); - const extensions = extensionService.getAllExtensionDescriptions().map(ext => { - if (!ext.main) { - return undefined; - } - return new TPromise((resolve, reject) => { - realpath(ext.extensionFolderPath, (err, path) => { - if (err) { - reject(err); - } else { - trie.insert(path, ext); - resolve(void 0); - } - }); - }); - }); - - return TPromise.join(extensions).then(() => trie); + return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie)); } function defineAPI(factory: IExtensionApiFactory, extensionPaths: TrieMap): void { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 395ee8ae8e9..a653f9d00b2 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -47,6 +47,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { ITreeItem } from 'vs/workbench/parts/views/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDisposable } from "vs/base/common/lifecycle"; +import { SerializedError } from "vs/base/common/errors"; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -189,7 +190,7 @@ export interface MainThreadTreeViewsShape extends IDisposable { } export interface MainThreadErrorsShape extends IDisposable { - $onUnexpectedExtHostError(err: any): void; + $onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void; } export interface MainThreadLanguageFeaturesShape extends IDisposable { @@ -221,8 +222,13 @@ export interface MainThreadLanguagesShape extends IDisposable { $getLanguages(): TPromise; } +export interface MainThreadMessageOptions { + extensionId?: string; + modal?: boolean; +} + export interface MainThreadMessageServiceShape extends IDisposable { - $showMessage(severity: Severity, message: string, options: vscode.MessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable; + $showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable; } export interface MainThreadOutputServiceShape extends IDisposable { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 0b92e69bd94..e2f327c3efb 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -14,10 +14,12 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; -import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData } from './extHost.protocol'; +import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape } from './extHost.protocol'; import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule } from "vs/workbench/api/node/extHostExtensionActivator"; import { Barrier } from "vs/workbench/services/extensions/node/barrier"; import { ExtHostThreadService } from "vs/workbench/services/thread/node/extHostThreadService"; +import { realpath } from 'fs'; +import { TrieMap } from "vs/base/common/map"; class ExtensionMemento implements IExtensionMemento { @@ -105,7 +107,7 @@ class ExtensionStoragePath { } } -export class ExtHostExtensionService { +export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _barrier: Barrier; private readonly _registry: ExtensionDescriptionRegistry; @@ -115,7 +117,7 @@ export class ExtHostExtensionService { private readonly _storagePath: ExtensionStoragePath; private readonly _proxy: MainThreadExtensionServiceShape; private _activator: ExtensionsActivator; - + private _extensionPathIndex: TPromise>; /** * This class is constructed manually because it is a service, so it doesn't use any ctor injection */ @@ -202,6 +204,31 @@ export class ExtHostExtensionService { } } + // create trie to enable fast 'filename -> extension id' look up + public getExtensionPathIndex(): TPromise> { + if (!this._extensionPathIndex) { + const trie = new TrieMap(); + const extensions = this.getAllExtensionDescriptions().map(ext => { + if (!ext.main) { + return undefined; + } + return new TPromise((resolve, reject) => { + realpath(ext.extensionFolderPath, (err, path) => { + if (err) { + reject(err); + } else { + trie.insert(path, ext); + resolve(void 0); + } + }); + }); + }); + this._extensionPathIndex = TPromise.join(extensions).then(() => trie); + } + return this._extensionPathIndex; + } + + public deactivate(extensionId: string): TPromise { let result: TPromise = TPromise.as(void 0); diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index fa223101e15..8e0c521684c 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -634,10 +634,12 @@ class SignatureHelpAdapter { class LinkProviderAdapter { private _documents: ExtHostDocuments; + private _heapService: ExtHostHeapService; private _provider: vscode.DocumentLinkProvider; - constructor(documents: ExtHostDocuments, provider: vscode.DocumentLinkProvider) { + constructor(documents: ExtHostDocuments, heapService: ExtHostHeapService, provider: vscode.DocumentLinkProvider) { this._documents = documents; + this._heapService = heapService; this._provider = provider; } @@ -645,23 +647,37 @@ class LinkProviderAdapter { const doc = this._documents.getDocumentData(resource).document; return asWinJsPromise(token => this._provider.provideDocumentLinks(doc, token)).then(links => { - if (Array.isArray(links)) { - return links.map(TypeConverters.DocumentLink.from); + if (!Array.isArray(links)) { + return undefined; } - return undefined; + const result: modes.ILink[] = []; + for (const link of links) { + let data = TypeConverters.DocumentLink.from(link); + let id = this._heapService.keep(link); + ObjectIdentifier.mixin(data, id); + result.push(data); + } + return result; }); } resolveLink(link: modes.ILink): TPromise { - if (typeof this._provider.resolveDocumentLink === 'function') { - return asWinJsPromise(token => this._provider.resolveDocumentLink(TypeConverters.DocumentLink.to(link), token)).then(value => { - if (value) { - return TypeConverters.DocumentLink.from(value); - } - return undefined; - }); + if (typeof this._provider.resolveDocumentLink !== 'function') { + return undefined; } - return undefined; + + const id = ObjectIdentifier.of(link); + const item = this._heapService.get(id); + if (!item) { + return undefined; + } + + return asWinJsPromise(token => this._provider.resolveDocumentLink(item, token)).then(value => { + if (value) { + return TypeConverters.DocumentLink.from(value); + } + return undefined; + }); } } @@ -993,7 +1009,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { const handle = this._nextHandle(); - this._adapter.set(handle, new LinkProviderAdapter(this._documents, provider)); + this._adapter.set(handle, new LinkProviderAdapter(this._documents, this._heapService, provider)); this._proxy.$registerDocumentLinkProvider(handle, selector); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/node/extHostMessageService.ts b/src/vs/workbench/api/node/extHostMessageService.ts index 6ae9309f3ce..3ac25ae5226 100644 --- a/src/vs/workbench/api/node/extHostMessageService.ts +++ b/src/vs/workbench/api/node/extHostMessageService.ts @@ -6,22 +6,13 @@ import Severity from 'vs/base/common/severity'; import vscode = require('vscode'); -import { MainContext, MainThreadMessageServiceShape, IMainContext } from './extHost.protocol'; +import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol'; -const emptyMessageOptions: vscode.MessageOptions = Object.create(null); function isMessageItem(item: any): item is vscode.MessageItem { return item && item.title; } -function parseMessageArguments(first: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): { options: vscode.MessageOptions; items: (string | vscode.MessageItem)[]; } { - if (typeof first === 'string' || isMessageItem(first)) { - return { options: emptyMessageOptions, items: [first, ...rest] }; - } else { - return { options: first || emptyMessageOptions, items: rest }; - } -} - export class ExtHostMessageService { private _proxy: MainThreadMessageServiceShape; @@ -30,10 +21,20 @@ export class ExtHostMessageService { this._proxy = mainContext.get(MainContext.MainThreadMessageService); } - showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable; - showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable; - showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable { - const { options, items } = parseMessageArguments(optionsOrFirstItem, rest); + showMessage(extensionId: string, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable; + showMessage(extensionId: string, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable; + showMessage(extensionId: string, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable { + + let options: MainThreadMessageOptions = { extensionId }; + let items: (string | vscode.MessageItem)[]; + + if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) { + items = [optionsOrFirstItem, ...rest]; + } else { + options.modal = optionsOrFirstItem.modal; + items = rest; + } + const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = []; for (let handle = 0; handle < items.length; handle++) { diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index c990fba30bd..763969acc96 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -542,10 +542,10 @@ export class ExtHostTextEditor implements vscode.TextEditor { return TPromise.as(undefined); } return callback().then(() => this, err => { - if (err instanceof Error && err.name === 'DISPOSED') { - return; + if (!(err instanceof Error && err.name === 'DISPOSED')) { + console.warn(err); } - console.warn(err); + return null; }); } } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 994bcb27dc5..d154a8be24d 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1029,14 +1029,14 @@ export class Color { static fromHSLA(hue: number, saturation: number, luminance: number, alpha: number): Color { const color = new BaseColor(new HSLA(hue, saturation, luminance, alpha)).rgba; - return new Color(color.r, color.g, color.b, color.a / 255); + return new Color(color.r, color.g, color.b, color.a); } static fromHex(hex: string): Color | null { let baseColor = BaseColor.Format.CSS.parseHex(hex); if (baseColor) { const rgba = baseColor.rgba; - return new Color(rgba.r, rgba.g, rgba.b, rgba.a / 255); + return new Color(rgba.r, rgba.g, rgba.b, rgba.a); } return null; } diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 247cd788487..9eee812338d 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -98,6 +98,7 @@ export abstract class BaseWorkspacesAction extends Action { } const res = this.windowService.showMessageBox(opts); + return !buttons[res].canceled; } @@ -117,41 +118,6 @@ export abstract class BaseWorkspacesAction extends Action { } } -export class NewWorkspaceFromExistingAction extends BaseWorkspacesAction { - - static ID = 'workbench.action.newWorkspaceFromExisting'; - static LABEL = nls.localize('newWorkspaceFormExisting', "New Workspace From Existing..."); - - constructor( - id: string, - label: string, - @IWindowService windowService: IWindowService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEnvironmentService environmentService: IEnvironmentService, - @IWorkspacesService protected workspacesService: IWorkspacesService, - @IWindowsService protected windowsService: IWindowsService, - ) { - super(id, label, windowService, environmentService, contextService); - } - - public run(): TPromise { - if (this.contextService.hasWorkspace()) { - let folders = this.pickFolders(mnemonicLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace")); - if (folders && folders.length) { - if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "To open multiple folders, window reload is required."))) { - return this.createWorkspace([this.contextService.getWorkspace().roots[0], ...folders.map(folder => URI.file(folder))]); - } - } - } - return TPromise.as(null); - } - - private createWorkspace(folders: URI[]): TPromise { - return this.workspacesService.createWorkspace(distinct(folders.map(folder => folder.toString(true /* encoding */)))) - .then(({ configPath }) => this.windowsService.openWindow([configPath])); - } -} - export class AddRootFolderAction extends BaseWorkspacesAction { static ID = 'workbench.action.addRootFolder'; @@ -172,11 +138,11 @@ export class AddRootFolderAction extends BaseWorkspacesAction { public run(): TPromise { if (!this.contextService.hasWorkspace()) { - return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL).run(); + return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, []).run(); } if (this.contextService.hasFolderWorkspace()) { - return this.instantiationService.createInstance(NewWorkspaceFromExistingAction, NewWorkspaceFromExistingAction.ID, NewWorkspaceFromExistingAction.LABEL).run(); + return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().roots).run(); } const folders = super.pickFolders(mnemonicLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace")); @@ -190,6 +156,42 @@ export class AddRootFolderAction extends BaseWorkspacesAction { } } +class NewWorkspaceAction extends BaseWorkspacesAction { + + static ID = 'workbench.action.newWorkspace'; + static LABEL = nls.localize('newWorkspace', "New Workspace..."); + + constructor( + id: string, + label: string, + private presetRoots: URI[], + @IWindowService windowService: IWindowService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWorkspacesService protected workspacesService: IWorkspacesService, + @IWindowsService protected windowsService: IWindowsService, + ) { + super(id, label, windowService, environmentService, contextService); + } + + public run(): TPromise { + const folders = this.pickFolders(mnemonicLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace")); + if (folders && folders.length) { + if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "To open multiple folders, window reload is required."))) { + return this.createWorkspace([...this.presetRoots, ...folders.map(folder => URI.file(folder))]); + } + } + + return TPromise.as(null); + } + + private createWorkspace(folders: URI[]): TPromise { + const workspaceFolders = distinct(folders.map(folder => folder.toString(true /* encoding */))); + + return this.windowService.createAndOpenWorkspace(workspaceFolders); + } +} + export class RemoveRootFolderAction extends Action { static ID = 'workbench.action.removeRootFolder'; @@ -249,15 +251,11 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { private saveFolderWorkspace(configPath: string): TPromise { if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('saveNotSupported', "To save workspace, window reload is required."))) { - // Create workspace first - this.workspacesService.createWorkspace(this.contextService.getWorkspace().roots.map(root => root.toString(true /* skip encoding */))) - .then(workspaceIdentifier => { - // Save the workspace in new location - return this.workspacesService.saveWorkspace(workspaceIdentifier, configPath) - // Open the saved workspace - .then(({ configPath }) => this.windowsService.openWindow([configPath])); - }); + const workspaceFolders = this.contextService.getWorkspace().roots.map(root => root.toString(true /* skip encoding */)); + + return this.windowService.createAndOpenWorkspace(workspaceFolders, configPath); } + return TPromise.as(null); } @@ -292,7 +290,6 @@ export class OpenWorkspaceAction extends Action { id: string, label: string, @IWindowService private windowService: IWindowService, - @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(id, label); } @@ -302,25 +299,6 @@ export class OpenWorkspaceAction extends Action { } } -class NewWorkspaceAction extends Action { - - static ID = 'workbench.action.newWorkspace'; - static LABEL = nls.localize('newWorkspace', "New Workspace..."); - - constructor( - id: string, - label: string, - @IWindowService private windowService: IWindowService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - super(id, label); - } - - public run(): TPromise { - return this.windowService.newWorkspace(); - } -} - export class OpenWorkspaceConfigFileAction extends Action { public static ID = 'workbench.action.openWorkspaceConfigFile'; @@ -333,6 +311,7 @@ export class OpenWorkspaceConfigFileAction extends Action { @IWorkbenchEditorService private editorService: IWorkbenchEditorService ) { super(id, label); + this.enabled = this.workspaceContextService.hasMultiFolderWorkspace(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 6c625a26b61..abf883ccba4 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -403,7 +403,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return editor; - }, e => this.messageService.show(Severity.Error, types.isString(e) ? new Error(e) : e)); + }, e => { + this.messageService.show(Severity.Error, types.isString(e) ? new Error(e) : e); + return null; + }); } private doCreateEditor(group: EditorGroup, descriptor: IEditorDescriptor, monitor: ProgressMonitor): TPromise { @@ -513,7 +516,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Fullfill promise with Editor that is being used return editor; - }, e => this.doHandleSetInputError(e, group, editor, input, options, monitor)); + }, e => { + this.doHandleSetInputError(e, group, editor, input, options, monitor); + return null; + }); } private doHandleSetInputError(e: Error | IMessageWithAction, group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): void { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 242abde192d..c0eb5a14a1b 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -44,7 +44,6 @@ import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; interface IEditorInputLabel { - editor: IEditorInput; name: string; hasAmbiguousName?: boolean; description?: string; @@ -325,10 +324,14 @@ export class TabsTitleControl extends TitleControl { // Build labels and descriptions for each editor editors.forEach(editor => { + const name = editor.getName(); let description = editor.getDescription(); + if (mapLabelAndDescriptionToDuplicates.has(`${name}${description}`)) { + description = editor.getDescription(true); // try verbose description if name+description already exists + } + const item: IEditorInputLabel = { - editor, - name: editor.getName(), + name, description, title: editor.getTitle(Verbosity.LONG) }; @@ -351,7 +354,7 @@ export class TabsTitleControl extends TitleControl { }); if (duplicates.length > 1) { - const shortenedDescriptions = shorten(duplicates.map(duplicate => duplicate.editor.getDescription())); + const shortenedDescriptions = shorten(duplicates.map(duplicate => duplicate.description)); duplicates.forEach((duplicate, i) => { duplicate.description = shortenedDescriptions[i]; duplicate.hasAmbiguousName = true; diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 2670b54ac62..084c5d086f3 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -207,6 +207,7 @@ export class QuickOpenController extends Component implements IQuickOpenService }); }, err => { // ignore + return null; }); } } diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 0870ae8b268..593ee95af9c 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -23,7 +23,7 @@ import { ContextViewService } from 'vs/platform/contextview/browser/contextViewS import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, configurationTelemetry, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; -import { loadExperiments } from 'vs/platform/telemetry/common/experiments'; +import { IExperimentService, ExperimentService } from 'vs/platform/telemetry/common/experiments'; import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { IdleMonitor, UserStatus } from 'vs/platform/telemetry/browser/idleMonitor'; @@ -117,6 +117,7 @@ export class WorkbenchShell { private configurationService: IConfigurationService; private contextService: IWorkspaceContextService; private telemetryService: ITelemetryService; + private experimentService: IExperimentService; private extensionService: ExtensionService; private broadcastService: IBroadcastService; private timerService: ITimerService; @@ -208,7 +209,7 @@ export class WorkbenchShell { customKeybindingsCount: info.customKeybindingsCount, theme: this.themeService.getColorTheme().id, language: platform.language, - experiments: loadExperiments(), + experiments: this.experimentService.getExperiments(), pinnedViewlets: info.pinnedViewlets, restoredViewlet: info.restoredViewlet, restoredEditors: info.restoredEditors.length, @@ -263,6 +264,10 @@ export class WorkbenchShell { restoreFontInfo(this.storageService); readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getConfiguration('editor'), browser.getZoomLevel())); + // Experiments + this.experimentService = instantiationService.createInstance(ExperimentService); + serviceCollection.set(IExperimentService, this.experimentService); + // Telemetry this.sendMachineIdToMain(this.storageService); if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) { diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 2fcb43ba60c..0e86283dd78 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -12,6 +12,7 @@ import { join } from 'path'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; +import { IExtensionDescription } from "vs/platform/extensions/common/extensions"; import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; import { RemoteTelemetryService } from 'vs/workbench/api/node/extHostTelemetry'; @@ -49,9 +50,26 @@ export class ExtensionHostMain { const telemetryService = new RemoteTelemetryService('pluginHostTelemetry', threadService); this._extensionService = new ExtHostExtensionService(initData, threadService, telemetryService); - // Error forwarding + // error forwarding and stack trace scanning + const extensionErrors = new WeakMap(); + this._extensionService.getExtensionPathIndex().then(map => { + (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { + let stackTraceMessage = ''; + let extension: IExtensionDescription; + for (const call of stackTrace) { + stackTraceMessage += `\n\tat ${call.toString()}`; + extension = extension || map.findSubstr(stackTrace[0].getFileName()); + } + extensionErrors.set(error, extension); + return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; + }; + }); const mainThreadErrors = threadService.get(MainContext.MainThreadErrors); - errors.setUnexpectedErrorHandler(err => mainThreadErrors.$onUnexpectedExtHostError(errors.transformErrorForSerialization(err))); + errors.setUnexpectedErrorHandler(err => { + const data = errors.transformErrorForSerialization(err); + const extension = extensionErrors.get(err); + mainThreadErrors.$onUnexpectedError(data, extension && extension.id); + }); // Configure the watchdog to kill our process if the JS event loop is unresponsive for more than 10s if (!initData.environment.isExtensionDevelopmentDebug) { diff --git a/src/vs/workbench/parts/backup/common/backupRestorer.ts b/src/vs/workbench/parts/backup/common/backupRestorer.ts index 26c470d63bd..f035909c3e9 100644 --- a/src/vs/workbench/parts/backup/common/backupRestorer.ts +++ b/src/vs/workbench/parts/backup/common/backupRestorer.ts @@ -62,7 +62,7 @@ export class BackupRestorer implements IWorkbenchContribution { private doResolveOpenedBackups(backups: URI[]): TPromise { const stacks = this.groupService.getStacksModel(); - const restorePromises: TPromise[] = []; + const restorePromises: TPromise[] = []; const unresolved: URI[] = []; backups.forEach(backup => { diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts index c852b905cd1..3aa4c25c532 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -81,7 +81,9 @@ class InstallAction extends Action { }); } }) - .then(() => this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName))); + .then(() => { + this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName)); + }); }); } @@ -141,8 +143,9 @@ class UninstallAction extends Action { return pfs.unlink(this.target) .then(null, ignore('ENOENT')) - .then(() => this.messageService.show(Severity.Info, nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName))) - .then(null); + .then(() => { + this.messageService.show(Severity.Info, nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName)); + }); }); } } diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index c4fd3c05bc8..86c56ed37d9 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -167,6 +167,7 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { if (options.length === 0) { options.push(nls.localize('noConfigurations', "No Configurations")); + this.executeOnSelect.push(() => false); } options.push(StartDebugActionItem.SEPARATOR); this.executeOnSelect.push(undefined); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 4cdd77f497c..5089c0e4720 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -617,7 +617,7 @@ export class DebugService implements debug.IDebugService { this.model.removeWatchExpressions(id); } - public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false): TPromise { + public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise { // make sure to save all files and that the configuration is up to date return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() => @@ -640,7 +640,8 @@ export class DebugService implements debug.IDebugService { config = configOrName; } if (launch) { - manager.selectConfiguration(launch, typeof configOrName === 'string' ? configOrName : undefined, true); + // in the drop down the name of the top most compound takes precedence over the launch config name + manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true); } if (compound) { @@ -649,13 +650,13 @@ export class DebugService implements debug.IDebugService { "Compound must have \"configurations\" attribute set in order to start multiple configurations."))); } - return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name) : TPromise.as(null))); + return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null))); } if (configOrName && !config) { return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName))); } - return manager.getStartSessionCommand(config ? config.type : undefined).then(commandAndType => { + return manager.getStartSessionCommand(config ? config.type : undefined).then(commandAndType => { if (noDebug && config) { config.noDebug = true; } @@ -752,13 +753,15 @@ export class DebugService implements debug.IDebugService { }); }, err => { if (!this.contextService.hasWorkspace()) { - return this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); + this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); + return undefined; } return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => { if (openend) { this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message)); } + return undefined; }); }) ); diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index fc22516ec9e..3719581c61a 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -1166,6 +1166,34 @@ export class ShowLanguageExtensionsAction extends Action { } } +export class ShowAzureExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showAzureExtensions'; + static LABEL = localize('showAzureExtensions', "Show Azure Extensions"); + static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@sort:installs azure '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + export class ChangeSortAction extends Action { private query: Query; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index 826cb9e9a3c..470b1613dac 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -24,7 +24,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/e import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, - EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction + EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; @@ -121,6 +121,9 @@ actionRegistry.registerWorkbenchAction(keymapRecommendationsActionDescriptor, 'P const languageExtensionsActionDescriptor = new SyncActionDescriptor(ShowLanguageExtensionsAction, ShowLanguageExtensionsAction.ID, ShowLanguageExtensionsAction.SHORT_LABEL); actionRegistry.registerWorkbenchAction(languageExtensionsActionDescriptor, 'Preferences: Language Extensions', PreferencesLabel); +const azureExtensionsActionDescriptor = new SyncActionDescriptor(ShowAzureExtensionsAction, ShowAzureExtensionsAction.ID, ShowAzureExtensionsAction.SHORT_LABEL); +actionRegistry.registerWorkbenchAction(azureExtensionsActionDescriptor, 'Preferences: Azure Extensions', PreferencesLabel); + const workspaceRecommendationsActionDescriptor = new SyncActionDescriptor(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(workspaceRecommendationsActionDescriptor, 'Extensions: Show Workspace Recommended Extensions', ExtensionsLabel); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index c33a4ecd7d4..bfd75970a59 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -79,7 +79,7 @@ export class KeymapExtensions implements IWorkbenchContribution { localize('no', "No") ]; return this.choiceService.choose(Severity.Info, message, options, 1, false) - .then(value => { + .then(value => { const confirmed = value === 0; telemetryData['confirmed'] = confirmed; this.telemetryService.publicLog('disableOtherKeymaps', telemetryData); @@ -89,7 +89,8 @@ export class KeymapExtensions implements IWorkbenchContribution { })); } return undefined; - }, error => TPromise.wrapError(canceled())); + }, error => TPromise.wrapError(canceled())) + .then(() => { /* drop resolved value */ }); } dispose(): void { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 15cca4f0517..f6a30f32c9a 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -813,8 +813,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { const extensionId = match[1]; - this.queryGallery({ names: [extensionId] }) - .then(result => { + this.queryLocal().then(local => { + if (local.some(local => local.id === extensionId)) { + return TPromise.as(null); + } + + return this.queryGallery({ names: [extensionId] }).then(result => { if (result.total < 1) { return TPromise.as(null); } @@ -826,20 +830,16 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { nls.localize('install', "Install"), nls.localize('cancel', "Cancel") ]; - return this.choiceService.choose(Severity.Info, message, options, 2, false) - .then(value => { - if (value === 0) { - const promises: TPromise[] = []; - if (this.local.every(local => local.id !== extension.id)) { - promises.push(this.install(extension)); - } - return TPromise.join(promises); - } + return this.choiceService.choose(Severity.Info, message, options, 2, false).then(value => { + if (value !== 0) { return TPromise.as(null); - }); + } + + return this.install(extension); + }); }); - }) - .done(undefined, error => this.onError(error)); + }); + }).done(undefined, error => this.onError(error)); } dispose(): void { diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 5b431213cf5..6016dda0910 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1246,26 +1246,38 @@ export class CompareResourcesAction extends Action { constructor( resource: URI, tree: ITree, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEnvironmentService environmentService: IEnvironmentService ) { - super('workbench.files.action.compareFiles', CompareResourcesAction.computeLabel()); + super('workbench.files.action.compareFiles', CompareResourcesAction.computeLabel(resource, contextService, environmentService)); this.tree = tree; this.resource = resource; } - private static computeLabel(): string { + private static computeLabel(resource: URI, contextService: IWorkspaceContextService, environmentService: IEnvironmentService): string { if (globalResourceToCompare) { - return nls.localize('compareWith', "Compare with '{0}'", paths.basename(globalResourceToCompare.fsPath)); + let leftResourceName = paths.basename(globalResourceToCompare.fsPath); + let rightResourceName = paths.basename(resource.fsPath); + + // If the file names are identical, add more context by looking at the parent folder + if (leftResourceName === rightResourceName) { + const folderPaths = labels.shorten([ + labels.getPathLabel(paths.dirname(globalResourceToCompare.fsPath), contextService, environmentService), + labels.getPathLabel(paths.dirname(resource.fsPath), contextService, environmentService) + ]); + + leftResourceName = paths.join(folderPaths[0], leftResourceName); + rightResourceName = paths.join(folderPaths[1], rightResourceName); + } + + return nls.localize('compareWith', "Compare '{0}' with '{1}'", leftResourceName, rightResourceName); } return nls.localize('compareFiles', "Compare Files"); } - public getLabel(): string { - return CompareResourcesAction.computeLabel(); - } - _isEnabled(): boolean { // Need at least a resource to compare @@ -1326,7 +1338,10 @@ export abstract class BaseSaveFileAction extends BaseErrorReportingAction { } public run(context?: any): TPromise { - return this.doRun(context).then(() => true, error => this.onError(error)); + return this.doRun(context).then(() => true, error => { + this.onError(error); + return null; + }); } protected abstract doRun(context?: any): TPromise; diff --git a/src/vs/workbench/parts/files/browser/fileCommands.ts b/src/vs/workbench/parts/files/browser/fileCommands.ts index e38fb596c2c..be7013d7286 100644 --- a/src/vs/workbench/parts/files/browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/browser/fileCommands.ts @@ -215,12 +215,12 @@ function withFocusedExplorerItem(accessor: ServicesAccessor): TPromise { if (res) { - return res.item; + return res.item as FileStat | OpenEditor; } return void 0; }); - }) as TPromise; // TypeScript fail + }); }; export const renameFocusedFilesExplorerViewItemCommand = (accessor: ServicesAccessor) => { diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index d28ad5904f1..679a6fae06e 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -866,7 +866,7 @@ export class ExplorerView extends CollapsibleView { // Select and Reveal return this.explorerViewer.refresh(root).then(() => this.doSelect(root.find(resource), reveal)); - }, e => this.messageService.show(Severity.Error, e)); + }, e => { this.messageService.show(Severity.Error, e); }); } private hasSelection(resource: URI): FileStat { diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index 3282cfd22f9..3907902d276 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -353,8 +353,11 @@ export class FileRenderer implements IRenderer { this.state.actionProvider.runAction(tree, stat, editableData.action, { value: inputBox.value }); } + const restoreFocus = document.activeElement === inputBox.inputElement; // https://github.com/Microsoft/vscode/issues/20269 setTimeout(() => { - tree.DOMFocus(); + if (restoreFocus) { + tree.DOMFocus(); + } lifecycle.dispose(toDispose); container.removeChild(label.element); }, 0); diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 8700ed4ecdd..4ab1512df6a 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -33,6 +33,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private name: string; private description: string; + private verboseDescription: string; private shortTitle: string; private mediumTitle: string; @@ -127,12 +128,18 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { return this.decorateOrphanedFiles(this.name); } - public getDescription(): string { - if (!this.description) { - this.description = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService); + public getDescription(verbose?: boolean): string { + if (verbose) { + if (!this.verboseDescription) { + this.verboseDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService); + } + } else { + if (!this.description) { + this.description = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService); + } } - return this.description; + return verbose ? this.verboseDescription : this.description; } public getTitle(verbosity: Verbosity): string { @@ -221,7 +228,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } // Bubble any other error up - return TPromise.wrapError(error); + return TPromise.wrapError(error); }); } diff --git a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts index 5ac35b0980e..8b04c59fd2f 100644 --- a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -23,12 +23,10 @@ import { join } from 'path'; import { localize } from 'vs/nls'; import { toPromise, filterEvent } from 'vs/base/common/event'; import { platform, Platform } from 'vs/base/common/platform'; -import { readdir, stat } from 'vs/base/node/pfs'; +import { readdir } from 'vs/base/node/pfs'; import { release } from 'os'; import { stopProfiling } from 'vs/base/node/profiler'; import { virtualMachineHint } from 'vs/base/node/id'; -import { forEach } from 'vs/base/common/collections'; -import URI from 'vs/base/common/uri'; class ProfilingHint implements IWorkbenchContribution { @@ -215,46 +213,6 @@ class StartupProfiler implements IWorkbenchContribution { } } -class PerformanceTelemetry implements IWorkbenchContribution { - - constructor( - @ITimerService private readonly _timerService: ITimerService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IExtensionService extensionService: IExtensionService, - ) { - TPromise.join([ - TPromise.timeout(7 * 1000), - extensionService.onReady() - ]).then(() => { - this._sendWorkbenchMainSizeTelemetry(); - this._validateTimers(); - }); - } - - getId(): string { - return 'performance.PerformanceTelemetry'; - } - - private _validateTimers(): void { - const { startupMetrics } = this._timerService; - const invalidTimers: string[] = []; - forEach(startupMetrics.timers, (entry) => { - if (entry.value < 0) { - invalidTimers.push(entry.key); - } - }); - this._telemetryService.publicLog('perf:invalidTimers', { invalidTimers }); - } - - private _sendWorkbenchMainSizeTelemetry(): void { - const { fsPath } = URI.parse(require.toUrl('vs/workbench/workbench.main.js')); - stat(fsPath).then(stats => { - this._telemetryService.publicLog('perf:jsFileSize', { workbenchMain: stats.size }); - }); - } -} - const registry = Registry.as(Extensions.Workbench); registry.registerWorkbenchContribution(ProfilingHint); registry.registerWorkbenchContribution(StartupProfiler); -registry.registerWorkbenchContribution(PerformanceTelemetry); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 1ac73c1283e..046c8e24c15 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -340,15 +340,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); } - private createIfNotExists(resource: URI, contents: string): TPromise { + private createIfNotExists(resource: URI, contents: string): TPromise { return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => { if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { return this.fileService.updateContent(resource, contents).then(null, error => { - return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.contextService, this.environmentService), error))); + return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.contextService, this.environmentService), error))); }); } - return TPromise.wrapError(error); + return TPromise.wrapError(error); }); } diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css index e568bf2982c..dbbe3915e79 100644 --- a/src/vs/workbench/parts/search/browser/media/searchviewlet.css +++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css @@ -270,10 +270,12 @@ } .search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .filematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .foldermatch .monaco-count-badge, .search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .linematch .monaco-count-badge, .search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .filematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .foldermatch .monaco-count-badge, .search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .linematch .monaco-count-badge { - display: none; + display: none; } .search-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove, diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 8c79b5fa0ea..3eae07844b7 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -216,6 +216,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { }, (error: Error) => { this.pendingSearch = null; this.messageService.show(Severity.Error, error); + return null; }); return this.pendingSearch; diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 4e45a8e1d6b..67591e57e7d 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -494,7 +494,7 @@ export class SearchResult extends Disposable { public onChange: Event = this._onChange.event; private _folderMatches: FolderMatch[] = []; - private _folderMatchesMap: TrieMap = new TrieMap(TrieMap.PathSplitter); + private _folderMatchesMap: TrieMap = new TrieMap(); private _query: ISearchQuery = null; private _showHighlights: boolean; @@ -652,7 +652,7 @@ export class SearchResult extends Disposable { private disposeMatches(): void { this._folderMatches.forEach(folderMatch => folderMatch.dispose()); this._folderMatches = []; - this._folderMatchesMap = new TrieMap(TrieMap.PathSplitter); + this._folderMatchesMap = new TrieMap(); this._rangeHighlightDecorations.removeHighlightRange(); } diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts index 3490f5c2766..0a418364f12 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -92,7 +92,7 @@ class InsertSnippetAction extends EditorAction { if (langId) { languageId = modeService.getLanguageIdentifier(langId).id; } else { - editor.getModel().forceTokenization(lineNumber); + editor.getModel().tokenizeIfCheap(lineNumber); languageId = editor.getModel().getLanguageIdAtPosition(lineNumber, column); // validate the `languageId` to ensure this is a user diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index 7bd845976f0..0af3c024910 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -187,7 +187,7 @@ export class SnippetSuggestProvider implements ISuggestSupport { // validate the `languageId` to ensure this is a user // facing language with a name and the chance to have // snippets, else fall back to the outer language - model.forceTokenization(position.lineNumber); + model.tokenizeIfCheap(position.lineNumber); let languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column); let { language } = this._modeService.getLanguageIdentifier(languageId); if (!this._modeService.getLanguageName(language)) { diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index ed09f3635ba..eb29e67c023 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -232,7 +232,7 @@ export class ProcessRunnerDetector { let config = ProcessRunnerDetector.detectorConfig('gulp'); let process = new LineProcess('gulp', [config.arg, '--no-color'], true, { cwd: this._cwd }); return this.runDetection(process, 'gulp', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); - }, (err: any): TaskConfig.ExternalTaskRunnerConfiguration => { + }, (err: any) => { return null; }); } @@ -242,7 +242,7 @@ export class ProcessRunnerDetector { let config = ProcessRunnerDetector.detectorConfig('grunt'); let process = new LineProcess('grunt', [config.arg, '--no-color'], true, { cwd: this._cwd }); return this.runDetection(process, 'grunt', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); - }, (err: any): TaskConfig.ExternalTaskRunnerConfiguration => { + }, (err: any) => { return null; }); } @@ -258,7 +258,7 @@ export class ProcessRunnerDetector { }, (err: any) => { return this.fileService.resolveFile(this.contextService.toResource('Jakefile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return run(); - }, (err: any): TaskConfig.ExternalTaskRunnerConfiguration => { + }, (err: any) => { return null; }); }); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index fe64fdb45ed..719ca5cd644 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -226,7 +226,8 @@ configurationRegistry.registerConfiguration({ NavigateRightAction.ID, NavigateLeftAction.ID, DeleteWordLeftTerminalAction.ID, - DeleteWordRightTerminalAction.ID + DeleteWordRightTerminalAction.ID, + 'workbench.action.quickOpenNavigateNextInViewPicker' ].sort() }, 'terminal.integrated.env.osx': { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 19bee0cac6c..0ef23d537c1 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -13,7 +13,7 @@ import * as dom from 'vs/base/browser/dom'; import Event, { Emitter } from 'vs/base/common/event'; import Uri from 'vs/base/common/uri'; import { WindowsShellHelper } from 'vs/workbench/parts/terminal/electron-browser/windowsShellHelper'; -import XTermTerminal = require('xterm'); +import { Terminal as XTermTerminal } from 'xterm'; import { Dimension } from 'vs/base/browser/builder'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -280,7 +280,7 @@ export class TerminalInstance implements ITerminalInstance { dom.addClass(this._wrapperElement, 'terminal-wrapper'); this._xtermElement = document.createElement('div'); - this._xterm.open(this._xtermElement, false); + this._xterm.open(this._xtermElement); this._xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => { // Disable all input if the terminal is exiting if (this._isExiting) { @@ -560,8 +560,23 @@ export class TerminalInstance implements ITerminalInstance { this._configHelper.mergeDefaultShellPathAndArgs(shell); } this._initialCwd = this._getCwd(this._shellLaunchConfig, this._historyService.getLastActiveWorkspaceRoot()); - const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux'; - const envFromConfig = { ...process.env, ...this._configHelper.config.env[platformKey] }; + let envFromConfig: IStringDictionary; + if (platform.isWindows) { + envFromConfig = { ...process.env }; + for (let configKey in this._configHelper.config.env['windows']) { + let actualKey = configKey; + for (let envKey in envFromConfig) { + if (configKey.toLowerCase() === envKey.toLowerCase()) { + actualKey = envKey; + break; + } + } + envFromConfig[actualKey] = this._configHelper.config.env['windows'][configKey]; + } + } else { + const platformKey = platform.isMacintosh ? 'osx' : 'linux'; + envFromConfig = { ...process.env, ...this._configHelper.config.env[platformKey] }; + } const env = TerminalInstance.createTerminalEnv(envFromConfig, shell, this._initialCwd, locale, this._cols, this._rows); this._process = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], { env, @@ -667,7 +682,7 @@ export class TerminalInstance implements ITerminalInstance { args = this._shellLaunchConfig.args; } else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) { args = ' ' + this._shellLaunchConfig.args.map(a => { - if (a.indexOf(' ') !== -1) { + if (typeof a === 'string' && a.indexOf(' ') !== -1) { return `'${a}'`; } return a; @@ -768,7 +783,9 @@ export class TerminalInstance implements ITerminalInstance { } public onExit(listener: (exitCode: number) => void): lifecycle.IDisposable { - this._process.on('exit', listener); + if (this._process) { + this._process.on('exit', listener); + } return { dispose: () => { if (this._process) { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 0d357f399a6..fa7c86eee17 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -246,7 +246,7 @@ export class TerminalPanel extends Panel { uri = URI.parse(uri).path; } else if (e.dataTransfer.files.length > 0) { // Check if the file was dragged from the filesystem - uri = URI.file(e.dataTransfer.files[0].path).path; + uri = URI.file(e.dataTransfer.files[0].path).fsPath; } if (!uri) { diff --git a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts index 4effb4b9466..6dab973c67a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts @@ -10,7 +10,7 @@ import * as path from 'path'; import { TPromise } from 'vs/base/common/winjs.base'; import { Emitter, debounceEvent } from 'vs/base/common/event'; import { ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; -import XTermTerminal = require('xterm'); +import { Terminal as XTermTerminal } from 'xterm'; const SHELL_EXECUTABLES = ['cmd.exe', 'powershell.exe', 'bash.exe']; diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts index 926d92f29b3..0ad49659764 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts @@ -69,6 +69,7 @@ export default () => `

diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 0235e8fc9c7..530d3e2012b 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -23,6 +23,7 @@ import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExperimentService } from 'vs/platform/telemetry/common/experiments'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -114,8 +115,10 @@ export class WelcomePageAction extends Action { interface ExtensionSuggestion { name: string; + title?: string; id: string; isKeymap?: boolean; + isCommand?: boolean; } const extensionPacks: ExtensionSuggestion[] = [ @@ -124,6 +127,7 @@ const extensionPacks: ExtensionSuggestion[] = [ { name: localize('welcomePage.python', "Python"), id: 'donjayamanne.python' }, // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, + { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, { name: localize('welcomePage.docker', "Docker"), id: 'PeterJausovec.vscode-docker' }, ]; @@ -191,6 +195,7 @@ class WelcomePage { @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @ILifecycleService lifecycleService: ILifecycleService, @IThemeService private themeService: IThemeService, + @IExperimentService private experimentService: IExperimentService, @ITelemetryService private telemetryService: ITelemetryService ) { this.disposables.push(lifecycleService.onShutdown(() => this.dispose())); @@ -310,6 +315,12 @@ class WelcomePage { } }; })); + + if (this.experimentService.getExperiments().deployToAzureQuickLink) { + container.querySelector('.showInterfaceOverview').remove(); + } else { + container.querySelector('.deployToAzure').remove(); + } } private addExtensionList(container: HTMLElement, listSelector: string, suggestions: ExtensionSuggestion[], strings: Strings) { @@ -322,23 +333,28 @@ class WelcomePage { const a = document.createElement('a'); a.innerText = extension.name; - a.title = extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name); - a.classList.add('installExtension'); - a.setAttribute('data-extension', extension.id); - a.href = 'javascript:void(0)'; - a.addEventListener('click', e => { - this.installExtension(extension, strings); - e.preventDefault(); - e.stopPropagation(); - }); - list.appendChild(a); + a.title = extension.title || (extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name)); + if (extension.isCommand) { + a.href = `command:${extension.id}`; + list.appendChild(a); + } else { + a.classList.add('installExtension'); + a.setAttribute('data-extension', extension.id); + a.href = 'javascript:void(0)'; + a.addEventListener('click', e => { + this.installExtension(extension, strings); + e.preventDefault(); + e.stopPropagation(); + }); + list.appendChild(a); - const span = document.createElement('span'); - span.innerText = extension.name; - span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name); - span.classList.add('enabledExtension'); - span.setAttribute('data-extension', extension.id); - list.appendChild(span); + const span = document.createElement('span'); + span.innerText = extension.name; + span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name); + span.classList.add('enabledExtension'); + span.setAttribute('data-extension', extension.id); + list.appendChild(span); + } }); } } @@ -450,7 +466,7 @@ class WelcomePage { }) ] }); - }).then(null, err => { + }).then(null, err => { this.telemetryService.publicLog(strings.installedEvent, { from: telemetryFrom, extensionId: extensionSuggestion.id, diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md index f154fd91f57..1edc236faf6 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md @@ -144,13 +144,13 @@ You can greatly accelerate your editing through the use of snippets. Simply sta ### Emmet -Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. To use Emmet simply press tab after a valid piece for Emmet syntax and the expansion will occur. Try it by pressing tab after `ul>li.item$*5` to see Emmet in action. +Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. To use Emmet simply run the command `Emmet: Expand Abbreviation` with cursor at the end of a valid Emmet abbreviation or snippet and the expansion will occur. ```html ul>li.item$*5 ``` ->**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. Emmet can also be enabled for additional languages via the `emmet.syntaxProfiles` [setting](command:workbench.action.openGlobalSettings). +>**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. To expand Emmet abbreviations and snippets using the `tab` key use the `emmet.triggerExpansionOnTab` [setting](command:workbench.action.openGlobalSettings). Check out the docs on [Emmet in VS Code](https://code.visualstudio.com/docs/editor/emmet) to learn more. diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 259864baf1c..39dcec2fb80 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -153,10 +153,10 @@ export class ConfigurationEditingService implements IConfigurationEditingService this.commandService.executeCommand(ConfigurationTarget.USER === target ? 'workbench.action.openGlobalSettings' : 'workbench.action.openWorkspaceSettings'); } - private wrapError(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): TPromise { + private wrapError(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): TPromise { const message = this.toErrorMessage(code, target, operation); - return TPromise.wrapError(new ConfigurationEditingError(message, code)); + return TPromise.wrapError(new ConfigurationEditingError(message, code)); } private toErrorMessage(error: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): string { @@ -271,14 +271,14 @@ export class ConfigurationEditingService implements IConfigurationEditingService const model = reference.object.textEditorModel; if (this.hasParseErrors(model, operation)) { - return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation); + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation); } // Target cannot be dirty if not writing into buffer if (checkDirty && this.textFileService.isDirty(operation.resource)) { - return this.wrapError(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); + return this.wrapError(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); } - return TPromise.wrap(reference); + return reference; }); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index a3bc92f7c0b..b107064177a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -11,7 +11,6 @@ import { stringify } from 'vs/base/common/marshalling'; import * as objects from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Action } from 'vs/base/common/actions'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { findFreePort } from 'vs/base/node/ports'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; @@ -23,225 +22,240 @@ import { ChildProcess, fork } from 'child_process'; import { ipcRenderer as ipc } from 'electron'; import product from 'vs/platform/node/product'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; -import { createServer, Server } from 'net'; +import { createServer, Server, Socket } from 'net'; import Event, { Emitter, debounceEvent, mapEvent, any } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; import { IInitData, IWorkspaceData } from 'vs/workbench/api/node/extHost.protocol'; -import { ExtensionService } from "vs/workbench/services/extensions/electron-browser/extensionService"; +import { IExtensionService } from "vs/platform/extensions/common/extensions"; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService'; import { IBroadcastService, IBroadcast } from "vs/platform/broadcast/electron-browser/broadcastService"; import { isEqual } from "vs/base/common/paths"; import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, ILogEntry, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from "vs/platform/extensions/common/extensionHost"; - -export class LazyMessagePassingProtol implements IMessagePassingProtocol { - - private _delegate: IMessagePassingProtocol; - private _onMessage = new Emitter(); - private _buffer: any[] = []; - - readonly onMessage: Event = this._onMessage.event; - - send(msg: any): void { - if (this._delegate) { - this._delegate.send(msg); - } else { - this._buffer.push(msg); - } - } - - resolve(delegate: IMessagePassingProtocol): void { - this._delegate = delegate; - this._delegate.onMessage(data => this._onMessage.fire(data)); - this._buffer.forEach(this._delegate.send, this._delegate); - this._buffer = null; - } -} +import { IDisposable, dispose } from "vs/base/common/lifecycle"; export class ExtensionHostProcessWorker { - private extensionHostProcess: ChildProcess; + private _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); + public readonly onCrashed: Event<[number, string]> = this._onCrashed.event; - private lastExtensionHostError: string; - private terminating: boolean; + private readonly _toDispose: IDisposable[]; - private isExtensionDevelopmentHost: boolean; - private isExtensionDevelopmentTestFromCli: boolean; - private isExtensionDevelopmentDebug: boolean; - private isExtensionDevelopmentDebugBrk: boolean; + private readonly _isExtensionDevHost: boolean; + private readonly _isExtensionDevDebug: boolean; + private readonly _isExtensionDevDebugBrk: boolean; + private readonly _isExtensionDevTestFromCli: boolean; - readonly messagingProtocol = new LazyMessagePassingProtol(); + // State + private _lastExtensionHostError: string; + private _terminating: boolean; - private extensionService: ExtensionService; + // Resources, in order they get acquired/created when .start() is called: + private _namedPipeServer: Server; + private _extensionHostProcess: ChildProcess; + private _extensionHostConnection: Socket; + private _messageProtocol: TPromise; constructor( - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IMessageService private messageService: IMessageService, - @IWindowsService private windowsService: IWindowsService, - @IWindowService private windowService: IWindowService, - @IBroadcastService private broadcastService: IBroadcastService, - @ILifecycleService lifecycleService: ILifecycleService, - @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, - @ITelemetryService private telemetryService: ITelemetryService, - @ICrashReporterService private crashReporterService: ICrashReporterService - + /* intentionally not injected */private readonly _extensionService: IExtensionService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IMessageService private readonly _messageService: IMessageService, + @IWindowsService private readonly _windowsService: IWindowsService, + @IWindowService private readonly _windowService: IWindowService, + @IBroadcastService private readonly _broadcastService: IBroadcastService, + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICrashReporterService private readonly _crashReporterService: ICrashReporterService ) { // handle extension host lifecycle a bit special when we know we are developing an extension that runs inside - this.isExtensionDevelopmentHost = environmentService.isExtensionDevelopment; - this.isExtensionDevelopmentDebug = (typeof environmentService.debugExtensionHost.port === 'number'); - this.isExtensionDevelopmentDebugBrk = !!environmentService.debugExtensionHost.break; - this.isExtensionDevelopmentTestFromCli = this.isExtensionDevelopmentHost && !!environmentService.extensionTestsPath && !environmentService.debugExtensionHost.break; + this._isExtensionDevHost = this._environmentService.isExtensionDevelopment; + this._isExtensionDevDebug = (typeof this._environmentService.debugExtensionHost.port === 'number'); + this._isExtensionDevDebugBrk = !!this._environmentService.debugExtensionHost.break; + this._isExtensionDevTestFromCli = this._isExtensionDevHost && !!this._environmentService.extensionTestsPath && !this._environmentService.debugExtensionHost.break; - lifecycleService.onWillShutdown(this._onWillShutdown, this); - lifecycleService.onShutdown(reason => this.terminate()); + this._lastExtensionHostError = null; + this._terminating = false; - broadcastService.onBroadcast(b => this.onBroadcast(b)); + this._namedPipeServer = null; + this._extensionHostProcess = null; + this._extensionHostConnection = null; + this._messageProtocol = null; + + this._toDispose = []; + this._toDispose.push(this._onCrashed); + this._toDispose.push(this._lifecycleService.onWillShutdown((e) => this._onWillShutdown(e))); + this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate())); + this._toDispose.push(this._broadcastService.onBroadcast(b => this._onBroadcast(b))); + + const globalExitListener = () => this.terminate(); + process.once('exit', globalExitListener); + this._toDispose.push({ + dispose: () => { + process.removeListener('exit', globalExitListener); + } + }); } - private onBroadcast(broadcast: IBroadcast): void { + public dispose(): void { + this.terminate(); + } + + private _onBroadcast(broadcast: IBroadcast): void { // Close Ext Host Window Request - if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this.isExtensionDevelopmentHost) { + if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) { const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this.environmentService.extensionDevelopmentPath, path, !isLinux))) { - this.windowService.closeWindow(); + if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + this._windowService.closeWindow(); } } - if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this.isExtensionDevelopmentHost) { + if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this._isExtensionDevHost) { const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this.environmentService.extensionDevelopmentPath, path, !isLinux))) { - this.windowService.reloadWindow(); + if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + this._windowService.reloadWindow(); } } } - public start(extensionService: ExtensionService): TPromise { - this.extensionService = extensionService; + public start(): TPromise { + if (this._terminating) { + // .terminate() was called + return null; + } - return TPromise.join([this.tryListenOnPipe(), this.tryFindDebugPort()]).then(data => { - const [server, hook] = <[Server, string]>data[0]; - const port = data[1]; + if (!this._messageProtocol) { + this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [string, number]) => { + const pipeName = data[0]; + // The port will be 0 if there's no need to debug or if a free port was not found + const port = data[1]; - const opts = { - env: objects.mixin(objects.clone(process.env), { - AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', - PIPE_LOGGING: 'true', - VERBOSE_LOGGING: true, - VSCODE_WINDOW_ID: String(this.windowService.getCurrentWindowId()), - VSCODE_IPC_HOOK_EXTHOST: hook, - ELECTRON_NO_ASAR: '1' - }), - // We only detach the extension host on windows. Linux and Mac orphan by default - // and detach under Linux and Mac create another process group. - // We detach because we have noticed that when the renderer exits, its child processes - // (i.e. extension host) is taken down in a brutal fashion by the OS - detached: !!isWindows, - execArgv: port - ? ['--nolazy', (this.isExtensionDevelopmentDebugBrk ? '--inspect-brk=' : '--inspect=') + port] - : undefined, - silent: true - }; + const opts = { + env: objects.mixin(objects.clone(process.env), { + AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: true, + VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), + VSCODE_IPC_HOOK_EXTHOST: pipeName, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, + ELECTRON_NO_ASAR: '1' + }), + // We only detach the extension host on windows. Linux and Mac orphan by default + // and detach under Linux and Mac create another process group. + // We detach because we have noticed that when the renderer exits, its child processes + // (i.e. extension host) are taken down in a brutal fashion by the OS + detached: !!isWindows, + execArgv: port + ? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port] + : undefined, + silent: true + }; - const crashReporterOptions = this.crashReporterService.getChildProcessStartOptions('extensionHost'); - if (crashReporterOptions) { - opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); - } - - // Run Extension Host as fork of current process - this.extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); - - // Catch all output coming from the extension host process - type Output = { data: string, format: string[] }; - this.extensionHostProcess.stdout.setEncoding('utf8'); - this.extensionHostProcess.stderr.setEncoding('utf8'); - const onStdout = fromEventEmitter(this.extensionHostProcess.stdout, 'data'); - const onStderr = fromEventEmitter(this.extensionHostProcess.stderr, 'data'); - const onOutput = any( - mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), - mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) - ); - - // Debounce all output, so we can render it in the Chrome console as a group - const onDebouncedOutput = debounceEvent(onOutput, (r, o) => { - return r - ? { data: r.data + o.data, format: [...r.format, ...o.format] } - : { data: o.data, format: o.format }; - }, 100); - - // Print out extension host output - onDebouncedOutput(data => { - console.group('Extension Host'); - console.log(data.data, ...data.format); - console.groupEnd(); - }); - - // Support logging from extension host - this.extensionHostProcess.on('message', msg => { - if (msg && (msg).type === '__$console') { - this.logExtensionHostMessage(msg); + const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost'); + if (crashReporterOptions) { + opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); } - }); - // Lifecycle - let onExit = () => this.terminate(); - process.once('exit', onExit); - this.extensionHostProcess.on('error', (err) => this.onError(err)); - this.extensionHostProcess.on('exit', (code: any, signal: any) => this.onExit(code, signal, onExit)); + // Run Extension Host as fork of current process + this._extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); - // Notify debugger that we are ready to attach to the process if we run a development extension - if (this.isExtensionDevelopmentHost && port) { - this.broadcastService.broadcast({ - channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, - payload: { - debugId: this.environmentService.debugExtensionHost.debugId, - port + // Catch all output coming from the extension host process + type Output = { data: string, format: string[] }; + this._extensionHostProcess.stdout.setEncoding('utf8'); + this._extensionHostProcess.stderr.setEncoding('utf8'); + const onStdout = fromEventEmitter(this._extensionHostProcess.stdout, 'data'); + const onStderr = fromEventEmitter(this._extensionHostProcess.stderr, 'data'); + const onOutput = any( + mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), + mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) + ); + + // Debounce all output, so we can render it in the Chrome console as a group + const onDebouncedOutput = debounceEvent(onOutput, (r, o) => { + return r + ? { data: r.data + o.data, format: [...r.format, ...o.format] } + : { data: o.data, format: o.format }; + }, 100); + + // Print out extension host output + onDebouncedOutput(data => { + console.group('Extension Host'); + console.log(data.data, ...data.format); + console.groupEnd(); + }); + + // Support logging from extension host + this._extensionHostProcess.on('message', msg => { + if (msg && (msg).type === '__$console') { + this._logExtensionHostMessage(msg); } }); - } - // Help in case we fail to start it - let startupTimeoutHandle: number; - if (!this.environmentService.isBuilt || this.isExtensionDevelopmentHost) { - startupTimeoutHandle = setTimeout(() => { - const msg = this.isExtensionDevelopmentDebugBrk - ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") - : nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); + // Lifecycle + this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err)); + this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); - this.messageService.show(Severity.Warning, msg); - }, 10000); - } + // Notify debugger that we are ready to attach to the process if we run a development extension + if (this._isExtensionDevHost && port) { + this._broadcastService.broadcast({ + channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, + payload: { + debugId: this._environmentService.debugExtensionHost.debugId, + port + } + }); + } - // Initialize extension host process with hand shakes - return this.tryExtHostHandshake(server).then((protocol) => { - clearTimeout(startupTimeoutHandle); - return protocol; + // Help in case we fail to start it + let startupTimeoutHandle: number; + if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + startupTimeoutHandle = setTimeout(() => { + const msg = this._isExtensionDevDebugBrk + ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") + : nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); + + this._messageService.show(Severity.Warning, msg); + }, 10000); + } + + // Initialize extension host process with hand shakes + return this._tryExtHostHandshake().then((protocol) => { + clearTimeout(startupTimeoutHandle); + return protocol; + }); + }); + } + + return this._messageProtocol; + } + + /** + * Start a server (`this._namedPipeServer`) that listens on a named pipe and return the named pipe name. + */ + private _tryListenOnPipe(): TPromise { + return new TPromise((resolve, reject) => { + const pipeName = generateRandomPipeName(); + + this._namedPipeServer = createServer(); + this._namedPipeServer.on('error', reject); + this._namedPipeServer.listen(pipeName, () => { + this._namedPipeServer.removeListener('error', reject); + resolve(pipeName); }); }); } - private tryListenOnPipe(): TPromise<[Server, string]> { - return new TPromise<[Server, string]>((resolve, reject) => { - const server = createServer(); - server.on('error', reject); - const hook = generateRandomPipeName(); - server.listen(hook, () => { - server.removeListener('error', reject); - resolve([server, hook]); - }); - }); - } - - private tryFindDebugPort(): TPromise { - const extensionHostPort = this.environmentService.debugExtensionHost.port; + /** + * Find a free port if extension host debugging is enabled. + */ + private _tryFindDebugPort(): TPromise { + const extensionHostPort = this._environmentService.debugExtensionHost.port; if (typeof extensionHostPort !== 'number') { - return TPromise.wrap(void 0); + return TPromise.wrap(0); } return new TPromise((c, e) => { findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => { @@ -252,7 +266,7 @@ export class ExtensionHostProcessWorker { if (port !== extensionHostPort) { console.warn(`%c[Extension Host] %cProvided debugging port ${extensionHostPort} is not free, using ${port} instead.`, 'color: blue', 'color: black'); } - if (this.isExtensionDevelopmentDebugBrk) { + if (this._isExtensionDevDebugBrk) { console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black'); } else { console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black'); @@ -262,61 +276,90 @@ export class ExtensionHostProcessWorker { }); } - private tryExtHostHandshake(server: Server): TPromise { + private _tryExtHostHandshake(): TPromise { return new TPromise((resolve, reject) => { - let handle = setTimeout(() => reject('timeout'), 60 * 1000); - server.on('connection', socket => { + + // Wait for the extension host to connect to our named pipe + // and wrap the socket in the message passing protocol + let handle = setTimeout(() => { + this._namedPipeServer.close(); + this._namedPipeServer = null; + reject('timeout'); + }, 60 * 1000); + + this._namedPipeServer.on('connection', socket => { clearTimeout(handle); - const protocol = new Protocol(socket); - resolve(protocol); + this._namedPipeServer.close(); + this._namedPipeServer = null; + this._extensionHostConnection = socket; + resolve(new Protocol(this._extensionHostConnection)); }); - // }).then(protocol => { - // return protocol; - }).then(protocol => { + }).then((protocol) => { + // 1) wait for the incoming `ready` event and send the initialization data. + // 2) wait for the incoming `initialized` event. return new TPromise((resolve, reject) => { - protocol.onMessage(msg => { + + let handle = setTimeout(() => { + reject('timeout'); + }, 60 * 1000); + + const disposable = protocol.onMessage(msg => { + if (msg === 'ready') { - // 1) Host is ready to receive messages, initialize it - return this.createExtHostInitData().then(data => protocol.send(stringify(data))); - } else if (msg === 'initialized') { - // 2) Host is initialized - this.messagingProtocol.resolve(protocol); - resolve(protocol); + // 1) Extension Host is ready to receive messages, initialize it + this._createExtHostInitData().then(data => protocol.send(stringify(data))); + return; } - return undefined; + + if (msg === 'initialized') { + // 2) Extension Host is initialized + + clearTimeout(handle); + + // stop listening for messages here + disposable.dispose(); + + // release this promise + resolve(protocol); + return; + } + + console.error(`received unexpected message during handshake phase from the extension host: `, msg); }); + }); + }); } - private createExtHostInitData(): TPromise { - return TPromise.join([this.telemetryService.getTelemetryInfo(), this.extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { - let r: IInitData = { + private _createExtHostInitData(): TPromise { + return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { + const r: IInitData = { parentPid: process.pid, environment: { - isExtensionDevelopmentDebug: this.isExtensionDevelopmentDebug, - appSettingsHome: this.environmentService.appSettingsHome, - disableExtensions: this.environmentService.disableExtensions, - userExtensionsHome: this.environmentService.extensionsPath, - extensionDevelopmentPath: this.environmentService.extensionDevelopmentPath, - extensionTestsPath: this.environmentService.extensionTestsPath, + isExtensionDevelopmentDebug: this._isExtensionDevDebug, + appSettingsHome: this._environmentService.appSettingsHome, + disableExtensions: this._environmentService.disableExtensions, + userExtensionsHome: this._environmentService.extensionsPath, + extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, + extensionTestsPath: this._environmentService.extensionTestsPath, // globally disable proposed api when built and not insiders developing extensions - enableProposedApiForAll: !this.environmentService.isBuilt || (!!this.environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0), - enableProposedApiFor: this.environmentService.args['enable-proposed-api'] || [] + enableProposedApiForAll: !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0), + enableProposedApiFor: this._environmentService.args['enable-proposed-api'] || [] }, - workspace: this.contextService.getWorkspace(), + workspace: this._contextService.getWorkspace(), extensions: extensionDescriptions, - configuration: this.configurationService.getConfigurationData(), + configuration: this._configurationService.getConfigurationData(), telemetryInfo }; return r; }); } - private logExtensionHostMessage(logEntry: ILogEntry) { + private _logExtensionHostMessage(logEntry: ILogEntry) { let args = []; try { let parsed = JSON.parse(logEntry.arguments); @@ -336,83 +379,105 @@ export class ExtensionHostProcessWorker { } // Send to local console unless we run tests from cli - if (!this.isExtensionDevelopmentTestFromCli) { + if (!this._isExtensionDevTestFromCli) { console[logEntry.severity].apply(console, consoleArgs); } // Log on main side if running tests from cli - if (this.isExtensionDevelopmentTestFromCli) { - this.windowsService.log(logEntry.severity, ...args); + if (this._isExtensionDevTestFromCli) { + this._windowsService.log(logEntry.severity, ...args); } // Broadcast to other windows if we are in development mode - else if (!this.environmentService.isBuilt || this.isExtensionDevelopmentHost) { - this.broadcastService.broadcast({ + else if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + this._broadcastService.broadcast({ channel: EXTENSION_LOG_BROADCAST_CHANNEL, payload: { logEntry, - debugId: this.environmentService.debugExtensionHost.debugId + debugId: this._environmentService.debugExtensionHost.debugId } }); } } - private onError(err: any): void { + private _onExtHostProcessError(err: any): void { let errorMessage = toErrorMessage(err); - if (errorMessage === this.lastExtensionHostError) { + if (errorMessage === this._lastExtensionHostError) { return; // prevent error spam } - this.lastExtensionHostError = errorMessage; + this._lastExtensionHostError = errorMessage; - this.messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage)); + this._messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage)); } - private onExit(code: any, signal: any, onProcessExit: any): void { - process.removeListener('exit', onProcessExit); + private _onExtHostProcessExit(code: number, signal: string): void { + if (this._terminating) { + // Expected termination path (we asked the process to terminate) + return; + } - if (!this.terminating) { + // Unexpected termination + if (!this._isExtensionDevHost) { + this._onCrashed.fire([code, signal]); + } - // Unexpected termination - if (!this.isExtensionDevelopmentHost) { - const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, async (): TPromise => { - await this.windowService.openDevTools(); - return false; - }); + // Expected development extension termination: When the extension host goes down we also shutdown the window + else if (!this._isExtensionDevTestFromCli) { + this._windowService.closeWindow(); + } - let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."); - if (code === 87) { - message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive. Please reload the window to recover."); - } - this.messageService.show(Severity.Error, { - message: message, - actions: [ - openDevTools, - this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL) - ] - }); - - console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); - } - - // Expected development extension termination: When the extension host goes down we also shutdown the window - else if (!this.isExtensionDevelopmentTestFromCli) { - this.windowService.closeWindow(); - } - - // When CLI testing make sure to exit with proper exit code - else { - ipc.send('vscode:exit', code); - } + // When CLI testing make sure to exit with proper exit code + else { + ipc.send('vscode:exit', code); } } public terminate(): void { - this.terminating = true; - if (this.extensionHostProcess) { - this.messagingProtocol.send({ + if (this._terminating) { + return; + } + this._terminating = true; + + dispose(this._toDispose); + + if (!this._messageProtocol) { + // .start() was not called + return; + } + + this._messageProtocol.then((protocol) => { + + // Send the extension host a request to terminate itself + // (graceful termination) + protocol.send({ type: '__$terminate' }); + + // Give the extension host 60s, after which we will + // try to kill the process and release any resources + setTimeout(() => this._cleanResources(), 60 * 1000); + + }, (err) => { + + // Establishing a protocol with the extension host failed, so + // try to kill the process and release any resources. + this._cleanResources(); + }); + } + + private _cleanResources(): void { + if (this._namedPipeServer) { + this._namedPipeServer.close(); + this._namedPipeServer = null; + } + if (this._extensionHostConnection) { + this._extensionHostConnection.end(); + this._extensionHostConnection = null; + } + if (this._extensionHostProcess) { + this._extensionHostProcess.kill(); + this._extensionHostProcess = null; } } @@ -420,11 +485,11 @@ export class ExtensionHostProcessWorker { // If the extension development host was started without debugger attached we need // to communicate this back to the main side to terminate the debug session - if (this.isExtensionDevelopmentHost && !this.isExtensionDevelopmentTestFromCli && !this.isExtensionDevelopmentDebug) { - this.broadcastService.broadcast({ + if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug) { + this._broadcastService.broadcast({ channel: EXTENSION_TERMINATE_BROADCAST_CHANNEL, payload: { - debugId: this.environmentService.debugExtensionHost.debugId + debugId: this._environmentService.debugExtensionHost.debugId } }); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index da1a86518b9..735c3b15c0f 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import pkg from 'vs/platform/node/package'; -import { localize } from 'vs/nls'; import * as path from 'path'; import URI from 'vs/base/common/uri'; import { ExtensionDescriptionRegistry } from "vs/workbench/services/extensions/node/extensionDescriptionRegistry"; @@ -28,6 +29,9 @@ import { MainThreadService } from "vs/workbench/services/thread/electron-browser import { Barrier } from "vs/workbench/services/extensions/node/barrier"; import { IMessagePassingProtocol } from "vs/base/parts/ipc/common/ipc"; import { ExtHostCustomersRegistry } from "vs/workbench/api/electron-browser/extHostCustomers"; +import { IWindowService } from "vs/platform/windows/common/windows"; +import { Action } from "vs/base/common/actions"; +import { IDisposable } from "vs/base/common/lifecycle"; const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); @@ -52,14 +56,22 @@ export class ExtensionService implements IExtensionService { private readonly _barrier: Barrier; private readonly _isDev: boolean; private readonly _extensionsStatus: { [id: string]: IExtensionsStatus }; + private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; + + + // --- Members used per extension host process + /** * A map of already activated events to speed things up if the same activation event is triggered multiple times. */ - private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; }; - - // winjs believes a proxy is a promise because it has a `then` method, - // so wrap the result in an object. - private _proxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; + private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; }; + private _extensionHostProcessWorker: ExtensionHostProcessWorker; + private _extensionHostProcessThreadService: MainThreadService; + private _extensionHostProcessCustomers: IDisposable[]; + /** + * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. + */ + private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -68,65 +80,154 @@ export class ExtensionService implements IExtensionService { @ITelemetryService private readonly _telemetryService: ITelemetryService, @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, @IStorageService private readonly _storageService: IStorageService, + @IWindowService private readonly _windowService: IWindowService ) { this._registry = null; this._barrier = new Barrier(); this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; this._extensionsStatus = {}; - this._alreadyActivatedEvents = Object.create(null); + this._allRequestedActivateEvents = Object.create(null); - const extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker); - this._proxy = extensionHostProcessWorker.start(this).then((protocol) => { - return { value: this._begin(protocol) }; - }); + this._extensionHostProcessFinishedActivateEvents = Object.create(null); + this._extensionHostProcessWorker = null; + this._extensionHostProcessThreadService = null; + this._extensionHostProcessCustomers = []; + this._extensionHostProcessProxy = null; + this._startExtensionHostProcess([]); this._scanAndHandleExtensions(); } - private _begin(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + private _stopExtensionHostProcess(): void { + this._extensionHostProcessFinishedActivateEvents = Object.create(null); + if (this._extensionHostProcessWorker) { + this._extensionHostProcessWorker.dispose(); + this._extensionHostProcessWorker = null; + } + if (this._extensionHostProcessThreadService) { + this._extensionHostProcessThreadService.dispose(); + this._extensionHostProcessThreadService = null; + } + for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) { + const customer = this._extensionHostProcessCustomers[i]; + try { + customer.dispose(); + } catch (err) { + errors.onUnexpectedError(err); + } + } + this._extensionHostProcessCustomers = []; + this._extensionHostProcessProxy = null; + } - const threadService = this._instantiationService.createInstance(MainThreadService, protocol); - const extHostContext: IExtHostContext = threadService; + private _startExtensionHostProcess(initialActivationEvents: string[]): void { + this._stopExtensionHostProcess(); + + this._extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this); + this._extensionHostProcessWorker.onCrashed(([code, signal]) => this._onExtensionHostCrashed(code, signal)); + this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then( + (protocol) => { + return { value: this._createExtensionHostCustomers(protocol) }; + }, + (err) => { + console.error('Error received from starting extension host'); + console.error(err); + return null; + } + ); + this._extensionHostProcessProxy.then(() => { + initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent)); + }); + } + + private _onExtensionHostCrashed(code: number, signal: string): void { + const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, (): TPromise => { + return this._windowService.openDevTools().then(() => false); + }); + + const restart = new Action('restart', nls.localize('restart', "Restart Extension Host"), '', true, (): TPromise => { + this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents)); + return TPromise.as(true); + }); + + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + this._stopExtensionHostProcess(); + + let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly."); + if (code === 87) { + message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive."); + } + this._messageService.show(Severity.Error, { + message: message, + actions: [ + openDevTools, + restart + ] + }); + } + + private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + + this._extensionHostProcessThreadService = this._instantiationService.createInstance(MainThreadService, protocol); + const extHostContext: IExtHostContext = this._extensionHostProcessThreadService; // Named customers const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers(); for (let i = 0, len = namedCustomers.length; i < len; i++) { const [id, ctor] = namedCustomers[i]; - threadService.set(id, this._instantiationService.createInstance(ctor, extHostContext)); + const instance = this._instantiationService.createInstance(ctor, extHostContext); + this._extensionHostProcessCustomers.push(instance); + this._extensionHostProcessThreadService.set(id, instance); } // Customers const customers = ExtHostCustomersRegistry.getCustomers(); for (let i = 0, len = customers.length; i < len; i++) { const ctor = customers[i]; - this._instantiationService.createInstance(ctor, extHostContext); + const instance = this._instantiationService.createInstance(ctor, extHostContext); + this._extensionHostProcessCustomers.push(instance); } // Check that no named customers are missing const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => MainContext[key]); - threadService.assertRegistered(expected); + this._extensionHostProcessThreadService.assertRegistered(expected); - return threadService.get(ExtHostContext.ExtHostExtensionService); + return this._extensionHostProcessThreadService.get(ExtHostContext.ExtHostExtensionService); } // ---- begin IExtensionService public activateByEvent(activationEvent: string): TPromise { if (this._barrier.isOpen) { + // Extensions have been scanned and interpreted + + if (!this._registry.containsActivationEvent(activationEvent)) { + // There is no extension that is interested in this activation event + return NO_OP_VOID_PROMISE; + } + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + return this._activateByEvent(activationEvent); } else { + // Extensions have not been scanned yet. + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + return this._barrier.wait().then(() => this._activateByEvent(activationEvent)); } } protected _activateByEvent(activationEvent: string): TPromise { - if (this._alreadyActivatedEvents[activationEvent]) { + if (this._extensionHostProcessFinishedActivateEvents[activationEvent]) { return NO_OP_VOID_PROMISE; } - return this._proxy.then((proxy) => { + return this._extensionHostProcessProxy.then((proxy) => { return proxy.value.$activateByEvent(activationEvent); }).then(() => { - this._alreadyActivatedEvents[activationEvent] = true; + this._extensionHostProcessFinishedActivateEvents[activationEvent] = true; }); } @@ -242,20 +343,20 @@ export class ExtensionService implements IExtensionService { }); userExtensions.forEach((userExtension) => { if (result.hasOwnProperty(userExtension.id)) { - log.warn(userExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); + log.warn(userExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); } result[userExtension.id] = userExtension; }); developedExtensions.forEach(developedExtension => { - log.info('', localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); + log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); if (result.hasOwnProperty(developedExtension.id)) { - log.warn(developedExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); + log.warn(developedExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); } result[developedExtension.id] = developedExtension; }); return Object.keys(result).map(name => result[name]); - }).then(null, err => { + }).then(null, err => { log.error('', err); return []; }); diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index 9f1e8df89b4..0a0042db484 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -40,6 +40,10 @@ export class ExtensionDescriptionRegistry { } } + public containsActivationEvent(activationEvent: string): boolean { + return hasOwnProperty.call(this._activationMap, activationEvent); + } + public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] { if (!hasOwnProperty.call(this._activationMap, activationEvent)) { return []; diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index 61d4259a78a..7f23b136747 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -16,6 +16,7 @@ export interface IDispatcher { export class RPCProtocol { + private _isDisposed: boolean; private _bigHandler: IDispatcher; private _lastMessageId: number; private readonly _invokedHandlers: { [req: string]: TPromise; }; @@ -23,6 +24,7 @@ export class RPCProtocol { private readonly _multiplexor: RPCMultiplexer; constructor(protocol: IMessagePassingProtocol) { + this._isDisposed = false; this._bigHandler = null; this._lastMessageId = 0; this._invokedHandlers = Object.create(null); @@ -30,7 +32,20 @@ export class RPCProtocol { this._multiplexor = new RPCMultiplexer(protocol, (msg) => this._receiveOneMessage(msg)); } + public dispose(): void { + this._isDisposed = true; + + // Release all outstanding promises with a canceled error + Object.keys(this._pendingRPCReplies).forEach((msgId) => { + const pending = this._pendingRPCReplies[msgId]; + pending.resolveErr(errors.canceled()); + }); + } + private _receiveOneMessage(rawmsg: string): void { + if (this._isDisposed) { + throw new Error(`disposed`); + } let msg = marshalling.parse(rawmsg); if (msg.seq) { @@ -97,6 +112,10 @@ export class RPCProtocol { } public callOnRemote(proxyId: string, methodName: string, args: any[]): TPromise { + if (this._isDisposed) { + return TPromise.wrapError(errors.canceled()); + } + let req = String(++this._lastMessageId); let result = new LazyPromise(() => { this._multiplexor.send(MessageFactory.cancel(req)); diff --git a/src/vs/workbench/services/message/browser/messageList.ts b/src/vs/workbench/services/message/browser/messageList.ts index c9e44de2a82..fd4190c867d 100644 --- a/src/vs/workbench/services/message/browser/messageList.ts +++ b/src/vs/workbench/services/message/browser/messageList.ts @@ -35,11 +35,13 @@ export enum Severity { export interface IMessageWithAction { message: string; actions: Action[]; + source: string; } interface IMessageEntry { id: any; text: string; + source: string; severity: Severity; time: number; count?: number; @@ -199,6 +201,7 @@ export class MessageList { severity: severity, time: Date.now(), actions: (id).actions, + source: (id).source, onHide }); @@ -339,7 +342,10 @@ export class MessageList { className: 'message-left-side', }); - $(messageContentElement as HTMLElement).title(messageContentElement.textContent).appendTo(div); + // Hover title + const title = message.source ? `[${message.source}] ${messageContentElement.textContent}` : messageContentElement.textContent; + + $(messageContentElement as HTMLElement).title(title).appendTo(div); }); }); } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index d4bfaa7bf82..413d030c6ca 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -561,7 +561,7 @@ export abstract class TextFileService implements ITextFileService { modelPromise = this.untitledEditorService.loadOrCreate({ resource }); } - return modelPromise.then(model => { + return modelPromise.then(model => { // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before) if (model) { diff --git a/src/vs/workbench/services/themes/common/colorThemeSchema.ts b/src/vs/workbench/services/themes/common/colorThemeSchema.ts index e6a4a910d32..5c6b7609512 100644 --- a/src/vs/workbench/services/themes/common/colorThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/colorThemeSchema.ts @@ -127,7 +127,7 @@ export const tokenColorizationSettingSchema: IJSONSchema = { fontStyle: { type: 'string', description: nls.localize('schema.token.fontStyle', 'Font style of the rule: One or a combination of \'italic\', \'bold\' and \'underline\''), - pattern: '^(\\s*\\b(italics|bold|underline))*\\s*$', + pattern: '^(\\s*\\b(italic|bold|underline))*\\s*$', patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be a combination of \'italic\', \'bold\' and \'underline\''), defaultSnippets: [{ body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 8f0acdd5f7c..10b047cc5e0 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -340,7 +340,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } - private initialize(): TPromise { + private initialize(): TPromise<[IColorTheme, IFileIconTheme]> { this.updateColorCustomizations(false); diff --git a/src/vs/workbench/services/thread/node/abstractThreadService.ts b/src/vs/workbench/services/thread/node/abstractThreadService.ts index e8a75d60cde..e6f11ed4c78 100644 --- a/src/vs/workbench/services/thread/node/abstractThreadService.ts +++ b/src/vs/workbench/services/thread/node/abstractThreadService.ts @@ -25,6 +25,10 @@ export abstract class AbstractThreadService implements IDispatcher { this._rpcProtocol.setDispatcher(this); } + public dispose(): void { + this._rpcProtocol.dispose(); + } + public invoke(proxyId: string, methodName: string, args: any[]): any { if (!this._locals[proxyId]) { throw new Error('Unknown actor ' + proxyId); diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index 0876cbf5b90..37c944c5ce9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -14,7 +14,7 @@ suite('ExtHostMessageService', function () { test('propagte handle on select', function () { - let service = new MainThreadMessageService(null, { + let service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { assert.equal(m.actions.length, 1); setImmediate(() => m.actions[0].run()); @@ -34,7 +34,7 @@ suite('ExtHostMessageService', function () { test('isCloseAffordance', function () { let actions: Action[]; - let service = new MainThreadMessageService(null, { + let service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { actions = m.actions; } @@ -62,7 +62,7 @@ suite('ExtHostMessageService', function () { let actions: Action[]; let c: number; - let service = new MainThreadMessageService(null, { + let service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { c = 0; actions = m.actions; @@ -85,7 +85,7 @@ suite('ExtHostMessageService', function () { suite('modal', () => { test('calls choice service', () => { - const service = new MainThreadMessageService(null, { + const service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { throw new Error('not implemented'); } @@ -105,7 +105,7 @@ suite('ExtHostMessageService', function () { }); test('returns undefined when cancelled', () => { - const service = new MainThreadMessageService(null, { + const service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { throw new Error('not implemented'); } @@ -121,7 +121,7 @@ suite('ExtHostMessageService', function () { }); test('hides Cancel button when not needed', () => { - const service = new MainThreadMessageService(null, { + const service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { throw new Error('not implemented'); } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index a9ffb50bf78..5555e8bb960 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -882,7 +882,7 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } - newWorkspace(): TPromise { + createAndOpenWorkspace(folders?: string[], path?: string): TPromise { return TPromise.as(void 0); } @@ -1018,7 +1018,7 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - newWorkspace(windowId: number): TPromise { + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { return TPromise.as(void 0); } diff --git a/test/smoke/src/test.ts b/test/smoke/src/test.ts index 3eb91634c19..0f1725dfa90 100644 --- a/test/smoke/src/test.ts +++ b/test/smoke/src/test.ts @@ -19,7 +19,7 @@ import { testExtensions } from "./tests/extensions"; import { testLocalization } from "./tests/localization"; import { testMultiRoot } from "./tests/multiroot"; -describe('Smoke Test Suite', () => { +describe('Smoke:', () => { testDataMigration(); testDataLoss(); testExplorer(); diff --git a/test/smoke/src/tests/configuration-views.ts b/test/smoke/src/tests/configuration-views.ts index ff810dd37d4..c5673a73416 100644 --- a/test/smoke/src/tests/configuration-views.ts +++ b/test/smoke/src/tests/configuration-views.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testConfigViews() { - context('Configuration and views', () => { + describe('Configuration and views', () => { let configView: ConfigurationView; beforeEach(async function () { diff --git a/test/smoke/src/tests/css.ts b/test/smoke/src/tests/css.ts index 52fcf60335f..c591c0fc6f2 100644 --- a/test/smoke/src/tests/css.ts +++ b/test/smoke/src/tests/css.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testCSS() { - context('CSS', () => { + describe('CSS', () => { let css: CSS; beforeEach(async function () { diff --git a/test/smoke/src/tests/data-loss.ts b/test/smoke/src/tests/data-loss.ts index c090b6f140c..cccefb9aa42 100644 --- a/test/smoke/src/tests/data-loss.ts +++ b/test/smoke/src/tests/data-loss.ts @@ -14,7 +14,7 @@ let common: CommonActions; let dl: DataLoss; export function testDataLoss() { - context('Data Loss', () => { + describe('Data Loss', () => { beforeEach(async function () { app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH], [`--user-data-dir=${USER_DIR}`]); diff --git a/test/smoke/src/tests/data-migration.ts b/test/smoke/src/tests/data-migration.ts index 4c9ac38334d..86e781bf0ec 100644 --- a/test/smoke/src/tests/data-migration.ts +++ b/test/smoke/src/tests/data-migration.ts @@ -16,7 +16,7 @@ export function testDataMigration() { return; } - context('Data Migration', () => { + describe('Data Migration', () => { afterEach(async function () { await app.stop(); diff --git a/test/smoke/src/tests/explorer.ts b/test/smoke/src/tests/explorer.ts index d1a4570f003..9edf29bb7d6 100644 --- a/test/smoke/src/tests/explorer.ts +++ b/test/smoke/src/tests/explorer.ts @@ -12,7 +12,7 @@ let app: SpectronApplication; let common: CommonActions; export function testExplorer() { - context('Explorer', () => { + describe('Explorer', () => { beforeEach(async function () { app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); diff --git a/test/smoke/src/tests/extensions.ts b/test/smoke/src/tests/extensions.ts index 39894de6fea..d9527e1432e 100644 --- a/test/smoke/src/tests/extensions.ts +++ b/test/smoke/src/tests/extensions.ts @@ -16,7 +16,7 @@ let common: CommonActions; export function testExtensions() { - context('Extensions', () => { + describe('Extensions', () => { let extensions: Extensions; const extensionName = 'vscode-smoketest-check'; diff --git a/test/smoke/src/tests/git.ts b/test/smoke/src/tests/git.ts index 98f2c761aa5..35d61752dae 100644 --- a/test/smoke/src/tests/git.ts +++ b/test/smoke/src/tests/git.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testGit() { - context('Git', () => { + describe('Git', () => { let git: Git; beforeEach(async function () { diff --git a/test/smoke/src/tests/integrated-terminal.ts b/test/smoke/src/tests/integrated-terminal.ts index 57ec1d42d09..b9dbe88bcb6 100644 --- a/test/smoke/src/tests/integrated-terminal.ts +++ b/test/smoke/src/tests/integrated-terminal.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testIntegratedTerminal() { - context('Integrated Terminal', () => { + describe('Integrated Terminal', () => { let terminal: IntegratedTerminal; beforeEach(async function () { diff --git a/test/smoke/src/tests/javascript-debug.ts b/test/smoke/src/tests/javascript-debug.ts index 970fa6edabf..0018ae2f077 100644 --- a/test/smoke/src/tests/javascript-debug.ts +++ b/test/smoke/src/tests/javascript-debug.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testJavaScriptDebug() { - context('Debugging JavaScript', () => { + describe('Debugging JavaScript', () => { let jsDebug: JavaScriptDebug; beforeEach(async function () { diff --git a/test/smoke/src/tests/javascript.ts b/test/smoke/src/tests/javascript.ts index b522ce5c66f..60466ec5deb 100644 --- a/test/smoke/src/tests/javascript.ts +++ b/test/smoke/src/tests/javascript.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testJavaScript() { - context('JavaScript', () => { + describe('JavaScript', () => { let js: JavaScript; beforeEach(async function () { diff --git a/test/smoke/src/tests/localization.ts b/test/smoke/src/tests/localization.ts index a40f7cfc9ed..3e45fbc1e48 100644 --- a/test/smoke/src/tests/localization.ts +++ b/test/smoke/src/tests/localization.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testLocalization() { - context('Localization', () => { + describe('Localization', () => { afterEach(async function () { return await app.stop(); }); diff --git a/test/smoke/src/tests/multiroot.ts b/test/smoke/src/tests/multiroot.ts index 22aa8001452..73153097514 100644 --- a/test/smoke/src/tests/multiroot.ts +++ b/test/smoke/src/tests/multiroot.ts @@ -12,7 +12,7 @@ let app: SpectronApplication; let common: CommonActions; export function testMultiRoot() { - context('Multi Root', () => { + describe('Multi Root', () => { beforeEach(async function () { app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [CODE_WORKSPACE_PATH]); diff --git a/test/smoke/src/tests/search.ts b/test/smoke/src/tests/search.ts index 35ea09e7e46..36818f0bcbd 100644 --- a/test/smoke/src/tests/search.ts +++ b/test/smoke/src/tests/search.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testSearch() { - context('Search', () => { + describe('Search', () => { let search: Search; beforeEach(async function () { diff --git a/test/smoke/src/tests/statusbar.ts b/test/smoke/src/tests/statusbar.ts index 9038266b334..d092105bec6 100644 --- a/test/smoke/src/tests/statusbar.ts +++ b/test/smoke/src/tests/statusbar.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testStatusbar() { - context('Status Bar', () => { + describe('Status Bar', () => { let statusBar: StatusBar; beforeEach(async function () { diff --git a/test/smoke/src/tests/tasks.ts b/test/smoke/src/tests/tasks.ts index 45fc5d794bb..1aa10e3eac2 100644 --- a/test/smoke/src/tests/tasks.ts +++ b/test/smoke/src/tests/tasks.ts @@ -11,7 +11,7 @@ import { Tasks } from "../areas/tasks"; let app: SpectronApplication; export function testTasks() { - context('Tasks', () => { + describe('Tasks', () => { let tasks: Tasks; beforeEach(async function () {